diff --git a/package.json b/package.json index bcd872685ed..fbd7dfbe1f4 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "build:specs": "./scripts/build/specs.sh ${0:-all} ${1:-yaml}", "build": "yarn build:specs && yarn build:clients", "clean": "rm -rf **/dist **/build **/node_modules", - "cts:generate": "yarn workspace tests cts:generate", + "cts:generate": "yarn workspace tests start", "cts:test": "yarn workspace tests test", "docker:build": "./scripts/docker/build.sh", "docker:clean": "docker stop dev; docker rm -f dev; docker image rm -f api-clients-automation", diff --git a/tests/CTS/templates/javascript.mustache b/tests/CTS/templates/javascript.mustache index 68fdf74ac2a..f43c371470e 100644 --- a/tests/CTS/templates/javascript.mustache +++ b/tests/CTS/templates/javascript.mustache @@ -1,18 +1,21 @@ // @ts-nocheck import { {{client}}, EchoRequester } from '{{{import}}}'; -describe('Common Test Suite', () => { - const client = new {{client}}(process.env.ALGOLIA_APPLICATION_ID, process.env.ALGOLIA_SEARCH_KEY, { requester: new EchoRequester() }); +const client = new {{client}}(process.env.ALGOLIA_APPLICATION_ID, process.env.ALGOLIA_SEARCH_KEY, { requester: new EchoRequester() }); - {{#tests}} - test('{{testName}}', async () => { - const req = await client.{{method}}({{#parameters}}{{{value}}}{{^-last}}, {{/-last}}{{/parameters}}); - expect(req).toMatchObject({ - path: '{{{request.path}}}', - method: '{{{request.method}}}', - {{#request.data}}data: {{{.}}},{{/request.data}} - }) - }); +{{#blocks}} +describe('{{operationId}}', () => { + {{#tests}} + test('{{testName}}', async () => { + const req = await client.{{method}}({{#parameters}}{{{value}}}{{^-last}}, {{/-last}}{{/parameters}}); + expect(req).toMatchObject({ + path: '{{{request.path}}}', + method: '{{{request.method}}}', + {{#request.data}}data: {{{.}}},{{/request.data}} + }) + }); - {{/tests}} -}); + {{/tests}} +}) + +{{/blocks}} diff --git a/tests/generateCTS.ts b/tests/generateCTS.ts index 46033d5d12c..46c4949319c 100644 --- a/tests/generateCTS.ts +++ b/tests/generateCTS.ts @@ -2,16 +2,16 @@ import fsp from 'fs/promises'; import path from 'path'; +import SwaggerParser from '@apidevtools/swagger-parser'; import Mustache from 'mustache'; import type { OpenAPIV3 } from 'openapi-types'; -import SwaggerParser from 'swagger-parser'; import openapitools from '../openapitools.json'; const availableLanguages = ['javascript'] as const; type Language = typeof availableLanguages[number]; -type CTSBlock = { +type Tests = { testName?: string; method: string; parameters: any[]; @@ -22,6 +22,11 @@ type CTSBlock = { }; }; +type CTSBlock = { + operationId: string; + tests: Tests[]; +}; + // Array of test per client type CTS = Record; @@ -68,6 +73,10 @@ function capitalize(str: string): string { async function loadCTSForClient(client: string): Promise { // load the list of operations from the spec const spec = await SwaggerParser.validate(`../specs/${client}/spec.yml`); + if (!spec.paths) { + throw new Error(`No paths found for spec ${client}/spec.yml`); + } + const operations = Object.values(spec.paths) .flatMap((p) => Object.values(p)) .map((obj) => obj.operationId); @@ -78,22 +87,18 @@ async function loadCTSForClient(client: string): Promise { if (!file.name.endsWith('json')) { continue; } - const operationId = file.name.replace('.json', ''); + const fileName = file.name.replace('.json', ''); const fileContent = (await fsp.readFile(file.path)).toString(); if (!fileContent) { - throw new Error( - `cannot read empty file for operationId ${operationId} - ${client} client` - ); + throw new Error(`cannot read empty file ${fileName} - ${client} client`); } - const tests: CTSBlock[] = JSON.parse(fileContent); + const tests: Tests[] = JSON.parse(fileContent); // check test validity against spec - if (!operations.includes(operationId)) { - throw new Error( - `cannot find operationId ${operationId} for the ${client} client` - ); + if (!operations.includes(fileName)) { + throw new Error(`cannot find ${fileName} for the ${client} client`); } for (const test of tests) { @@ -116,8 +121,13 @@ async function loadCTSForClient(client: string): Promise { // stringify request.data too test.request.data = JSON.stringify(test.request.data); } - ctsClient.push(...tests); + + ctsClient.push({ + operationId: fileName, + tests, + }); } + return ctsClient; } @@ -142,7 +152,7 @@ async function generateCode(language: Language): Promise { const code = Mustache.render(template, { import: packageNames[language][client], client: `${capitalize(client)}Api`, - tests: cts[client], + blocks: cts[client], }); await fsp.writeFile( `output/${language}/${client}${extensionForLanguage[language]}`, diff --git a/tests/output/javascript/recommend.test.ts b/tests/output/javascript/recommend.test.ts index 1912ed7b390..68b6c7065a2 100644 --- a/tests/output/javascript/recommend.test.ts +++ b/tests/output/javascript/recommend.test.ts @@ -1,13 +1,13 @@ // @ts-nocheck import { RecommendApi, EchoRequester } from '@algolia/recommend'; -describe('Common Test Suite', () => { - const client = new RecommendApi( - process.env.ALGOLIA_APPLICATION_ID, - process.env.ALGOLIA_SEARCH_KEY, - { requester: new EchoRequester() } - ); +const client = new RecommendApi( + process.env.ALGOLIA_APPLICATION_ID, + process.env.ALGOLIA_SEARCH_KEY, + { requester: new EchoRequester() } +); +describe('getRecommendations', () => { test('get recommendations with minimal parameters', async () => { const req = await client.getRecommendations({ requests: [ diff --git a/tests/output/javascript/search.test.ts b/tests/output/javascript/search.test.ts index 6fd2f9e78ef..e648d5b19e4 100644 --- a/tests/output/javascript/search.test.ts +++ b/tests/output/javascript/search.test.ts @@ -1,13 +1,13 @@ // @ts-nocheck import { SearchApi, EchoRequester } from '@algolia/client-search'; -describe('Common Test Suite', () => { - const client = new SearchApi( - process.env.ALGOLIA_APPLICATION_ID, - process.env.ALGOLIA_SEARCH_KEY, - { requester: new EchoRequester() } - ); +const client = new SearchApi( + process.env.ALGOLIA_APPLICATION_ID, + process.env.ALGOLIA_SEARCH_KEY, + { requester: new EchoRequester() } +); +describe('getDictionarySettings', () => { test('get getDictionarySettings results', async () => { const req = await client.getDictionarySettings(); expect(req).toMatchObject({ @@ -15,7 +15,9 @@ describe('Common Test Suite', () => { method: 'GET', }); }); +}); +describe('searchSynonyms', () => { test('searchSynonyms', async () => { const req = await client.searchSynonyms( 'indexName', @@ -27,7 +29,9 @@ describe('Common Test Suite', () => { method: 'POST', }); }); +}); +describe('saveSynonyms', () => { test('saveSynonyms', async () => { const req = await client.saveSynonyms( 'indexName', @@ -65,7 +69,9 @@ describe('Common Test Suite', () => { ], }); }); +}); +describe('deleteRule', () => { test('deleteRule', async () => { const req = await client.deleteRule('indexName', 'id1'); expect(req).toMatchObject({ @@ -73,7 +79,9 @@ describe('Common Test Suite', () => { method: 'DELETE', }); }); +}); +describe('searchForFacetValues', () => { test('get searchForFacetValues results with minimal parameters', async () => { const req = await client.searchForFacetValues('indexName', 'facetName'); expect(req).toMatchObject({ @@ -98,7 +106,9 @@ describe('Common Test Suite', () => { }, }); }); +}); +describe('getSynonym', () => { test('getSynonym', async () => { const req = await client.getSynonym('indexName', 'id1'); expect(req).toMatchObject({ @@ -106,7 +116,9 @@ describe('Common Test Suite', () => { method: 'GET', }); }); +}); +describe('search', () => { test('search', async () => { const req = await client.search('indexName', { query: 'queryString' }); expect(req).toMatchObject({ @@ -115,7 +127,9 @@ describe('Common Test Suite', () => { data: { query: 'queryString' }, }); }); +}); +describe('setDictionarySettings', () => { test('get setDictionarySettings results with minimal parameters', async () => { const req = await client.setDictionarySettings({ disableStandardEntries: { plurals: { fr: false, en: false, ru: true } }, @@ -149,7 +163,9 @@ describe('Common Test Suite', () => { }, }); }); +}); +describe('getRule', () => { test('getRule', async () => { const req = await client.getRule('indexName', 'id1'); expect(req).toMatchObject({ @@ -157,7 +173,9 @@ describe('Common Test Suite', () => { method: 'GET', }); }); +}); +describe('searchDictionaryEntries', () => { test('get searchDictionaryEntries results with minimal parameters', async () => { const req = await client.searchDictionaryEntries('dictionaryName', { query: 'foo', @@ -182,7 +200,9 @@ describe('Common Test Suite', () => { data: { query: 'foo', page: 4, hitsPerPage: 2, language: 'fr' }, }); }); +}); +describe('batchRules', () => { test('batchRules', async () => { const req = await client.batchRules( 'indexName', @@ -218,7 +238,9 @@ describe('Common Test Suite', () => { ], }); }); +}); +describe('updateApiKey', () => { test('updateApiKey', async () => { const req = await client.updateApiKey('myApiKey', { acl: ['search', 'addObject'], @@ -237,7 +259,9 @@ describe('Common Test Suite', () => { }, }); }); +}); +describe('getDictionaryLanguages', () => { test('get getDictionaryLanguages', async () => { const req = await client.getDictionaryLanguages(); expect(req).toMatchObject({ @@ -245,7 +269,9 @@ describe('Common Test Suite', () => { method: 'GET', }); }); +}); +describe('deleteApiKey', () => { test('deleteApiKey', async () => { const req = await client.deleteApiKey('myTestApiKey'); expect(req).toMatchObject({ @@ -253,7 +279,9 @@ describe('Common Test Suite', () => { method: 'DELETE', }); }); +}); +describe('searchRules', () => { test('searchRules', async () => { const req = await client.searchRules('indexName', { query: 'something' }); expect(req).toMatchObject({ @@ -262,7 +290,9 @@ describe('Common Test Suite', () => { data: { query: 'something' }, }); }); +}); +describe('clearAllSynonyms', () => { test('clearAllSynonyms', async () => { const req = await client.clearAllSynonyms('indexName'); expect(req).toMatchObject({ @@ -270,7 +300,9 @@ describe('Common Test Suite', () => { method: 'POST', }); }); +}); +describe('saveRule', () => { test('saveRule', async () => { const req = await client.saveRule( 'indexName', @@ -292,7 +324,9 @@ describe('Common Test Suite', () => { }, }); }); +}); +describe('addApiKey', () => { test('addApiKey', async () => { const req = await client.addApiKey({ acl: ['search', 'addObject'], @@ -313,7 +347,9 @@ describe('Common Test Suite', () => { }, }); }); +}); +describe('restoreApiKey', () => { test('restoreApiKey', async () => { const req = await client.restoreApiKey('myApiKey'); expect(req).toMatchObject({ @@ -321,7 +357,9 @@ describe('Common Test Suite', () => { method: 'POST', }); }); +}); +describe('getApiKey', () => { test('getApiKey', async () => { const req = await client.getApiKey('myTestApiKey'); expect(req).toMatchObject({ @@ -329,7 +367,9 @@ describe('Common Test Suite', () => { method: 'GET', }); }); +}); +describe('browse', () => { test('get browse results with minimal parameters', async () => { const req = await client.browse('indexName'); expect(req).toMatchObject({ @@ -349,7 +389,9 @@ describe('Common Test Suite', () => { data: { params: "query=foo&facetFilters=['bar']", cursor: 'cts' }, }); }); +}); +describe('deleteSynonym', () => { test('deleteSynonym', async () => { const req = await client.deleteSynonym('indexName', 'id1'); expect(req).toMatchObject({ @@ -357,7 +399,9 @@ describe('Common Test Suite', () => { method: 'DELETE', }); }); +}); +describe('clearRules', () => { test('clearRules', async () => { const req = await client.clearRules('indexName'); expect(req).toMatchObject({ @@ -365,7 +409,9 @@ describe('Common Test Suite', () => { method: 'POST', }); }); +}); +describe('batchDictionaryEntries', () => { test('get batchDictionaryEntries results with minimal parameters', async () => { const req = await client.batchDictionaryEntries('dictionaryName', { requests: [ @@ -445,7 +491,9 @@ describe('Common Test Suite', () => { }, }); }); +}); +describe('listApiKeys', () => { test('listApiKeys', async () => { const req = await client.listApiKeys(); expect(req).toMatchObject({ @@ -453,7 +501,9 @@ describe('Common Test Suite', () => { method: 'GET', }); }); +}); +describe('saveSynonym', () => { test('saveSynonym', async () => { const req = await client.saveSynonym( 'indexName', diff --git a/tests/package.json b/tests/package.json index 81588bc7420..c8f88d2b1ed 100644 --- a/tests/package.json +++ b/tests/package.json @@ -2,13 +2,15 @@ "name": "tests", "version": "1.0.0", "scripts": { - "test": "jest", + "build": "tsc", "lint:fix": "yarn eslint --ext=ts output --fix", - "cts:generate": "ts-node -T generateCTS.ts ${0:-all} && yarn lint:fix" + "test": "jest", + "start": "yarn build && node dist/tests/generateCTS.js ${0:-all} && yarn lint:fix" }, "devDependencies": { "@algolia/client-search": "5.0.0", "@algolia/recommend": "5.0.0", + "@apidevtools/swagger-parser": "10.0.3", "@types/jest": "27.0.3", "@types/mustache": "4.1.2", "@types/node": "16.11.11", @@ -18,9 +20,7 @@ "mustache": "4.2.0", "openapi-types": "9.3.1", "prettier": "2.5.0", - "swagger-parser": "10.0.3", "ts-jest": "27.0.7", - "ts-node": "10.4.0", "typescript": "4.5.2" } } diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 00000000000..9ecd9c43370 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": false, + "suppressImplicitAnyIndexErrors": true, + "target": "ES6", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "strict": true, + "moduleResolution": "node", + "removeComments": true, + "sourceMap": true, + "noLib": false, + "declaration": true, + "lib": ["dom", "es6", "es5", "dom.iterable", "scripthost"], + "outDir": "dist", + "typeRoots": ["../node_modules/@types"], + "types": ["node"], + "resolveJsonModule": true + }, + "exclude": ["dist", "output"] +} diff --git a/yarn.lock b/yarn.lock index d640cc521c4..1266d96ded5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -529,22 +529,6 @@ __metadata: languageName: node linkType: hard -"@cspotcode/source-map-consumer@npm:0.8.0": - version: 0.8.0 - resolution: "@cspotcode/source-map-consumer@npm:0.8.0" - checksum: c0c16ca3d2f58898f1bd74c4f41a189dbcc202e642e60e489cbcc2e52419c4e89bdead02c886a12fb13ea37798ede9e562b2321df997ebc210ae9bd881561b4e - languageName: node - linkType: hard - -"@cspotcode/source-map-support@npm:0.7.0": - version: 0.7.0 - resolution: "@cspotcode/source-map-support@npm:0.7.0" - dependencies: - "@cspotcode/source-map-consumer": 0.8.0 - checksum: 9faddda7757cd778b5fd6812137b2cc265810043680d6399acc20441668fafcdc874053be9dccd0d9110087287bfad27eb3bf342f72bceca9aa9059f5d0c4be8 - languageName: node - linkType: hard - "@es-joy/jsdoccomment@npm:0.12.0": version: 0.12.0 resolution: "@es-joy/jsdoccomment@npm:0.12.0" @@ -990,34 +974,6 @@ __metadata: languageName: node linkType: hard -"@tsconfig/node10@npm:^1.0.7": - version: 1.0.8 - resolution: "@tsconfig/node10@npm:1.0.8" - checksum: b8d5fffbc6b17ef64ef74f7fdbccee02a809a063ade785c3648dae59406bc207f70ea2c4296f92749b33019fa36a5ae716e42e49cc7f1bbf0fd147be0d6b970a - languageName: node - linkType: hard - -"@tsconfig/node12@npm:^1.0.7": - version: 1.0.9 - resolution: "@tsconfig/node12@npm:1.0.9" - checksum: a01b2400ab3582b86b589c6d31dcd0c0656f333adecde85d6d7d4086adb059808b82692380bb169546d189bf771ae21d02544a75b57bd6da4a5dd95f8567bec9 - languageName: node - linkType: hard - -"@tsconfig/node14@npm:^1.0.0": - version: 1.0.1 - resolution: "@tsconfig/node14@npm:1.0.1" - checksum: 976345e896c0f059867f94f8d0f6ddb8b1844fb62bf36b727de8a9a68f024857e5db97ed51d3325e23e0616a5e48c034ff51a8d595b3fe7e955f3587540489be - languageName: node - linkType: hard - -"@tsconfig/node16@npm:^1.0.2": - version: 1.0.2 - resolution: "@tsconfig/node16@npm:1.0.2" - checksum: ca94d3639714672bbfd55f03521d3f56bb6a25479bd425da81faf21f13e1e9d15f40f97377dedbbf477a5841c5b0c8f4cd1b391f33553d750b9202c54c2c07aa - languageName: node - linkType: hard - "@types/babel__core@npm:^7.0.0, @types/babel__core@npm:^7.1.14": version: 7.1.16 resolution: "@types/babel__core@npm:7.1.16" @@ -1301,13 +1257,6 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.1.1": - version: 8.2.0 - resolution: "acorn-walk@npm:8.2.0" - checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 - languageName: node - linkType: hard - "acorn@npm:^7.1.1": version: 7.4.1 resolution: "acorn@npm:7.4.1" @@ -1317,7 +1266,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.6.0": +"acorn@npm:^8.2.4, acorn@npm:^8.6.0": version: 8.6.0 resolution: "acorn@npm:8.6.0" bin: @@ -1443,13 +1392,6 @@ __metadata: languageName: node linkType: hard -"arg@npm:^4.1.0": - version: 4.1.3 - resolution: "arg@npm:4.1.3" - checksum: 544af8dd3f60546d3e4aff084d451b96961d2267d668670199692f8d054f0415d86fc5497d0e641e91546f0aa920e7c29e5250e99fc89f5552a34b5d93b77f43 - languageName: node - linkType: hard - "argparse@npm:^1.0.7": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -2058,13 +2000,6 @@ __metadata: languageName: node linkType: hard -"create-require@npm:^1.1.0": - version: 1.1.1 - resolution: "create-require@npm:1.1.1" - checksum: a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff - languageName: node - linkType: hard - "cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -2235,13 +2170,6 @@ __metadata: languageName: node linkType: hard -"diff@npm:^4.0.1": - version: 4.0.2 - resolution: "diff@npm:4.0.2" - checksum: f2c09b0ce4e6b301c221addd83bf3f454c0bc00caa3dd837cf6c127d6edf7223aa2bbe3b688feea110b7f262adbfc845b757c44c8a9f8c0c5b15d8fa9ce9d20d - languageName: node - linkType: hard - "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -4448,7 +4376,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"make-error@npm:1.x, make-error@npm:^1.1.1": +"make-error@npm:1.x": version: 1.3.6 resolution: "make-error@npm:1.3.6" checksum: b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 @@ -5716,15 +5644,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"swagger-parser@npm:10.0.3": - version: 10.0.3 - resolution: "swagger-parser@npm:10.0.3" - dependencies: - "@apidevtools/swagger-parser": 10.0.3 - checksum: bd99d94543b9bdc6bf062ea211f70d4fa004e6e8ce97ee47a388a046f3cc2839e7349970e8a0b11136b55a674e1cc7b0bbdfbe4fb40adf0c68bc70736eedb8b7 - languageName: node - linkType: hard - "symbol-tree@npm:^3.2.4": version: 3.2.4 resolution: "symbol-tree@npm:3.2.4" @@ -5773,6 +5692,7 @@ fsevents@^2.3.2: dependencies: "@algolia/client-search": 5.0.0 "@algolia/recommend": 5.0.0 + "@apidevtools/swagger-parser": 10.0.3 "@types/jest": 27.0.3 "@types/mustache": 4.1.2 "@types/node": 16.11.11 @@ -5782,9 +5702,7 @@ fsevents@^2.3.2: mustache: 4.2.0 openapi-types: 9.3.1 prettier: 2.5.0 - swagger-parser: 10.0.3 ts-jest: 27.0.7 - ts-node: 10.4.0 typescript: 4.5.2 languageName: unknown linkType: soft @@ -5909,42 +5827,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"ts-node@npm:10.4.0": - version: 10.4.0 - resolution: "ts-node@npm:10.4.0" - dependencies: - "@cspotcode/source-map-support": 0.7.0 - "@tsconfig/node10": ^1.0.7 - "@tsconfig/node12": ^1.0.7 - "@tsconfig/node14": ^1.0.0 - "@tsconfig/node16": ^1.0.2 - acorn: ^8.4.1 - acorn-walk: ^8.1.1 - arg: ^4.1.0 - create-require: ^1.1.0 - diff: ^4.0.1 - make-error: ^1.1.1 - yn: 3.1.1 - peerDependencies: - "@swc/core": ">=1.2.50" - "@swc/wasm": ">=1.2.50" - "@types/node": "*" - typescript: ">=2.7" - peerDependenciesMeta: - "@swc/core": - optional: true - "@swc/wasm": - optional: true - bin: - ts-node: dist/bin.js - ts-node-cwd: dist/bin-cwd.js - ts-node-script: dist/bin-script.js - ts-node-transpile-only: dist/bin-transpile.js - ts-script: dist/bin-script-deprecated.js - checksum: 3933ac0a937d33c45e04a6750fcdd3e765eb2897d1da1307cd97ac52af093bcfb632ec0453a75000a65c8b5b7bdb32b1077050a186dcc556e62657cb592e6d49 - languageName: node - linkType: hard - "tsconfig-paths@npm:^3.11.0": version: 3.12.0 resolution: "tsconfig-paths@npm:3.12.0" @@ -6459,13 +6341,6 @@ typescript@4.5.2: languageName: node linkType: hard -"yn@npm:3.1.1": - version: 3.1.1 - resolution: "yn@npm:3.1.1" - checksum: 2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6 - languageName: node - linkType: hard - "z-schema@npm:^5.0.1": version: 5.0.2 resolution: "z-schema@npm:5.0.2"