From 8d3db3dc2a1a24bf9872839e45c5408473a2155a Mon Sep 17 00:00:00 2001 From: maymanaf Date: Mon, 13 Jan 2025 16:35:46 +0100 Subject: [PATCH] front: add stdcm simulation sheet e2e test Signed-off-by: maymanaf --- front/package-lock.json | 37 ++++ front/package.json | 2 + .../rollingStock/freightRollingStocks.ts | 7 +- front/tests/005-operational-studies.spec.ts | 2 +- front/tests/006-stdcm.spec.ts | 41 +--- .../tests/013-stdcm-simulation-sheet.spec.ts | 112 ++++++++++ front/tests/assets/project-const.ts | 10 +- .../rollingStock/dual-mode_rolling_stock.json | 2 +- .../improbable_rolling_stock.json | 2 +- front/tests/assets/simulation-sheet-const.ts | 105 ++++++++++ .../assets/trainSchedule/train_schedules.json | 36 ++-- front/tests/pages/stdcm-page-model.ts | 2 +- front/tests/utils/date.ts | 39 ++++ front/tests/utils/setup-utils.ts | 11 +- front/tests/utils/simulationSheet.ts | 195 ++++++++++++++++++ 15 files changed, 524 insertions(+), 79 deletions(-) create mode 100644 front/tests/013-stdcm-simulation-sheet.spec.ts create mode 100644 front/tests/assets/simulation-sheet-const.ts create mode 100644 front/tests/utils/date.ts create mode 100644 front/tests/utils/simulationSheet.ts diff --git a/front/package-lock.json b/front/package-lock.json index 7e10ed3d8c0..51c9e66a0e6 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.8", "react": "^18.2.0", @@ -117,6 +118,7 @@ "@types/jest": "^29.5.14", "@types/lodash": "^4.17.14", "@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", @@ -4554,6 +4556,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", @@ -14807,6 +14816,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", @@ -15884,6 +15899,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 a42d371e6be..0cf3923b596 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.8", "react": "^18.2.0", @@ -112,6 +113,7 @@ "@types/jest": "^29.5.14", "@types/lodash": "^4.17.14", "@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/src/assets/rollingStock/freightRollingStocks.ts b/front/src/assets/rollingStock/freightRollingStocks.ts index 2e2b1e31be3..a6850a4c388 100644 --- a/front/src/assets/rollingStock/freightRollingStocks.ts +++ b/front/src/assets/rollingStock/freightRollingStocks.ts @@ -107,9 +107,9 @@ export const FREIGHT_ROLLING_STOCKS = [ 'Y8000AG', 'Y8000AP', 'Y9000US', - 'electric_rolling_stock_test_e2e', - // TODO Find a better solution for injecting test data. - 'FAST_ROLLING_STOCK_TEST_E2E', + // TODO: Find a better solution for injecting e2e test data. + 'ELECTRIC_RS_E2E', + 'FAST_RS_E2E', ]; export const ALLOWED_TOWED_ROLLING_STOCKS = [ @@ -125,5 +125,6 @@ export const ALLOWED_TOWED_ROLLING_STOCKS = [ 'ME200SER', 'MEPA', 'TRAINLOC', + // TODO: Find a better solution for injecting e2e test data. 'TOWED-TEST-E2E', ]; diff --git a/front/tests/005-operational-studies.spec.ts b/front/tests/005-operational-studies.spec.ts index dfe182cd60a..358c32c4de5 100644 --- a/front/tests/005-operational-studies.spec.ts +++ b/front/tests/005-operational-studies.spec.ts @@ -69,7 +69,7 @@ test.describe('Verify simulation configuration in operational studies', () => { await expect(rollingstockPage.rollingStockSelectorModal).toBeVisible(); // Test rolling stock search with normalization (spaces and capital letters) - await rollingstockPage.searchRollingstock(' electric_rolling_stock_test_E2E '); + await rollingstockPage.searchRollingstock(' electric__Rs_E2e '); // Select the rolling stock card based on the test ID const rollingstockCard = rollingstockPage.getRollingstockCardByTestID( diff --git a/front/tests/006-stdcm.spec.ts b/front/tests/006-stdcm.spec.ts index a1f60dff63e..900b25ba71e 100644 --- a/front/tests/006-stdcm.spec.ts +++ b/front/tests/006-stdcm.spec.ts @@ -12,7 +12,7 @@ test.use({ slowMo: 500, // Give the interface time to update between actions }, }); -test.describe('Verify train schedule elements and filters', () => { +test.describe('Verify stdcm simulation page', () => { test.slow(); // Mark test as slow due to multiple steps test.use({ viewport: { width: 1920, height: 1080 } }); @@ -55,7 +55,7 @@ test.describe('Verify train schedule elements and filters', () => { await homePage.goToHomePage(); OSRDLanguage = await homePage.getOSRDLanguage(); await page.goto('/stdcm'); - await page.waitForLoadState('load', { timeout: 30 * 1000 }); + await page.waitForLoadState('domcontentloaded', { timeout: 30_000 }); await homePage.removeViteOverlay(); // Wait for infra to be in 'CACHED' state before proceeding @@ -99,43 +99,6 @@ test.describe('Verify train schedule elements and filters', () => { }); /** *************** Test 3 **************** */ - test('Verify STDCM stops and simulation sheet', async ({ page, browserName }) => { - // Populate STDCM page with origin, destination, and via details - const stdcmPage = new STDCMPage(page); - await stdcmPage.fillAndVerifyConsistDetails( - consistDetails, - tractionEnginePrefilledValues.tonnage, - tractionEnginePrefilledValues.length, - tractionEnginePrefilledValues.maxSpeed - ); - await stdcmPage.fillOriginDetailsLight(); - await stdcmPage.fillDestinationDetailsLight(); - await stdcmPage.fillAndVerifyViaDetails({ - viaNumber: 1, - ciSearchText: 'mid_west', - }); - // Verify input map markers in Chromium - if (browserName === 'chromium') { - await stdcmPage.mapMarkerVisibility(); - } - // Launch simulation and verify output data matches expected results - await stdcmPage.launchSimulation(); - // Verify map results markers in Chromium - if (browserName === 'chromium') { - await stdcmPage.mapMarkerResultVisibility(); - } - await stdcmPage.verifyTableData('./tests/assets/stdcm/stdcmWithoutAllVia.json'); - await stdcmPage.displayAllOperationalPoints(); - await stdcmPage.verifyTableData('./tests/assets/stdcm/stdcmWithAllVia.json'); - await stdcmPage.retainSimulation(); - await stdcmPage.downloadSimulation(browserName); - // Reset and verify empty fields - await stdcmPage.startNewQuery(); - // TODO: Uncomment the check when the bug #9533 is fixed - // await stdcmPage.verifyAllFieldsEmpty(); - }); - - /** *************** Test 4 **************** */ test('Launch simulation with and without capacity for towed rolling stock', async ({ page }) => { const towedConsistDetails: ConsistFields = { tractionEngine: fastRollingStockName, 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..e5619216767 --- /dev/null +++ b/front/tests/013-stdcm-simulation-sheet.spec.ts @@ -0,0 +1,112 @@ +import fs from 'fs'; + +import pdfParse from 'pdf-parse'; + +import type { Infra } from 'common/api/osrdEditoastApi'; + +import { electricRollingStockName } from './assets/project-const'; +import simulationSheetDetails from './assets/simulation-sheet-const'; +import HomePage from './pages/home-page-model'; +import STDCMPage, { type ConsistFields } from './pages/stdcm-page-model'; +import test from './test-logger'; +import { waitForInfraStateToBeCached } from './utils'; +import { getInfra } from './utils/api-setup'; +import { findFirstPdf, verifySimulationContent, type Simulation } from './utils/simulationSheet'; + +test.use({ + launchOptions: { + slowMo: 500, // Give the interface time to update between actions + }, +}); +test.describe('Verify stdcm simulation page', () => { + test.describe.configure({ mode: 'serial' }); // Configure this block to run serially + test.slow(); // Mark test as slow due to multiple steps + test.use({ viewport: { width: 1920, height: 1080 } }); + + let infra: Infra; + let OSRDLanguage: string; + const consistDetails: ConsistFields = { + tractionEngine: electricRollingStockName, + tonnage: '950', + length: '567', + maxSpeed: '180', + speedLimitTag: 'HLP', + }; + const tractionEnginePrefilledValues = { + tonnage: '900', + length: '400', + maxSpeed: '288', + }; + + test.beforeAll('Fetch infrastructure', async () => { + infra = await getInfra(); + }); + + test.beforeEach('Navigate to the STDCM page', async ({ page }) => { + // Retrieve OSRD language and navigate to STDCM page + const homePage = new HomePage(page); + await homePage.goToHomePage(); + OSRDLanguage = await homePage.getOSRDLanguage(); + await page.goto('/stdcm'); + await page.waitForLoadState('domcontentloaded', { timeout: 30_000 }); + await homePage.removeViteOverlay(); + + // Wait for infra to be in 'CACHED' state before proceeding + await waitForInfraStateToBeCached(infra.id); + }); + + /** *************** Test 1 **************** */ + test('Verify STDCM stops and simulation sheet', async ({ page, browserName, context }) => { + // Populate STDCM page with origin, destination, and via details + const stdcmPage = new STDCMPage(page); + await stdcmPage.fillAndVerifyConsistDetails( + consistDetails, + tractionEnginePrefilledValues.tonnage, + tractionEnginePrefilledValues.length, + tractionEnginePrefilledValues.maxSpeed + ); + await stdcmPage.fillOriginDetailsLight(); + await stdcmPage.fillDestinationDetailsLight(); + await stdcmPage.fillAndVerifyViaDetails({ + viaNumber: 1, + ciSearchText: 'mid_west', + }); + // Verify input map markers in Chromium + if (browserName === 'chromium') { + await stdcmPage.mapMarkerVisibility(); + } + // Launch simulation and verify output data matches expected results + await stdcmPage.launchSimulation(); + // Verify map results markers in Chromium + if (browserName === 'chromium') { + await stdcmPage.mapMarkerResultVisibility(); + } + await stdcmPage.verifyTableData('./tests/assets/stdcm/stdcmWithoutAllVia.json'); + await stdcmPage.displayAllOperationalPoints(); + await stdcmPage.verifyTableData('./tests/assets/stdcm/stdcmWithAllVia.json'); + await stdcmPage.retainSimulation(); + await stdcmPage.downloadSimulation(browserName); + // Reset and verify empty fields + const [newPage] = await Promise.all([context.waitForEvent('page'), stdcmPage.startNewQuery()]); + await newPage.waitForLoadState(); + // TODO: Uncomment the check when the bug #10335 is fixed + // const newStdcmPage = new STDCMPage(newPage); + // await newStdcmPage.verifyAllFieldsEmpty(); + }); + + /** *************** Test 2 **************** */ + test('Verify simulation sheet content', async ({ browserName }) => { + 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); + const pdfText = pdfData.text; + const expectedSimulation: Simulation = simulationSheetDetails(OSRDLanguage); + verifySimulationContent(pdfText, expectedSimulation); + }); +}); diff --git a/front/tests/assets/project-const.ts b/front/tests/assets/project-const.ts index 1728021a4aa..460bbe40e1b 100644 --- a/front/tests/assets/project-const.ts +++ b/front/tests/assets/project-const.ts @@ -1,8 +1,8 @@ -export const electricRollingStockName = 'electric_rolling_stock_test_e2e'; -export const dualModeRollingStockName = 'dual-mode_rolling_stock_test_e2e'; -export const slowRollingStockName = 'slow_rolling_stock_test_e2e'; -export const fastRollingStockName = 'FAST_ROLLING_STOCK_TEST_E2E'; -export const improbableRollingStockName = 'improbable_rolling_stock_test_e2e'; +export const electricRollingStockName = 'ELECTRIC_RS_E2E'; +export const dualModeRollingStockName = 'DUAL-MODE_RS_E2E'; +export const slowRollingStockName = 'SLOW_RS_E2E'; +export const fastRollingStockName = 'FAST_RS_E2E'; +export const improbableRollingStockName = 'IMPROBABLE_RS_E2E'; export const infrastructureName = 'small_infra_test_e2e'; export const trainScheduleProjectName = 'TS_project_test_e2e'; export const globalProjectName = 'project_test_e2e'; diff --git a/front/tests/assets/rollingStock/dual-mode_rolling_stock.json b/front/tests/assets/rollingStock/dual-mode_rolling_stock.json index b1ae9ac0940..ddfe131dbe8 100644 --- a/front/tests/assets/rollingStock/dual-mode_rolling_stock.json +++ b/front/tests/assets/rollingStock/dual-mode_rolling_stock.json @@ -1,6 +1,6 @@ { "railjson_version": "3.2", - "name": "dual-mode_rolling_stock_test_e2e", + "name": "DUAL-MODE_RS_E2Ee", "effort_curves": { "modes": { "25000V": { diff --git a/front/tests/assets/rollingStock/improbable_rolling_stock.json b/front/tests/assets/rollingStock/improbable_rolling_stock.json index 330ed578da7..05148665723 100644 --- a/front/tests/assets/rollingStock/improbable_rolling_stock.json +++ b/front/tests/assets/rollingStock/improbable_rolling_stock.json @@ -1,7 +1,7 @@ { "id": 789, "railjson_version": "3.2", - "name": "improbable_rolling_stock_test_e2e", + "name": "IMPROBABLE_RS_E2E", "effort_curves": { "modes": { "1500V": { diff --git a/front/tests/assets/simulation-sheet-const.ts b/front/tests/assets/simulation-sheet-const.ts new file mode 100644 index 00000000000..213cfee176c --- /dev/null +++ b/front/tests/assets/simulation-sheet-const.ts @@ -0,0 +1,105 @@ +import enTranslations from '../../public/locales/en/stdcm-simulation-report-sheet.json'; +import frTranslations from '../../public/locales/fr/stdcm-simulation-report-sheet.json'; +import { getLocalizedDateString } from '../utils/date'; +import type { Simulation } from '../utils/simulationSheet'; + +const simulationSheetDetails = (selectedLanguage: string): Simulation => { + const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + console.info(selectedLanguage); + return { + header: { + toolDescription: translations.warningMessage, + documentTitle: translations.stdcm, + }, + applicationDate: translations.applicationDate, + + applicationDateValue: getLocalizedDateString('2024-10-17', selectedLanguage), + + trainDetails: { + compositionCode: translations.speedLimitByTag, + compositionCodeValue: 'HLP', + towedMaterial: translations.towedMaterial, + towedMaterialValue: '-', + maxSpeed: translations.maxSpeed, + maxSpeedValue: '180 km/h', + maxTonnage: translations.maxWeight, + maxTonnageValue: '950 t', + referenceEngine: translations.referenceEngine, + referenceEngineValue: 'ELECTRIC_RS_E2E', + maxLength: translations.maxLength, + maxLengthValue: '567 m', + }, + requestedRoute: { + station1: { + name: '1North_West_station', + ch: 'BV', + plusTolerance: '+60', + minusTolerance: '-15', + departureTime: '20:21', + reason: translations.serviceStop, + }, + station2: { + name: '2Mid_West_station', + ch: 'BV', + reason: translations.passageStop, + }, + station3: { + name: '3South_station', + ch: 'BV', + arrivalTime: translations.asap, + reason: translations.serviceStop, + }, + }, + simulationDetails: { + totalDistance: '51 km', + simulationRoute: { + station1: { + name: '1North_West_station', + ch: 'BV', + track: 'A', + departureTime: '20:21', + tonnage: '950 t', + length: '567 m', + stopType: translations.serviceStop, + }, + station2: { + name: '2Mid_West_station', + ch: 'BV', + track: 'V1', + passageTime: '20:41', + tonnage: '=', + length: '=', + stopType: translations.passageStop, + }, + station3: { + name: '3Mid_East_station', + ch: 'BV', + track: 'V1', + passageTime: '20:46', + tonnage: '=', + length: '=', + }, + station4: { + name: '4North_station', + ch: 'BV', + track: 'V1bis', + passageTime: '20:53', + tonnage: '=', + length: '=', + }, + station5: { + name: '5South_station', + ch: 'BV', + track: 'V1', + arrivalTime: '20:57', + tonnage: '=', + length: '=', + stopType: translations.serviceStop, + }, + }, + disclaimer: translations.withoutWarranty, + }, + }; +}; + +export default simulationSheetDetails; diff --git a/front/tests/assets/trainSchedule/train_schedules.json b/front/tests/assets/trainSchedule/train_schedules.json index cd9ad74d79f..9fe67d9ac01 100644 --- a/front/tests/assets/trainSchedule/train_schedules.json +++ b/front/tests/assets/trainSchedule/train_schedules.json @@ -2,7 +2,7 @@ { "train_name": "Train 1", "labels": ["Tag-1", "SS-NS"], - "rolling_stock_name": "slow_rolling_stock_test_e2e", + "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T03:00:00Z", "path": [ { "id": "id227", "deleted": false, "uic": 6, "secondary_code": "BV" }, @@ -28,7 +28,7 @@ { "train_name": "Train 2", "labels": ["Tag-2", "WS-SES"], - "rolling_stock_name": "electric_rolling_stock_test_e2e", + "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T03:15:00Z", "path": [ { "id": "id477", "deleted": false, "uic": 2, "secondary_code": "BV" }, @@ -63,7 +63,7 @@ { "train_name": "Train 3", "labels": ["Tag-3", "MWS-NES"], - "rolling_stock_name": "dual-mode_rolling_stock_test_e2e", + "rolling_stock_name": "DUAL-MODE_RS_E2E", "start_time": "2024-10-17T05:00:00Z", "path": [ { "id": "id744", "deleted": false, "uic": 3, "secondary_code": "BV" }, @@ -97,7 +97,7 @@ { "train_name": "Train 4", "labels": ["Tag-4", "WS-SES"], - "rolling_stock_name": "electric_rolling_stock_test_e2e", + "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T05:15:00Z", "path": [ { "id": "id938", "deleted": false, "uic": 2, "secondary_code": "BV" }, @@ -140,7 +140,7 @@ { "train_name": "Train 5", "labels": ["Tag-5", "SWE-MES"], - "rolling_stock_name": "slow_rolling_stock_test_e2e", + "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T07:30:00Z", "path": [ { "id": "id1306", "deleted": false, "uic": 1, "secondary_code": "BV" }, @@ -174,7 +174,7 @@ { "train_name": "Train 6", "labels": ["Tag-6", "WS-NS"], - "rolling_stock_name": "dual-mode_rolling_stock_test_e2e", + "rolling_stock_name": "DUAL-MODE_RS_E2E", "start_time": "2024-10-17T07:45:00Z", "path": [ { "id": "id1700", "deleted": false, "uic": 2, "secondary_code": "BV" }, @@ -208,7 +208,7 @@ { "train_name": "Train 7", "labels": ["Tag-7", "WS-NES"], - "rolling_stock_name": "FAST_ROLLING_STOCK_TEST_E2E", + "rolling_stock_name": "FAST_RS_E2E", "start_time": "2024-10-17T08:37:00Z", "path": [ { "id": "id2121", "deleted": false, "uic": 2, "secondary_code": "BV" }, @@ -235,7 +235,7 @@ { "train_name": "Train 8", "labels": ["Tag-8", "SS-MES"], - "rolling_stock_name": "slow_rolling_stock_test_e2e", + "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T08:56:01Z", "path": [ { "id": "id2532", "deleted": false, "uic": 6, "secondary_code": "BV" }, @@ -269,7 +269,7 @@ { "train_name": "Train 9", "labels": ["Tag-9", "MWS-NES"], - "rolling_stock_name": "electric_rolling_stock_test_e2e", + "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T09:35:59Z", "path": [ { "id": "id2854", "deleted": false, "uic": 3, "secondary_code": "BV" }, @@ -295,7 +295,7 @@ { "train_name": "Train 10", "labels": ["Tag-10", "SES-MES"], - "rolling_stock_name": "dual-mode_rolling_stock_test_e2e", + "rolling_stock_name": "DUAL-MODE_RS_E2E", "start_time": "2024-10-17T09:45:31Z", "path": [ { "id": "id3088", "deleted": false, "uic": 8, "secondary_code": "BV" }, @@ -377,7 +377,7 @@ { "train_name": "Train 13", "labels": ["Tag-13", "SES-SWS"], - "rolling_stock_name": "slow_rolling_stock_test_e2e", + "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T10:48:00Z", "path": [ { "id": "id354", "deleted": false, "uic": 8, "secondary_code": "BV" }, @@ -411,7 +411,7 @@ { "train_name": "Train 14", "labels": ["Tag-14", "NES-MES"], - "rolling_stock_name": "electric_rolling_stock_test_e2e", + "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T12:10:00Z", "path": [ { "id": "id803", "deleted": false, "uic": 7, "secondary_code": "BV" }, @@ -437,7 +437,7 @@ { "train_name": "Train 15", "labels": ["Tag-15"], - "rolling_stock_name": "improbable_rolling_stock_test_e2e", + "rolling_stock_name": "IMPROBABLE_RS_E2E", "start_time": "2024-10-17T12:30:00Z", "path": [ { "id": "id1073", "deleted": false, "track": "TA6", "offset": 6609000 }, @@ -464,7 +464,7 @@ { "train_name": "Train 16", "labels": ["Tag-16", "SES-WS"], - "rolling_stock_name": "dual-mode_rolling_stock_test_e2e", + "rolling_stock_name": "DUAL-MODE_RS_E2E", "start_time": "2024-10-17T13:00:00Z", "path": [ { "id": "id1353", "deleted": false, "uic": 8, "secondary_code": "BV" }, @@ -498,7 +498,7 @@ { "train_name": "Train 17", "labels": ["Tag-17", "WS-SES"], - "rolling_stock_name": "electric_rolling_stock_test_e2e", + "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T14:05:00Z", "path": [ { "id": "id1572", "deleted": false, "uic": 2, "secondary_code": "BV" }, @@ -540,7 +540,7 @@ { "train_name": "Train 18", "labels": ["Tag-18", "SWS-MWS"], - "rolling_stock_name": "slow_rolling_stock_test_e2e", + "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T14:15:08Z", "path": [ { "id": "id1948", "deleted": false, "uic": 1, "secondary_code": "BV" }, @@ -592,7 +592,7 @@ { "train_name": "Train 20", "labels": ["Tag-20", "SS-WS"], - "rolling_stock_name": "electric_rolling_stock_test_e2e", + "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T16:00:00Z", "path": [ { "id": "id2078", "deleted": false, "uic": 6, "secondary_code": "BV" }, @@ -634,7 +634,7 @@ { "train_name": "Train 21", "labels": ["Tag-21", "NWS-NES"], - "rolling_stock_name": "electric_rolling_stock_test_e2e", + "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T19:05:00Z", "path": [ { "id": "id2458", "deleted": false, "track": "TG4", "offset": 1549000 }, diff --git a/front/tests/pages/stdcm-page-model.ts b/front/tests/pages/stdcm-page-model.ts index efd045aa3ae..46a76d52432 100644 --- a/front/tests/pages/stdcm-page-model.ts +++ b/front/tests/pages/stdcm-page-model.ts @@ -699,7 +699,7 @@ class STDCMPage { } async startNewQuery() { - await this.startNewQueryButton.dispatchEvent('click'); + await this.startNewQueryButton.click(); } async mapMarkerVisibility() { diff --git a/front/tests/utils/date.ts b/front/tests/utils/date.ts new file mode 100644 index 00000000000..b65de99e0b8 --- /dev/null +++ b/front/tests/utils/date.ts @@ -0,0 +1,39 @@ +import dayjs from 'dayjs'; +import timezone from 'dayjs/plugin/timezone'; +import utc from 'dayjs/plugin/utc'; + +dayjs.extend(utc); +dayjs.extend(timezone); + +// Constants for supported languages +const SUPPORTED_LANGUAGES: Record = { + English: 'en', + Français: 'fr', +}; + +/** + * Get a localized date string formatted according to the specified language. + * + * @param dateString - The date string to format (ISO format recommended) + * @param language - The language for localization (e.g., "English", "French") + * @returns A formatted date string + */ +export function getLocalizedDateString(dateString: string, language: string): string { + const locale = SUPPORTED_LANGUAGES[language] ?? 'en'; + const date = new Date(dateString); + return new Intl.DateTimeFormat(locale, { + weekday: 'long', + day: 'numeric', + month: 'long', + year: 'numeric', + }).format(date); +} + +/** + * Create a Day.js object in a specific timezone. + * + * @param dateString - The date string in ISO format + * @param timeZone - The timezone (e.g., "Europe/Paris") + */ +export const createDateInSpecialTimeZone = (dateString: string, timeZone: string) => + dayjs.tz(dateString, timeZone); diff --git a/front/tests/utils/setup-utils.ts b/front/tests/utils/setup-utils.ts index c67db28dcbc..7cf7b538b0b 100644 --- a/front/tests/utils/setup-utils.ts +++ b/front/tests/utils/setup-utils.ts @@ -1,7 +1,3 @@ -import dayjs from 'dayjs'; -import timezone from 'dayjs/plugin/timezone'; -import utc from 'dayjs/plugin/utc'; - import type { Infra, PostInfraRailjsonApiResponse, @@ -33,9 +29,7 @@ import { trainScheduleStudyName, } from '../assets/project-const'; import { logger } from '../test-logger'; - -dayjs.extend(utc); -dayjs.extend(timezone); +import { createDateInSpecialTimeZone } from './date'; /** * Helper function to create infrastructure using RailJson. @@ -145,9 +139,6 @@ export async function createStudy(projectId: number, studyName = globalStudyName return study; } -const createDateInSpecialTimeZone = (dateString: string, timeZone: string) => - dayjs.tz(dateString, timeZone); - /** * Main function to create all necessary test data including infrastructure, rolling stocks, * project, study, and scenario. diff --git a/front/tests/utils/simulationSheet.ts b/front/tests/utils/simulationSheet.ts new file mode 100644 index 00000000000..fae296fd3ab --- /dev/null +++ b/front/tests/utils/simulationSheet.ts @@ -0,0 +1,195 @@ +import fs from 'fs'; +import path from 'path'; + +import { expect } from '@playwright/test'; + +export interface Simulation { + header: { + toolDescription: string; + documentTitle: string; + }; + applicationDate: string; + applicationDateValue: string; + trainDetails: { + compositionCode: string; + compositionCodeValue: string; + towedMaterial: string; + towedMaterialValue: string; + maxSpeed: string; + maxSpeedValue: string; + maxTonnage: string; + maxTonnageValue: string; + referenceEngine: string; + referenceEngineValue: string; + maxLength: string; + maxLengthValue: string; + }; + requestedRoute: { + station1: { + name: string; + ch: string; + arrivalTime?: string | null; + plusTolerance?: string | null; + minusTolerance?: string | null; + stop?: string | null; + departureTime?: string | null; + reason: string; + }; + station2: { + name: string; + ch: string; + arrivalTime?: string | null; + plusTolerance?: string | null; + minusTolerance?: string | null; + stop?: string | null; + departureTime?: string | null; + reason: string; + }; + station3: { + name: string; + ch: string; + arrivalTime?: string | null; + plusTolerance?: string | null; + minusTolerance?: string | null; + stop?: string | null; + departureTime?: string | null; + reason: string; + }; + }; + simulationDetails: { + totalDistance: string; + simulationRoute: { + station1: { + name: string; + ch: string; + track: string; + arrivalTime?: string | null; + passageTime?: string | null; + departureTime?: string | null; + tonnage: string; + length: string; + referenceEngine?: string | null; + stopType?: string | null; + }; + station2: { + name: string; + ch: string; + track: string; + arrivalTime?: string | null; + passageTime?: string | null; + departureTime?: string | null; + tonnage: string; + length: string; + referenceEngine?: string | null; + stopType?: string | null; + }; + station3: { + name: string; + ch: string; + track: string; + arrivalTime?: string | null; + passageTime?: string | null; + departureTime?: string | null; + tonnage: string; + length: string; + referenceEngine?: string | null; + stopType?: string | null; + }; + station4: { + name: string; + ch: string; + track: string; + arrivalTime?: string | null; + passageTime?: string | null; + departureTime?: string | null; + tonnage: string; + length: string; + referenceEngine?: string | null; + stopType?: string | null; + }; + station5: { + name: string; + ch: string; + track: string; + arrivalTime?: string | null; + passageTime?: string | null; + departureTime?: string | null; + tonnage: string; + length: string; + referenceEngine?: string | null; + stopType?: string | null; + }; + }; + disclaimer: string; + }; +} + +/** + * Finds the first PDF file in a directory. + * @param directory - The directory to search in. + * @returns The absolute path to the PDF file, or `null` if no PDF file is found. + */ +export function findFirstPdf(directory: string): string | null { + try { + const pdfFile = fs.readdirSync(directory).find((file) => file.endsWith('.pdf')); + return pdfFile ? path.resolve(directory, pdfFile) : null; + } catch { + 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 `true` if the content matches the expected simulation, otherwise `false`. + */ +export function verifySimulationContent(pdfText: string, expectedSimulation: Simulation) { + const textChecks = [ + expectedSimulation.header.toolDescription, + expectedSimulation.header.documentTitle, + expectedSimulation.applicationDate, + expectedSimulation.applicationDateValue, + expectedSimulation.trainDetails.compositionCode, + expectedSimulation.trainDetails.compositionCodeValue, + expectedSimulation.trainDetails.towedMaterial, + expectedSimulation.trainDetails.towedMaterialValue, + expectedSimulation.trainDetails.maxSpeed, + expectedSimulation.trainDetails.maxSpeedValue, + expectedSimulation.trainDetails.maxTonnage, + expectedSimulation.trainDetails.maxTonnageValue, + expectedSimulation.trainDetails.referenceEngine, + expectedSimulation.trainDetails.referenceEngineValue, + expectedSimulation.trainDetails.maxLength, + expectedSimulation.trainDetails.maxLengthValue, + expectedSimulation.simulationDetails.totalDistance, + ...Object.values(expectedSimulation.requestedRoute).flatMap((route) => + [ + route.name, + route.ch, + route.arrivalTime, + route.plusTolerance, + route.minusTolerance, + route.stop, + route.departureTime, + route.reason, + ].filter(Boolean) + ), + ...Object.values(expectedSimulation.simulationDetails.simulationRoute).flatMap((route) => + [ + route.name, + route.ch, + route.track, + route.arrivalTime, + route.passageTime, + route.departureTime, + route.tonnage, + route.length, + route.referenceEngine, + route.stopType, + ].filter(Boolean) + ), + expectedSimulation.simulationDetails.disclaimer, + ]; + textChecks.forEach((check) => expect(pdfText).toContain(check)); +}