From ccf66dcb01bff3466867acfd88c25842caa8ea11 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 26 Sep 2023 10:11:00 -0500 Subject: [PATCH] chore: use `eslint-plugin-jest` to lint test code (#37) --- .eslintrc | 9 ++++- package-lock.json | 35 +++++++++++++++++++ package.json | 1 + .../data-frame/DataFrameDataSource.test.ts | 22 ++++++------ src/datasources/notebook/datasource.test.ts | 12 +++---- .../system/SystemDataSource.test.ts | 14 ++++---- .../components/SystemQueryEditor.test.tsx | 4 +-- .../SystemVariableQueryEditor.test.tsx | 2 +- .../tag/components/TagQueryEditor.test.tsx | 6 ++-- 9 files changed, 73 insertions(+), 32 deletions(-) diff --git a/.eslintrc b/.eslintrc index 01e61df..e53a5e0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,10 @@ { - "extends": "./.config/.eslintrc" + "extends": "./.config/.eslintrc", + "overrides": [ + { + "files": ["**/*.test.ts", "**/*.test.tsx"], + "plugins": ["jest"], + "extends": ["plugin:jest/recommended"] + } + ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cefa7ef..232c2b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.7.3", "eslint": "^8.43.0", + "eslint-plugin-jest": "^27.4.0", "eslint-webpack-plugin": "^4.0.1", "fork-ts-checker-webpack-plugin": "^8.0.0", "glob": "^10.2.7", @@ -9323,6 +9324,31 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-jest": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.4.0.tgz", + "integrity": "sha512-ukVeKmMPAUA5SWjHenvyyXnirKfHKMdOsTZdn5tZx5EW05HGVQwBohigjFZGGj3zuv1cV6hc82FvWv6LdIbkgg==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.10.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", + "eslint": "^7.0.0 || ^8.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, "node_modules/eslint-plugin-jsdoc": { "version": "46.2.6", "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.2.6.tgz", @@ -32090,6 +32116,15 @@ "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true }, + "eslint-plugin-jest": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.4.0.tgz", + "integrity": "sha512-ukVeKmMPAUA5SWjHenvyyXnirKfHKMdOsTZdn5tZx5EW05HGVQwBohigjFZGGj3zuv1cV6hc82FvWv6LdIbkgg==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "^5.10.0" + } + }, "eslint-plugin-jsdoc": { "version": "46.2.6", "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.2.6.tgz", diff --git a/package.json b/package.json index 8ab53af..aa1cf38 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.7.3", "eslint": "^8.43.0", + "eslint-plugin-jest": "^27.4.0", "eslint-webpack-plugin": "^4.0.1", "fork-ts-checker-webpack-plugin": "^8.0.0", "glob": "^10.2.7", diff --git a/src/datasources/data-frame/DataFrameDataSource.test.ts b/src/datasources/data-frame/DataFrameDataSource.test.ts index a376c76..33b8197 100644 --- a/src/datasources/data-frame/DataFrameDataSource.test.ts +++ b/src/datasources/data-frame/DataFrameDataSource.test.ts @@ -45,8 +45,8 @@ it('should return data ignoring invalid queries', async () => { await ds.query(query); - expect(fetchMock).toBeCalledTimes(2); - expect(fetchMock).toBeCalledWith(expect.objectContaining({ url: '_/nidataframe/v1/tables/1/query-decimated-data' })); + expect(fetchMock).toHaveBeenCalledTimes(2); + expect(fetchMock).toHaveBeenCalledWith(expect.objectContaining({ url: '_/nidataframe/v1/tables/1/query-decimated-data' })); }); it('should return data for multiple targets', async () => { @@ -57,7 +57,7 @@ it('should return data for multiple targets', async () => { const response = await ds.query(query); - expect(fetchMock).toBeCalledTimes(4); + expect(fetchMock).toHaveBeenCalledTimes(4); expect(response.data).toHaveLength(2); }); @@ -99,7 +99,7 @@ it('should automatically apply time filters when index column is a timestamp', a await ds.query(query); - expect(fetchMock).toBeCalledWith( + expect(fetchMock).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ filters: [ @@ -123,7 +123,7 @@ it('should apply null and NaN filters', async () => { await ds.query(query); - expect(fetchMock).toBeCalledWith( + expect(fetchMock).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ filters: [ @@ -149,7 +149,7 @@ it('should provide decimation parameters correctly', async () => { await ds.query(query); - expect(fetchMock).toBeCalledWith( + expect(fetchMock).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ decimation: { intervals: 300, method: 'ENTRY_EXIT', yColumns: ['int', 'float'] }, @@ -163,12 +163,12 @@ it('should cache table metadata for subsequent requests', async () => { await ds.query(query); - expect(fetchMock).toBeCalledTimes(2); + expect(fetchMock).toHaveBeenCalledTimes(2); expect(fetchMock).toHaveBeenCalledWith(expect.objectContaining({ url: '_/nidataframe/v1/tables/1' })); await ds.query(query); - expect(fetchMock).toBeCalledTimes(3); + expect(fetchMock).toHaveBeenCalledTimes(3); }); it('should return error if query columns do not match table metadata', async () => { @@ -188,7 +188,7 @@ it('should migrate queries using columns of arrays of objects', async () => { await ds.query(query); - expect(fetchMock).toBeCalledWith(expect.objectContaining({ data: expect.objectContaining({ columns: ['float'] }) })); + expect(fetchMock).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ columns: ['float'] }) })); }); it('attempts to replace variables in metadata query', async () => { @@ -197,7 +197,7 @@ it('attempts to replace variables in metadata query', async () => { await ds.getTableMetadata(tableId); - expect(replaceMock).toBeCalledTimes(1); + expect(replaceMock).toHaveBeenCalledTimes(1); expect(replaceMock).toHaveBeenCalledWith(tableId); }); @@ -207,7 +207,7 @@ it('attempts to replace variables in data query', async () => { await ds.query(query); - expect(replaceMock).toBeCalledTimes(2); + expect(replaceMock).toHaveBeenCalledTimes(2); expect(replaceMock).toHaveBeenCalledWith(query.targets[0].tableId, expect.anything()); }); diff --git a/src/datasources/notebook/datasource.test.ts b/src/datasources/notebook/datasource.test.ts index 8a1a02b..0cc4770 100644 --- a/src/datasources/notebook/datasource.test.ts +++ b/src/datasources/notebook/datasource.test.ts @@ -197,9 +197,9 @@ describe('Notebook data source', () => { ds.replaceParameterVariables(parameters, options); - expect(replaceMock).toBeCalledTimes(2); - expect(replaceMock).toBeCalledWith(s1, expect.anything()); - expect(replaceMock).toBeCalledWith(s2, expect.anything()); + expect(replaceMock).toHaveBeenCalledTimes(2); + expect(replaceMock).toHaveBeenCalledWith(s1, expect.anything()); + expect(replaceMock).toHaveBeenCalledWith(s2, expect.anything()); }); it('does not attempt to replace variables in non-string parameters', () => { @@ -213,7 +213,7 @@ describe('Notebook data source', () => { ds.replaceParameterVariables(parameters, options); - expect(replaceMock).not.toBeCalled(); + expect(replaceMock).not.toHaveBeenCalled(); }); }); @@ -291,7 +291,7 @@ describe('Notebook data source', () => { ], } as unknown as DataQueryRequest; - expect(ds.query(options)).rejects.toThrow(); + await expect(ds.query(options)).rejects.toThrow(); }); it('throws error for notebook execution with invalid output', async () => { @@ -306,7 +306,7 @@ describe('Notebook data source', () => { ], } as unknown as DataQueryRequest; - expect(ds.query(options)).rejects.toThrow(); + await expect(ds.query(options)).rejects.toThrow(); }); it('executes notebook with resultCachePeriod', async () => { diff --git a/src/datasources/system/SystemDataSource.test.ts b/src/datasources/system/SystemDataSource.test.ts index b66e43a..beb92aa 100644 --- a/src/datasources/system/SystemDataSource.test.ts +++ b/src/datasources/system/SystemDataSource.test.ts @@ -86,13 +86,11 @@ test('query metadata for one system', async () => { test('query metadata with templated system name', async () => { templateSrv.replace.calledWith('$system_id').mockReturnValue('system-1'); - backendSrv.fetch - .calledWith( - requestMatching({ url: '/nisysmgmt/v1/query-systems', data: { filter: 'id = "system-1" || alias = "system-1"' } }) - ) - .mockReturnValue(createFetchResponse({ data: [fakeSystems[0]] })); + backendSrv.fetch.mockReturnValue(createFetchResponse({ data: [fakeSystems[0]] })); await ds.query(buildQuery({ queryKind: SystemQueryType.Metadata, systemName: '$system_id' })); + + expect(backendSrv.fetch.mock.lastCall?.[0].data).toHaveProperty('filter', 'id = "system-1" || alias = "system-1"'); }); test('queries for system variable values - all workspaces', async () => { @@ -109,11 +107,11 @@ test('queries for system variable values - all workspaces', async () => { }); test('queries for system variable values - single workspace', async () => { - backendSrv.fetch - .calledWith(requestMatching({ url: '/nisysmgmt/v1/query-systems', data: { filter: 'workspace = "1"' } })) - .mockReturnValue(createFetchResponse({ data: fakeSystems.map(({ id, alias }) => ({ id, alias })) })); + backendSrv.fetch.mockReturnValue(createFetchResponse({ data: fakeSystems.map(({ id, alias }) => ({ id, alias })) })); await ds.metricFindQuery({ workspace: '1' }); + + expect(backendSrv.fetch.mock.lastCall?.[0].data).toHaveProperty('filter', 'workspace = "1"'); }); const fakeSystems: SystemMetadata[] = [ diff --git a/src/datasources/system/components/SystemQueryEditor.test.tsx b/src/datasources/system/components/SystemQueryEditor.test.tsx index 8becc09..5575364 100644 --- a/src/datasources/system/components/SystemQueryEditor.test.tsx +++ b/src/datasources/system/components/SystemQueryEditor.test.tsx @@ -26,10 +26,10 @@ it('updates when user interacts with fields', async () => { // User changes query type await userEvent.click(screen.getByRole('radio', { name: 'Metadata' })); - expect(onChange).toBeCalledWith(expect.objectContaining({ queryKind: SystemQueryType.Metadata })); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ queryKind: SystemQueryType.Metadata })); expect(screen.getByPlaceholderText('All systems')).toBeInTheDocument(); // User types system name await userEvent.type(screen.getByLabelText('System'), 'my-system{enter}'); - expect(onChange).toBeCalledWith(expect.objectContaining({ systemName: 'my-system' })); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ systemName: 'my-system' })); }); diff --git a/src/datasources/system/components/SystemVariableQueryEditor.test.tsx b/src/datasources/system/components/SystemVariableQueryEditor.test.tsx index 0704a25..7e7c0d3 100644 --- a/src/datasources/system/components/SystemVariableQueryEditor.test.tsx +++ b/src/datasources/system/components/SystemVariableQueryEditor.test.tsx @@ -31,5 +31,5 @@ test('user selects new workspace', async () => { await workspacesLoaded(); await select(screen.getByRole('combobox'), 'Other workspace', { container: document.body }); - expect(onChange).toBeCalledWith({ workspace: '2' }); + expect(onChange).toHaveBeenCalledWith({ workspace: '2' }); }); diff --git a/src/datasources/tag/components/TagQueryEditor.test.tsx b/src/datasources/tag/components/TagQueryEditor.test.tsx index 450a122..fdadc1e 100644 --- a/src/datasources/tag/components/TagQueryEditor.test.tsx +++ b/src/datasources/tag/components/TagQueryEditor.test.tsx @@ -29,13 +29,13 @@ it('renders with initial query and updates when user makes changes', async () => // Users changes query type await userEvent.click(screen.getByRole('radio', { name: 'Current' })); - expect(onChange).toBeCalledWith(expect.objectContaining({ type: TagQueryType.Current })); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ type: TagQueryType.Current })); // User types in new tag path await userEvent.type(screen.getByLabelText('Tag path'), '.test{enter}'); - expect(onChange).toBeCalledWith(expect.objectContaining({ path: 'my.tag.test' })); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ path: 'my.tag.test' })); // User selects different workspace await select(screen.getByRole('combobox'), 'Other workspace', { container: document.body }); - expect(onChange).toBeCalledWith(expect.objectContaining({ workspace: '2' })); + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ workspace: '2' })); });