[ES|QL] unskip autocomplete tests (#182273)

## Summary

A `.only` snuck into the autocomplete tests a few PRs back and this PR
fixes that... FOR GOOD :D
This commit is contained in:
Drew Tate 2024-05-02 08:37:50 -06:00 committed by GitHub
parent 15d2235a73
commit b694a894ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 146 additions and 113 deletions

View file

@ -16,12 +16,7 @@ module.exports = {
files: ['**/*.{ts,tsx}'],
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
'ban',
'import',
'eslint-comments'
],
plugins: ['@typescript-eslint', 'ban', 'import', 'eslint-comments'],
env: {
es6: true,
@ -34,7 +29,7 @@ module.exports = {
sourceType: 'module',
ecmaVersion: 2018,
ecmaFeatures: {
jsx: true
jsx: true,
},
// NOTE: That is to avoid a known performance issue related with the `ts.Program` used by
// typescript eslint. As we are not using rules that need types information, we can safely
@ -43,7 +38,7 @@ module.exports = {
// https://github.com/typescript-eslint/typescript-eslint/issues/389
// https://github.com/typescript-eslint/typescript-eslint/issues/243
// https://github.com/typescript-eslint/typescript-eslint/pull/361
project: undefined
project: undefined,
},
// NOTE: we can't override the extends option here to apply
@ -60,32 +55,38 @@ module.exports = {
//
// Old recommended tslint rules
'@typescript-eslint/adjacent-overload-signatures': 'error',
'@typescript-eslint/array-type': ['error', { default: 'array-simple', readonly: 'array-simple' }],
'@typescript-eslint/ban-types': ['error', {
types: {
SFC: {
message: 'Use FC or FunctionComponent instead.',
fixWith: 'FC'
'@typescript-eslint/array-type': [
'error',
{ default: 'array-simple', readonly: 'array-simple' },
],
'@typescript-eslint/ban-types': [
'error',
{
types: {
SFC: {
message: 'Use FC or FunctionComponent instead.',
fixWith: 'FC',
},
'React.SFC': {
message: 'Use FC or FunctionComponent instead.',
fixWith: 'React.FC',
},
StatelessComponent: {
message: 'Use FunctionComponent instead.',
fixWith: 'FunctionComponent',
},
'React.StatelessComponent': {
message: 'Use FunctionComponent instead.',
fixWith: 'React.FunctionComponent',
},
// used in the codebase in the wild
'{}': false,
object: false,
Function: false,
},
'React.SFC': {
message: 'Use FC or FunctionComponent instead.',
fixWith: 'React.FC'
},
StatelessComponent: {
message: 'Use FunctionComponent instead.',
fixWith: 'FunctionComponent'
},
'React.StatelessComponent': {
message: 'Use FunctionComponent instead.',
fixWith: 'React.FunctionComponent'
},
// used in the codebase in the wild
'{}': false,
'object': false,
'Function': false,
}
}],
'camelcase': 'off',
},
],
camelcase: 'off',
'@typescript-eslint/naming-convention': [
'error',
{
@ -93,8 +94,8 @@ module.exports = {
format: ['camelCase'],
filter: {
regex: allowedNameRegexp,
match: false
}
match: false,
},
},
{
selector: 'variable',
@ -105,19 +106,16 @@ module.exports = {
],
filter: {
regex: allowedNameRegexp,
match: false
}
match: false,
},
},
{
selector: 'parameter',
format: [
'camelCase',
'PascalCase',
],
format: ['camelCase', 'PascalCase'],
filter: {
regex: allowedNameRegexp,
match: false
}
match: false,
},
},
{
selector: 'memberLike',
@ -125,23 +123,23 @@ module.exports = {
'camelCase',
'PascalCase',
'snake_case', // keys in elasticsearch requests / responses
'UPPER_CASE'
'UPPER_CASE',
],
filter: {
regex: allowedNameRegexp,
match: false
}
match: false,
},
},
{
selector: 'function',
format: [
'camelCase',
'PascalCase' // React.FunctionComponent =
'PascalCase', // React.FunctionComponent =
],
filter: {
regex: allowedNameRegexp,
match: false
}
match: false,
},
},
{
selector: 'typeLike',
@ -164,27 +162,31 @@ module.exports = {
'objectLiteralMethod',
'typeMethod',
'accessor',
'enumMember'
'enumMember',
],
format: null,
modifiers: ['requiresQuotes']
}
modifiers: ['requiresQuotes'],
},
],
'@typescript-eslint/explicit-member-accessibility': ['error',
'@typescript-eslint/explicit-member-accessibility': [
'error',
{
accessibility: 'off',
overrides: {
accessors: 'explicit',
constructors: 'no-public',
parameterProperties: 'explicit'
}
}
parameterProperties: 'explicit',
},
},
],
'@typescript-eslint/prefer-function-type': 'error',
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
'@typescript-eslint/member-ordering': ['error', {
'default': ['public-static-field', 'static-field', 'instance-field']
}],
'@typescript-eslint/member-ordering': [
'error',
{
default: ['public-static-field', 'static-field', 'instance-field'],
},
],
'@typescript-eslint/consistent-type-assertions': 'error',
'@typescript-eslint/no-empty-interface': 'error',
'@typescript-eslint/no-extra-non-null-assertion': 'error',
@ -195,24 +197,26 @@ module.exports = {
'@typescript-eslint/no-undef': 'off',
'no-undef': 'off',
'@typescript-eslint/triple-slash-reference': ['error', {
path: 'never',
types: 'never',
lib: 'never'
}],
'@typescript-eslint/triple-slash-reference': [
'error',
{
path: 'never',
types: 'never',
lib: 'never',
},
],
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/unified-signatures': 'error',
'constructor-super': 'error',
'dot-notation': 'error',
'eqeqeq': ['error', 'always', {'null': 'ignore'}],
eqeqeq: ['error', 'always', { null: 'ignore' }],
'guard-for-in': 'error',
'import/order': ['error', {
'groups': [
['external', 'builtin'],
'internal',
['parent', 'sibling', 'index'],
],
}],
'import/order': [
'error',
{
groups: [['external', 'builtin'], 'internal', ['parent', 'sibling', 'index']],
},
],
'max-classes-per-file': ['error', 1],
'no-bitwise': 'error',
'no-caller': 'error',
@ -233,22 +237,27 @@ module.exports = {
'no-unused-labels': 'error',
'no-var': 'error',
'object-shorthand': 'error',
'one-var': [ 'error', 'never' ],
'one-var': ['error', 'never'],
'prefer-const': 'error',
'prefer-rest-params': 'error',
'radix': 'error',
'spaced-comment': ["error", "always", {
"exceptions": ["/"]
}],
radix: 'error',
'spaced-comment': [
'error',
'always',
{
exceptions: ['/'],
},
],
'use-isnan': 'error',
// Old tslint yml override or defined rules
'ban/ban': [
2,
{'name': ['describe', 'only'], 'message': 'No exclusive suites.'},
{'name': ['it', 'only'], 'message': 'No exclusive tests.'},
{'name': ['test', 'only'], 'message': 'No exclusive tests.'},
{ name: ['describe', 'only'], message: 'No exclusive suites.' },
{ name: ['it', 'only'], message: 'No exclusive tests.' },
{ name: ['test', 'only'], message: 'No exclusive tests.' },
{ name: ['testSuggestions', 'only'], message: 'No exclusive tests.' },
{ name: ['testErrorsAndWarnings', 'only'], message: 'No exclusive tests.' },
],
'import/no-default-export': 'error',
@ -257,13 +266,13 @@ module.exports = {
'no-restricted-syntax': [
'error',
{
"selector": "TSEnumDeclaration[const=true]",
"message": "Do not use `const` with enum declarations"
}
]
selector: 'TSEnumDeclaration[const=true]',
message: 'Do not use `const` with enum declarations',
},
],
},
eslintConfigPrettierRules
)
),
},
]
],
};

