From 0471e4036dc23e3b1cc7746bd0e25b7b79c68f31 Mon Sep 17 00:00:00 2001 From: maymanaf Date: Tue, 24 Dec 2024 14:32:21 +0100 Subject: [PATCH] front: add stdcm simulation sheet e2e test Signed-off-by: maymanaf --- front/package-lock.json | 37 +++++++ front/package.json | 2 + .../tests/013-stdcm-simulation-sheet.spec.ts | 47 ++++++++ front/tests/assets/simulationSheet-const.ts | 104 ++++++++++++++++++ front/tests/utils/simulationSheet.ts | 94 ++++++++++++++++ 5 files changed, 284 insertions(+) create mode 100644 front/tests/013-stdcm-simulation-sheet.spec.ts create mode 100644 front/tests/assets/simulationSheet-const.ts create mode 100644 front/tests/utils/simulationSheet.ts diff --git a/front/package-lock.json b/front/package-lock.json index e9e8071400c..0ed9a1dd133 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -74,6 +74,7 @@ "maplibre-gl": "^4.0.0", "openapi-typescript-codegen": "^0.29.0", "party-js": "^2.2.0", + "pdf-parse": "^1.1.1", "prop-types": "^15.8.1", "rc-slider": "^11.1.6", "react": "^18.2.0", @@ -117,6 +118,7 @@ "@types/jest": "^29.5.14", "@types/lodash": "^4.17.13", "@types/node": "^22", + "@types/pdf-parse": "^1.1.4", "@types/react": "^18.2.53", "@types/react-beautiful-dnd": "^13.1.8", "@types/react-dom": "^18.2.18", @@ -4497,6 +4499,13 @@ "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", "license": "MIT" }, + "node_modules/@types/pdf-parse": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.4.tgz", + "integrity": "sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/picomatch": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-3.0.1.tgz", @@ -15164,6 +15173,12 @@ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "optional": true }, + "node_modules/node-ensure": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==", + "license": "MIT" + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -16224,6 +16239,28 @@ "node": ">=0.12" } }, + "node_modules/pdf-parse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz", + "integrity": "sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==", + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "node-ensure": "^0.0.0" + }, + "engines": { + "node": ">=6.8.1" + } + }, + "node_modules/pdf-parse/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", diff --git a/front/package.json b/front/package.json index 83b034f9e0e..46ea0a39512 100644 --- a/front/package.json +++ b/front/package.json @@ -69,6 +69,7 @@ "maplibre-gl": "^4.0.0", "openapi-typescript-codegen": "^0.29.0", "party-js": "^2.2.0", + "pdf-parse": "^1.1.1", "prop-types": "^15.8.1", "rc-slider": "^11.1.6", "react": "^18.2.0", @@ -112,6 +113,7 @@ "@types/jest": "^29.5.14", "@types/lodash": "^4.17.13", "@types/node": "^22", + "@types/pdf-parse": "^1.1.4", "@types/react": "^18.2.53", "@types/react-beautiful-dnd": "^13.1.8", "@types/react-dom": "^18.2.18", diff --git a/front/tests/013-stdcm-simulation-sheet.spec.ts b/front/tests/013-stdcm-simulation-sheet.spec.ts new file mode 100644 index 00000000000..888c32fc1b4 --- /dev/null +++ b/front/tests/013-stdcm-simulation-sheet.spec.ts @@ -0,0 +1,47 @@ +import fs from 'fs'; + +import pdfParse from 'pdf-parse'; + +import simulationSheetDetails from './assets/simulationSheet-const'; +import HomePage from './pages/home-page-model'; +import test, { logger } from './test-logger'; +import { findFirstPdf, verifySimulationContent, type Simulation } from './utils/simulationSheet'; + +let OSRDLanguage: string; + +test.beforeEach( + 'Navigate to Times and Stops tab with rolling stock and route set', + async ({ page }) => { + const homePage = new HomePage(page); + await homePage.goToHomePage(); + OSRDLanguage = await homePage.getOSRDLanguage(); + } +); + +test('Verify PDF content', async ({ browserName }) => { + /** + * Finds the first `.pdf` file in the specified directory. + * @param directory The directory to search in. + * @returns The path of the first `.pdf` file or `null` if not found. + */ + + const downloadDir = `./tests/stdcm-results/${browserName}`; + const pdfFilePath = findFirstPdf(downloadDir); + + if (!pdfFilePath) { + throw new Error(`No PDF files found in directory: ${downloadDir}`); + } + + // Read and parse the PDF + const pdfBuffer = fs.readFileSync(pdfFilePath); + const pdfData = await pdfParse(pdfBuffer); + + // Dynamically create the expected simulation based on language + const expectedSimulation: Simulation = simulationSheetDetails(OSRDLanguage); + + // Verify PDF content + const pdfText = pdfData.text; + const isCorrect = verifySimulationContent(pdfText, expectedSimulation); + + logger.info(isCorrect ? 'Simulation data is correct!' : 'Mismatch in simulation data.'); +}); diff --git a/front/tests/assets/simulationSheet-const.ts b/front/tests/assets/simulationSheet-const.ts new file mode 100644 index 00000000000..7eb12b8eb98 --- /dev/null +++ b/front/tests/assets/simulationSheet-const.ts @@ -0,0 +1,104 @@ +import enTranslations from '../../public/locales/en/stdcm-simulation-report-sheet.json'; +import frTranslations from '../../public/locales/fr/stdcm-simulation-report-sheet.json'; +import type { Simulation } from '../utils/simulationSheet'; + +const simulationSheetDetails = (selectedLanguage: string): Simulation => { + const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + + return { + header: { + toolDescription: translations.warningMessage, + documentTitle: translations.stdcm, + creationDetails: { + number: 'n°1224-352-455', + date: translations.formattedDate + .replace('{{day}}', '24') + .replace('{{month}}', '12') + .replace('{{year}}', '2024') + .replace('{{hours}}', '12') + .replace('{{minutes}}', '53'), + time: '12:53', + }, + }, + responsiblePerson: { + role: 'Super Fret', + name: 'Jane Smith', + phone: '01 23 45 67 89', + email: 'john.doe@example.com', + }, + applicationDate: translations.applicationDate, + referenceTrack: translations.referencePath.replace('XXXXXX', '1224'), + trainDetails: { + compositionCode: translations.speedLimitByTag, + towedMaterial: translations.towedMaterial, + maxSpeed: translations.maxSpeed.replace('{{value}}', '180 km/h'), + maxTonnage: translations.maxWeight.replace('{{value}}', '400 t'), + referenceEngine: translations.referenceEngine, + maxLength: translations.maxLength.replace('{{value}}', '300 m'), + }, + requestedRoute: [ + { + stopNumber: 1, + station: 'North_West_station', + type: 'BV', + arrivalTime: '20:21', + departureTime: '20:21', + reason: translations.serviceStop, + }, + { + stopNumber: 2, + station: 'Mid_West_station', + type: 'BV', + reason: translations.passageStop, + }, + { + stopNumber: 3, + station: 'South_station', + type: 'BV', + arrivalTime: translations.asap, + departureTime: translations.asap, + reason: translations.serviceStop, + }, + ], + simulationDetails: { + referenceTrackNumber: translations.referencePath.replace('XXXXXX', 'YYYYYY'), + viewSimulationLink: translations.viewSimulation, + totalDistance: '51 km', + simulationPath: [ + { + stopNumber: 1, + station: 'North_West_station', + type: 'BV', + arrivalTime: '20:21', + tonnage: translations.weight.replace('{{value}}', '400 t'), + }, + { + stopNumber: 2, + station: 'Mid_West_station', + type: 'BV', + departureTime: '20:41', + }, + { + stopNumber: 3, + station: 'Mid_East_station', + type: 'BV', + departureTime: '20:46', + }, + { + stopNumber: 4, + station: 'North_station', + type: 'BV', + departureTime: '20:52', + }, + { + stopNumber: 5, + station: 'South_station', + type: 'BV', + departureTime: '20:56', + }, + ], + disclaimer: translations.withoutWarranty, + }, + }; +}; +export default simulationSheetDetails; diff --git a/front/tests/utils/simulationSheet.ts b/front/tests/utils/simulationSheet.ts new file mode 100644 index 00000000000..800917d5997 --- /dev/null +++ b/front/tests/utils/simulationSheet.ts @@ -0,0 +1,94 @@ +import fs from 'fs'; +import path from 'path'; + +export interface Simulation { + header: { + toolDescription: string; + documentTitle: string; + creationDetails: { + number: string; + date: string; + time: string; + }; + }; + responsiblePerson: { + role: string; + name: string; + phone: string; + email: string; + }; + applicationDate: string; + referenceTrack: string; + trainDetails: { + compositionCode: string; + towedMaterial: string | null; + maxSpeed: string; + maxTonnage: string; + referenceEngine: string | null; + maxLength: string; + }; + requestedRoute: Array<{ + stopNumber: number; + station: string; + type: string; + arrivalTime?: string; + departureTime?: string; + reason?: string; + }>; + simulationDetails: { + referenceTrackNumber: string; + viewSimulationLink: string; + totalDistance: string; + simulationPath: Array<{ + stopNumber: number; + station: string; + type: string; + arrivalTime?: string; + passageTime?: string; + departureTime?: string; + tonnage?: string; + referenceEngine?: string | null; + signal?: string | null; + crossedTrain?: string | null; + }>; + disclaimer: string; + }; +} + +export function findFirstPdf(directory: string): string | null { + try { + const files = fs.readdirSync(directory); + const pdfFile = files.find((file) => file.endsWith('.pdf')); + return pdfFile ? path.resolve(directory, pdfFile) : null; + } catch (error) { + console.error(`Error reading directory ${directory}:`, error); + return null; + } +} + +/** + * Verifies the PDF content against the expected simulation. + * @param pdfText The text extracted from the PDF. + * @param expectedSimulation The expected simulation data. + * @returns Whether the content matches the expected simulation. + */ +export function verifySimulationContent(pdfText: string, expectedSimulation: Simulation): boolean { + const textChecks = [ + expectedSimulation.header.toolDescription, + expectedSimulation.header.documentTitle, + expectedSimulation.header.creationDetails.number, + expectedSimulation.header.creationDetails.date, + expectedSimulation.header.creationDetails.time, + expectedSimulation.applicationDate, + expectedSimulation.referenceTrack, + expectedSimulation.trainDetails.compositionCode, + expectedSimulation.trainDetails.maxSpeed, + expectedSimulation.trainDetails.maxTonnage, + expectedSimulation.trainDetails.maxLength, + ...expectedSimulation.requestedRoute.map((route) => route.station), + ...expectedSimulation.simulationDetails.simulationPath.map((p) => p.station), + expectedSimulation.simulationDetails.disclaimer, + ]; + + return textChecks.every((check) => pdfText.includes(check)); +}