From 60d4142d5fa26c615599070934d015cfbfa44612 Mon Sep 17 00:00:00 2001 From: Robin Monnier Date: Tue, 17 Dec 2024 18:25:53 +0100 Subject: [PATCH] feat: error if request not intercepted while e2e --- app/layout.tsx | 2 +- clients/stub-client-with-snaphots.ts | 121 ------------------- cypress/fixtures/api-inclusion-metadata.json | 57 +++++++++ cypress/mocks/handlers/api-inclusion.ts | 7 ++ cypress/mocks/routes.ts | 9 +- 5 files changed, 73 insertions(+), 123 deletions(-) delete mode 100644 clients/stub-client-with-snaphots.ts create mode 100644 cypress/fixtures/api-inclusion-metadata.json diff --git a/app/layout.tsx b/app/layout.tsx index f5cb258b6..263658cf5 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -17,7 +17,7 @@ if ( // https://github.com/mswjs/examples/pull/101/files const { mockServer } = require('#cypress/mocks/server'); mockServer.listen({ - onUnhandledRequest: 'warn', + onUnhandledRequest: 'error', }); } diff --git a/clients/stub-client-with-snaphots.ts b/clients/stub-client-with-snaphots.ts deleted file mode 100644 index 282aaa56c..000000000 --- a/clients/stub-client-with-snaphots.ts +++ /dev/null @@ -1,121 +0,0 @@ -type AsyncFn = (...args: T) => Promise; - -/** - * Stub a client when we are in E2E test context. - * - * We want to test the whole application, but we don't want to call the real API, - * because it would be slow and unreliable. - * - * Instead, we want to call a stubbed version of the client function, - * based on a JSON file generated by snapshot tests. - * - * @param client: an object with a uniq key containing the client function - * @returns a stubbed client function - */ -export default function stubClientWithSnapshots< - Name extends string, - Args extends unknown[], - R ->(arg: Record>): AsyncFn { - const clientName = Object.keys(arg)[0] as Name; - const client = arg[clientName]; - - // If mocking is not enabled, return the real client - if (process.env.NEXT_PUBLIC_END2END_MOCKING !== 'enabled') { - return client; - } - - const asyncSnapshots = loadSnapshots(clientName); - - return async function (...args: Args) { - const snaphots = await asyncSnapshots; - const simplifyParams = await loadSimplifyParams(clientName); - const stub = snaphots.find((stub: any) => { - return ( - JSON.stringify(simplifyParams(...args)) === JSON.stringify(stub.args) - ); - }); - - if (!stub) { - try { - const response = await client(...args); - - if (process.env.NODE_ENV !== 'production') { - // The warning is displayed only if no error - // is thrown by the client (as we cannot stub error for now) - console.warn( - ` - E2E Client Stub Warning - ----------------------- - When calling client : ${clientName} - No snaphots found for args : - ${JSON.stringify(simplifyParams(...args), null, 2).replaceAll( - '\n', - '\n ' - )} - - Calling the real client instead. - ` - ); - } - return response; - } finally { - } - } - return stub['result'] as R; - }; -} - -async function loadSimplifyParams(clientName: string) { - try { - const simplifyParamsPath = await import( - `#clients/_test/${clientName}/simplify-params.ts` - ); - - if (!simplifyParamsPath.default) { - throw new Error( - `No default export found for 'simplify-params.ts' of ${clientName}` - ); - } - return simplifyParamsPath.default; - } catch (e) { - return (...args: any[]) => args; - } -} - -// Load all snapshots .json files in the folder /_test//_snapshots/*.json -// using require.context webpack feature -async function loadSnapshots(clientName: string) { - //@ts-ignore - const snapshotContext = require.context( - `#clients/_test/`, - true, - /\.json$/, - 'lazy' - ); - const snapshots = await Promise.all( - snapshotContext - .keys() - .filter((fileName: string) => - fileName.includes(`/${clientName}/_snapshots/`) - ) - .map((fileName: string) => { - const snapshot = snapshotContext(fileName); - return snapshot; - }) - ); - - if (snapshots.length === 0) { - console.warn( - ` - E2E Client Stub Warning - ----------------------- - No snapshots found for client ${clientName}. - Please create a snapshot file in clients/_test/${clientName}/_snapshots/ -` - ); - } - return snapshots; -} - -export { stubClientWithSnapshots as stubClient }; diff --git a/cypress/fixtures/api-inclusion-metadata.json b/cypress/fixtures/api-inclusion-metadata.json new file mode 100644 index 000000000..64327de2e --- /dev/null +++ b/cypress/fixtures/api-inclusion-metadata.json @@ -0,0 +1,57 @@ +{ + "count": 10, + "next": null, + "previous": null, + "results": [ + { + "id": "EI", + "name": "Entreprise d'insertion", + "parent": "Insertion" + }, + { + "id": "AI", + "name": "Association intermédiaire", + "parent": "Insertion" + }, + { + "id": "ACI", + "name": "Atelier chantier d'insertion", + "parent": "Insertion" + }, + { + "id": "ETTI", + "name": "Entreprise de travail temporaire d'insertion", + "parent": "Insertion" + }, + { + "id": "EITI", + "name": "Entreprise d'insertion par le travail indépendant", + "parent": "Insertion" + }, + { + "id": "GEIQ", + "name": "Groupement d'employeurs pour l'insertion et la qualification", + "parent": "Insertion" + }, + { + "id": "SEP", + "name": "Produits et services réalisés en prison", + "parent": "Insertion" + }, + { + "id": "EA", + "name": "Entreprise adaptée", + "parent": "Handicap" + }, + { + "id": "EATT", + "name": "Entreprise adaptée de travail temporaire", + "parent": "Handicap" + }, + { + "id": "ESAT", + "name": "Etablissement et service d'aide par le travail", + "parent": "Handicap" + } + ] +} diff --git a/cypress/mocks/handlers/api-inclusion.ts b/cypress/mocks/handlers/api-inclusion.ts index b8c41f131..9a1ee5a69 100644 --- a/cypress/mocks/handlers/api-inclusion.ts +++ b/cypress/mocks/handlers/api-inclusion.ts @@ -1,6 +1,13 @@ import { HttpResponse, HttpResponseResolver } from 'msw'; +import apiInclusionMetadata from '../../fixtures/api-inclusion-metadata.json'; import apiInclusion from '../../fixtures/api-inclusion.json'; export const apiInclusionHandler: HttpResponseResolver = ({ request }) => { return HttpResponse.json(apiInclusion); }; + +export const apiInclusionMetadataHandler: HttpResponseResolver = ({ + request, +}) => { + return HttpResponse.json(apiInclusionMetadata); +}; diff --git a/cypress/mocks/routes.ts b/cypress/mocks/routes.ts index 4a1b2d390..73f8531d7 100644 --- a/cypress/mocks/routes.ts +++ b/cypress/mocks/routes.ts @@ -10,7 +10,10 @@ import { apiGeoEpcisHandler, apiGeoRegionsHandler, } from './handlers/api-geo'; -import { apiInclusionHandler } from './handlers/api-inclusion'; +import { + apiInclusionHandler, + apiInclusionMetadataHandler, +} from './handlers/api-inclusion'; import { apiSireneInseeAuthHandler, apiSireneInseeSirenHandler, @@ -51,6 +54,10 @@ export const routesHandlers = [ routes.certifications.entrepriseInclusive.api.getBySiren('*'), apiInclusionHandler ), + http.get( + routes.certifications.entrepriseInclusive.api.metadata, + apiInclusionMetadataHandler + ), http.get(routes.datagouv.ess, apiDataGouvEssHandler), http.get(routes.tooling.monitoring.getBySlug('*'), upDownIoHandler), http.get(routes.apiDataSubvention.grants('*'), apiDataSubventionHandler),