View file

@ -12,10 +12,9 @@ import { builtinFunctions } from '../definitions/builtin';
import { statsAggregationFunctionDefinitions } from '../definitions/aggs';
import { chronoLiterals, timeLiterals } from '../definitions/literals';
import { commandDefinitions } from '../definitions/commands';
import { TRIGGER_SUGGESTION_COMMAND } from './factories';
import { getUnitDuration, TRIGGER_SUGGESTION_COMMAND } from './factories';
import { camelCase } from 'lodash';
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
import { SuggestionRawDefinition } from './types';
import { groupingFunctionDefinitions } from '../definitions/grouping';
const triggerCharacters = [',', '(', '=', ' '];
@ -231,14 +230,14 @@ function getPolicyFields(policyName: string) {
describe('autocomplete', () => {
type TestArgs = [
string,
Array<string | Partial<SuggestionRawDefinition>>,
string[],
(string | number)?,
Parameters<typeof createCustomCallbackMocks>?
];
const testSuggestionsFn = (
statement: string,
expected: Array<string | Partial<SuggestionRawDefinition>>,
expected: string[],
triggerCharacter: string | number = '',
customCallbacksArgs: Parameters<typeof createCustomCallbackMocks> = [
undefined,
@ -271,28 +270,20 @@ describe('autocomplete', () => {
async (text) => (text ? getAstAndSyntaxErrors(text) : { ast: [], errors: [] }),
callbackMocks
);
const suggestionInertTextSorted = suggestions
// simulate the editor behaviour for sorting suggestions
// copied from https://github.com/microsoft/vscode/blob/0a141d23179c76c5771df25a43546d9d9b6ed71c/src/vs/workbench/contrib/testing/browser/testingDecorations.ts#L971-L972
// still not sure how accurate this is...
.sort((a, b) => (a.sortText || a.label).localeCompare(b.sortText || b.label));
expect(suggestionInertTextSorted).toHaveLength(expected.length);
for (const [index, receivedSuggestion] of suggestionInertTextSorted.entries()) {
if (typeof expected[index] !== 'object') {
expect(receivedSuggestion.text).toEqual(expected[index]);
} else {
// check all properties that are defined in the expected suggestion
for (const [key, value] of Object.entries(expected[index])) {
expect(receivedSuggestion[key as keyof SuggestionRawDefinition]).toEqual(value);
}
}
}
const sortedSuggestions = suggestions.map((suggestion) => suggestion.text).sort();
const sortedExpected = expected.sort();
expect(sortedSuggestions).toEqual(sortedExpected);
}
);
};
// Enrich the function to work with .only and .skip as regular test function
//
// DO NOT CHANGE THE NAME OF THIS FUNCTION WITHOUT ALSO CHANGING
// THE LINTER RULE IN packages/kbn-eslint-config/typescript.js
//
const testSuggestions = Object.assign(testSuggestionsFn, {
skip: (...args: TestArgs) => {
const paddingArgs = ['', [undefined, undefined, undefined]].slice(args.length - 2);
@ -616,6 +607,16 @@ describe('autocomplete', () => {
'any',
{
evalMath: true,
grouping: false,
},
undefined,
undefined,
'by'
);
const allGroupingFunctions = getFunctionSignaturesByReturnType(
'stats',
'any',
{
grouping: true,
},
undefined,
@ -623,25 +624,26 @@ describe('autocomplete', () => {
'by'
);
testSuggestions('from a | stats ', ['var0 =', ...allAggFunctions, ...allEvaFunctions]);
testSuggestions('from a | stats a ', [
{ text: '= $0', asSnippet: true, command: TRIGGER_SUGGESTION_COMMAND },
]);
testSuggestions('from a | stats a ', ['= $0']);
testSuggestions('from a | stats a=', [...allAggFunctions, ...allEvaFunctions]);
testSuggestions.only('from a | stats a=max(b) by ', [
testSuggestions('from a | stats a=max(b) by ', [
'var0 =',
...getFieldNamesByType('any'),
...allEvaFunctions,
...allGroupingFunctions,
]);
testSuggestions('from a | stats a=max(b) BY ', [
'var0 =',
...getFieldNamesByType('any'),
...allEvaFunctions,
...allGroupingFunctions,
]);
testSuggestions('from a | stats a=c by d ', [',', '|']);
testSuggestions('from a | stats a=c by d, ', [
'var0 =',
...getFieldNamesByType('any'),
...allEvaFunctions,
...allGroupingFunctions,
]);
testSuggestions('from a | stats a=max(b), ', [
'var0 =',
@ -663,6 +665,7 @@ describe('autocomplete', () => {
'var0 =',
...getFieldNamesByType('any'),
...allEvaFunctions,
...allGroupingFunctions,
]);
testSuggestions('from a | stats a=min(b),', ['var0 =', ...allAggFunctions, ...allEvaFunctions]);
testSuggestions('from a | stats var0=min(b),var1=c,', [
@ -705,19 +708,23 @@ describe('autocomplete', () => {
...getFieldNamesByType('number'),
'`avg(b)`',
...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }),
...allGroupingFunctions,
]);
testSuggestions('from a | stats avg(b) by var0 = ', [
...getFieldNamesByType('any'),
...allEvaFunctions,
...allGroupingFunctions,
]);
testSuggestions('from a | stats avg(b) by c, ', [
'var0 =',
...getFieldNamesByType('any'),
...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }),
...allGroupingFunctions,
]);
testSuggestions('from a | stats avg(b) by c, var0 = ', [
...getFieldNamesByType('any'),
...allEvaFunctions,
...allGroupingFunctions,
]);
testSuggestions('from a | stats avg(b) by numberField % 2 ', [',', '|']);
@ -1160,7 +1167,7 @@ describe('autocomplete', () => {
}
}
testSuggestions('from a | eval var0 = bucket(@timestamp,', []);
testSuggestions('from a | eval var0 = bucket(@timestamp,', getUnitDuration(1));
describe('date math', () => {
const dateSuggestions = timeLiterals.map(({ name }) => name);

View file

@ -289,7 +289,7 @@ export const buildNoPoliciesAvailableDefinition = (): SuggestionRawDefinition =>
},
});
function getUnitDuration(unit: number = 1) {
export function getUnitDuration(unit: number = 1) {
const filteredTimeLiteral = timeLiterals.filter(({ name }) => {
const result = /s$/.test(name);
return unit > 1 ? result : !result;
@ -297,6 +297,19 @@ function getUnitDuration(unit: number = 1) {
return filteredTimeLiteral.map(({ name }) => `${unit} ${name}`);
}
/**
* Given information about the current command and the parameter type, suggest
* some literals that may make sense.
*
* TODO this currently tries to cover both command-specific suggestions and type
* suggestions. We could consider separating the two... or just using parameter types
* and forgetting about command-specific suggestions altogether.
*
* Another thought... should literal suggestions be defined in the definitions file?
* That approach might allow for greater specificity in the suggestions and remove some
* "magical" logic. Maybe this is really the same thing as the literalOptions parameter
* definition property...
*/
export function getCompatibleLiterals(commandName: string, types: string[], names?: string[]) {
const suggestions: SuggestionRawDefinition[] = [];
if (types.includes('number')) {

View file

@ -362,6 +362,10 @@ describe('validation logic', () => {
type TestArgs = [string, string[], string[]?];
// Make only and skip work with our custom wrapper
//
// DO NOT CHANGE THE NAME OF THIS FUNCTION WITHOUT ALSO CHANGING
// THE LINTER RULE IN packages/kbn-eslint-config/typescript.js
//
const testErrorsAndWarnings = Object.assign(testErrorsAndWarningsFn, {
skip: (...args: TestArgs) => {
const warningArgs = [[]].slice(args.length - 2);