@hyperlane-xyz/utils
Version:
General utilities and types for the Hyperlane network
629 lines • 15 kB
JavaScript
import { expect } from 'chai';
import rule from './sort-yaml-arrays.js';
describe('sort-yaml-arrays rule', () => {
let lintResult;
let fixer;
beforeEach(() => {
lintResult = null;
// Only implement replaceText since it's the only method actually used
const dummyFix = () => ({
range: [0, 0],
text: '',
});
fixer = {
replaceText: (_, text) => {
lintResult = { fixed: true, output: text };
return { range: [0, 0], text };
},
insertTextAfter: dummyFix,
insertTextAfterRange: dummyFix,
insertTextBefore: dummyFix,
insertTextBeforeRange: dummyFix,
remove: dummyFix,
removeRange: dummyFix,
replaceTextRange: dummyFix,
};
});
function runLint(yamlText, filename, options = {}) {
const context = {
id: 'sort-yaml-arrays',
options: [options],
filename,
sourceCode: {
getText: () => yamlText,
},
report: ({ fix }) => {
if (fix)
fix(fixer);
},
};
const ruleInstance = rule.create(context);
if (ruleInstance?.Program) {
const lineCount = yamlText.split('\n').length;
ruleInstance.Program({
parent: {},
sourceType: 'module',
type: 'Program',
body: [],
comments: [],
tokens: [],
range: [0, yamlText.length],
loc: {
start: { line: 1, column: 0 },
end: { line: lineCount, column: 0 },
},
});
}
return lintResult;
}
const testCases = [
{
name: 'should sort people by name',
original: `people:
- name: Charlie
age: 30
- name: Alice
age: 25
- name: Bob
age: 35`,
expected: `people:
- name: Alice
age: 25
- name: Bob
age: 35
- name: Charlie
age: 30`,
options: {
arrays: [{ path: 'people', sortKey: 'name' }],
},
},
{
name: 'should sort people by age',
original: `people:
- name: Charlie
age: 30
- name: Alice
age: 25
- name: Bob
age: 35`,
expected: `people:
- name: Alice
age: 25
- name: Charlie
age: 30
- name: Bob
age: 35`,
options: {
arrays: [{ path: 'people', sortKey: 'age' }],
},
},
{
name: 'should sort nested arrays',
original: `departments:
- name: Engineering
employees:
- name: Dave
position: Developer
- name: Alice
position: Manager
- name: Marketing
employees:
- name: Zack
position: Designer
- name: Bob
position: Director`,
expected: `departments:
- name: Engineering
employees:
- name: Alice
position: Manager
- name: Dave
position: Developer
- name: Marketing
employees:
- name: Bob
position: Director
- name: Zack
position: Designer`,
options: {
arrays: [{ path: 'departments[].employees', sortKey: 'name' }],
},
},
{
name: 'should sort multiple arrays with different keys',
original: `company:
departments:
- id: 3
name: Engineering
projects:
- priority: 2
name: Website
- priority: 1
name: API
- id: 1
name: Marketing
projects:
- priority: 3
name: Campaign
- priority: 1
name: Branding`,
expected: `company:
departments:
- id: 1
name: Marketing
projects:
- priority: 1
name: Branding
- priority: 3
name: Campaign
- id: 3
name: Engineering
projects:
- priority: 1
name: API
- priority: 2
name: Website`,
options: {
arrays: [
{ path: 'company.departments', sortKey: 'id' },
{
path: 'company.departments.*.projects',
sortKey: 'priority',
},
],
},
},
{
name: 'should preserve comments',
original: `# This is a top comment
people:
# First person
- name: Charlie
age: 30
# Second person
- name: Alice
age: 25
# Third person
- name: Bob
age: 35`,
expected: `# This is a top comment
people:
# Second person
- name: Alice
age: 25
# Third person
- name: Bob
age: 35
# First person
- name: Charlie
age: 30`,
options: {
arrays: [{ path: 'people', sortKey: 'name' }],
},
},
{
name: 'should sort arrays with numeric values',
original: `versions:
- version: 1.2.0
released: true
- version: 0.9.0
released: true
- version: 2.0.0
released: false`,
expected: `versions:
- version: 0.9.0
released: true
- version: 1.2.0
released: true
- version: 2.0.0
released: false`,
options: {
arrays: [{ path: 'versions', sortKey: 'version' }],
},
},
{
name: 'should sort arrays with boolean values',
original: `features:
- name: auth
enabled: true
- name: notifications
enabled: true
- name: search
enabled: false`,
expected: `features:
- name: search
enabled: false
- name: auth
enabled: true
- name: notifications
enabled: true`,
options: {
arrays: [{ path: 'features', sortKey: 'enabled' }],
},
},
{
name: 'should handle deeply nested structures',
original: `organization:
divisions:
americas:
regions:
- code: US-W
name: West
offices:
- city: Portland
employees: 120
- city: Seattle
employees: 200
- city: San Francisco
employees: 300
- code: US-E
name: East
offices:
- city: New York
employees: 450
- city: Boston
employees: 150`,
expected: `organization:
divisions:
americas:
regions:
- code: US-W
name: West
offices:
- city: Portland
employees: 120
- city: Seattle
employees: 200
- city: San Francisco
employees: 300
- code: US-E
name: East
offices:
- city: Boston
employees: 150
- city: New York
employees: 450`,
options: {
arrays: [
{
path: 'organization.divisions.americas.regions.*.offices',
sortKey: 'employees',
},
],
},
},
{
name: 'should handle arrays without the sort key',
original: `items:
- id: 3
name: Hammer
- name: Screwdriver
- id: 1
name: Wrench
- id: 2`,
expected: `items:
- id: 1
name: Wrench
- id: 2
- id: 3
name: Hammer
- name: Screwdriver`,
options: {
arrays: [{ path: 'items', sortKey: 'id' }],
},
},
{
name: 'should preserve inline comments when sorting',
original: `configs:
- name: production # Production environment
priority: 3
active: true
- name: staging # Staging environment
priority: 2
active: true
- name: development # Development environment
priority: 1
active: false`,
expected: `configs:
- name: development # Development environment
priority: 1
active: false
- name: staging # Staging environment
priority: 2
active: true
- name: production # Production environment
priority: 3
active: true`,
options: {
arrays: [{ path: 'configs', sortKey: 'priority' }],
},
},
{
name: 'should preserve multi-line comments between items',
original: `services:
# Database service
# Handles all data storage
- name: db
port: 5432
# API service
# Handles external requests
- name: api
port: 3000
# Web service
# Serves the frontend
- name: web
port: 8080`,
expected: `services:
# API service
# Handles external requests
- name: api
port: 3000
# Database service
# Handles all data storage
- name: db
port: 5432
# Web service
# Serves the frontend
- name: web
port: 8080`,
options: {
arrays: [{ path: 'services', sortKey: 'name' }],
},
},
{
name: 'should handle array indexing with [] in path',
original: `data:
- list:
- value: 3
- value: 1
- list:
- value: 5
- value: 2`,
expected: `data:
- list:
- value: 1
- value: 3
- list:
- value: 2
- value: 5`,
options: {
arrays: [{ path: 'data[].list', sortKey: 'value' }],
},
},
{
name: 'should handle wildcard at beginning of path',
original: `store:
products:
- name: Laptop
price: 1200
- name: Phone
price: 800
services:
- name: Repair
price: 150
- name: Installation
price: 50`,
expected: `store:
products:
- name: Phone
price: 800
- name: Laptop
price: 1200
services:
- name: Installation
price: 50
- name: Repair
price: 150`,
options: {
arrays: [
{ path: '*.products', sortKey: 'price' },
{ path: '*.services', sortKey: 'price' },
],
},
},
{
name: 'should handle multiple wildcards in path',
original: `departments:
engineering:
teams:
frontend:
members:
- name: Dave
level: 3
- name: Alice
level: 5
backend:
members:
- name: Bob
level: 4
- name: Carol
level: 2
design:
teams:
ux:
members:
- name: Eve
level: 3
- name: Frank
level: 1`,
expected: `departments:
engineering:
teams:
frontend:
members:
- name: Dave
level: 3
- name: Alice
level: 5
backend:
members:
- name: Carol
level: 2
- name: Bob
level: 4
design:
teams:
ux:
members:
- name: Frank
level: 1
- name: Eve
level: 3`,
options: {
arrays: [{ path: 'departments.*.teams.*.members', sortKey: 'level' }],
},
},
{
name: 'should handle array notation with number indexes',
original: `data:
- id: 1
items:
- order: B
quantity: 5
- order: A
quantity: 10
- id: 2
items:
- order: D
quantity: 2
- order: C
quantity: 8`,
expected: `data:
- id: 1
items:
- order: A
quantity: 10
- order: B
quantity: 5
- id: 2
items:
- order: C
quantity: 8
- order: D
quantity: 2`,
options: {
arrays: [{ path: 'data[].items', sortKey: 'order' }],
},
},
{
name: 'should handle complex path with array notation and wildcards',
original: `repositories:
- name: frontend
branches:
main:
commits:
- hash: abc123
priority: 3
- hash: def456
priority: 1
develop:
commits:
- hash: ghi789
priority: 2
- hash: jkl012
priority: 4
- name: backend
branches:
main:
commits:
- hash: mno345
priority: 5
- hash: pqr678
priority: 2
staging:
commits:
- hash: stu901
priority: 1
- hash: vwx234
priority: 3`,
expected: `repositories:
- name: frontend
branches:
main:
commits:
- hash: def456
priority: 1
- hash: abc123
priority: 3
develop:
commits:
- hash: ghi789
priority: 2
- hash: jkl012
priority: 4
- name: backend
branches:
main:
commits:
- hash: pqr678
priority: 2
- hash: mno345
priority: 5
staging:
commits:
- hash: stu901
priority: 1
- hash: vwx234
priority: 3`,
options: {
arrays: [
{
path: 'repositories[].branches.*.commits',
sortKey: 'priority',
},
],
},
},
{
name: 'should handle missing sort keys by placing them at the end',
original: `projects:
- name: Alpha
priority: 2
status: active
- name: Beta
status: pending
- name: Gamma
priority: 1
status: active
- name: Delta
priority: 3
status: inactive
- name: Epsilon
status: active`,
expected: `projects:
- name: Gamma
priority: 1
status: active
- name: Alpha
priority: 2
status: active
- name: Delta
priority: 3
status: inactive
- name: Beta
status: pending
- name: Epsilon
status: active`,
options: {
arrays: [{ path: 'projects', sortKey: 'priority' }],
},
},
];
testCases.forEach(({ name, original, expected, options = { arrays: [] } }) => {
it(name, () => {
const result = runLint(original, 'test.yaml', options);
if (result) {
expect(result.fixed).to.be.true;
expect(result.output).to.equal(expected);
}
else {
expect(original).to.equal(expected);
}
});
});
it('should ignore non-YAML files', () => {
const result = runLint(`const data = { name: 'test' };`, 'test.js');
expect(result).to.be.null;
});
});
//# sourceMappingURL=sort-yaml-arrays.test.js.map