blob: 15b056206707823de82ed342942333cc6bbb51bb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { JsonCompletionRule } from './json-completion';
import { getCompletionsForPath } from './json-completion';
describe('json-completion', () => {
describe('getCompletionsForPath', () => {
const mockRules: JsonCompletionRule[] = [
// Root level object properties
{
path: '$',
isObject: true,
completions: [
{ value: 'name', documentation: 'The name property' },
{ value: 'type', documentation: 'The type property' },
],
},
// Root level values
{
path: '$.type',
completions: [
{ value: 'A', documentation: 'Type A' },
{ value: 'B', documentation: 'Type B' },
],
},
// Nested object properties
{
path: '$.config',
isObject: true,
completions: [
{ value: 'enabled', documentation: 'Enable the feature' },
{ value: 'timeout', documentation: 'Timeout in milliseconds' },
],
},
// Array item properties
{
path: '$.items.[]',
isObject: true,
completions: [
{ value: 'id', documentation: 'Item ID' },
{ value: 'value', documentation: 'Item value' },
],
},
// Conditional completion
{
path: '$',
isObject: true,
condition: obj => obj.type === 'A',
completions: [{ value: 'specialPropertyForA', documentation: 'Only for type A' }],
},
// RegExp path
{
path: /^\$\.dynamic\.[^.]+$/,
completions: [{ value: 'dynamicValue', documentation: 'Dynamic property value' }],
},
// Deep nested path
{
path: '$.level1.level2.level3',
isObject: true,
completions: [{ value: 'deepProperty', documentation: 'Deep nested property' }],
},
];
it('should return root level object completions for keys', () => {
const completions = getCompletionsForPath(mockRules, [], true, {});
expect(completions).toEqual([
{ value: 'name', documentation: 'The name property' },
{ value: 'type', documentation: 'The type property' },
]);
});
it('should return value completions for specific paths', () => {
const completions = getCompletionsForPath(mockRules, ['type'], false, {});
expect(completions).toEqual([
{ value: 'A', documentation: 'Type A' },
{ value: 'B', documentation: 'Type B' },
]);
});
it('should return nested object completions', () => {
const completions = getCompletionsForPath(mockRules, ['config'], true, {});
expect(completions).toEqual([
{ value: 'enabled', documentation: 'Enable the feature' },
{ value: 'timeout', documentation: 'Timeout in milliseconds' },
]);
});
it('should handle array paths correctly', () => {
const completions = getCompletionsForPath(mockRules, ['items', '0'], true, {});
expect(completions).toEqual([
{ value: 'id', documentation: 'Item ID' },
{ value: 'value', documentation: 'Item value' },
]);
});
it('should handle multiple array indices in path', () => {
const completions = getCompletionsForPath(mockRules, ['items', '0'], true, {});
expect(completions).toEqual([
{ value: 'id', documentation: 'Item ID' },
{ value: 'value', documentation: 'Item value' },
]);
});
it('should apply conditional completions when condition is met', () => {
const completions = getCompletionsForPath(mockRules, [], true, { type: 'A' });
expect(completions).toContainEqual({
value: 'specialPropertyForA',
documentation: 'Only for type A',
});
});
it('should not apply conditional completions when condition is not met', () => {
const completions = getCompletionsForPath(mockRules, [], true, { type: 'B' });
expect(completions).not.toContainEqual({
value: 'specialPropertyForA',
documentation: 'Only for type A',
});
});
it('should match RegExp paths', () => {
const completions = getCompletionsForPath(mockRules, ['dynamic', 'someProperty'], false, {});
expect(completions).toEqual([
{ value: 'dynamicValue', documentation: 'Dynamic property value' },
]);
});
it('should not match RegExp paths that do not match', () => {
const completions = getCompletionsForPath(
mockRules,
['dynamic', 'prop1', 'prop2'],
false,
{},
);
expect(completions).toEqual([]);
});
it('should handle deep nested paths', () => {
const completions = getCompletionsForPath(
mockRules,
['level1', 'level2', 'level3'],
true,
{},
);
expect(completions).toEqual([
{ value: 'deepProperty', documentation: 'Deep nested property' },
]);
});
it('should return empty array when no rules match', () => {
const completions = getCompletionsForPath(mockRules, ['nonexistent', 'path'], true, {});
expect(completions).toEqual([]);
});
it('should distinguish between key and value completions', () => {
// Looking for keys (isObject: true)
const keyCompletions = getCompletionsForPath(mockRules, [], true, {});
expect(keyCompletions.map(c => c.value)).toContain('name');
// Looking for values (isObject: false or undefined)
const valueCompletions = getCompletionsForPath(mockRules, [], false, {});
expect(valueCompletions).toEqual([]);
});
it('should combine completions from multiple matching rules', () => {
const multipleMatchRules: JsonCompletionRule[] = [
{
path: '$',
isObject: true,
completions: [{ value: 'prop1' }],
},
{
path: '$',
isObject: true,
completions: [{ value: 'prop2' }],
},
{
path: '$',
isObject: true,
condition: () => true,
completions: [{ value: 'prop3' }],
},
];
const completions = getCompletionsForPath(multipleMatchRules, [], true, {});
expect(completions).toHaveLength(3);
expect(completions.map(c => c.value)).toEqual(['prop1', 'prop2', 'prop3']);
});
it('should handle empty rules array', () => {
const completions = getCompletionsForPath([], ['any', 'path'], true, {});
expect(completions).toEqual([]);
});
it('should handle empty path array', () => {
const completions = getCompletionsForPath(mockRules, [], true, {});
expect(completions).toEqual([
{ value: 'name', documentation: 'The name property' },
{ value: 'type', documentation: 'The type property' },
]);
});
it('should handle complex array paths', () => {
const complexRules: JsonCompletionRule[] = [
{
path: '$.matrix.[].[]',
isObject: true,
completions: [{ value: 'cell' }],
},
{
path: '$.data.[].items.[].value',
completions: [{ value: 'itemValue' }],
},
];
// Test matrix path
const matrixCompletions = getCompletionsForPath(complexRules, ['matrix', '0', '1'], true, {});
expect(matrixCompletions).toEqual([{ value: 'cell' }]);
// Test nested array path
const nestedCompletions = getCompletionsForPath(
complexRules,
['data', '2', 'items', '5', 'value'],
false,
{},
);
expect(nestedCompletions).toEqual([{ value: 'itemValue' }]);
});
it('should handle paths with special characters', () => {
const specialRules: JsonCompletionRule[] = [
{
path: '$.config-name',
isObject: true,
completions: [{ value: 'sub-property' }],
},
{
path: '$["special.property"]',
completions: [{ value: 'special-value' }],
},
];
const completions = getCompletionsForPath(specialRules, ['config-name'], true, {});
expect(completions).toEqual([{ value: 'sub-property' }]);
});
});
describe('pathToString', () => {
// Since pathToString is not exported, we test it indirectly through getCompletionsForPath
it('should convert array indices to [] notation', () => {
const rules: JsonCompletionRule[] = [
{
path: '$.items.[]',
isObject: true,
completions: [{ value: 'test' }],
},
];
// Test single array index
let completions = getCompletionsForPath(rules, ['items', '0'], true, {});
expect(completions).toHaveLength(1);
// Test different array index
completions = getCompletionsForPath(rules, ['items', '99'], true, {});
expect(completions).toHaveLength(1);
// Test non-array path
completions = getCompletionsForPath(rules, ['items', 'notAnIndex'], true, {});
expect(completions).toHaveLength(0);
});
it('should handle multiple array indices', () => {
const rules: JsonCompletionRule[] = [
{
path: '$.data.[].items.[]',
isObject: true,
completions: [{ value: 'test' }],
},
];
const completions = getCompletionsForPath(rules, ['data', '0', 'items', '5'], true, {});
expect(completions).toHaveLength(1);
});
it('should handle paths starting with array index', () => {
const rules: JsonCompletionRule[] = [
{
path: '$.[].property',
isObject: true,
completions: [{ value: 'test' }],
},
];
const completions = getCompletionsForPath(rules, ['0', 'property'], true, {});
expect(completions).toHaveLength(1);
});
});
});