From 360d763e40fec56c3d3774231ca64d66d1cd7aa2 Mon Sep 17 00:00:00 2001 From: maymanaf Date: Tue, 14 Jan 2025 20:34:05 +0100 Subject: [PATCH] front: add e2e test for stdcm linked train simulation Signed-off-by: maymanaf --- front/tests/014-stdcm-linked-train.spec.ts | 111 ++++++++++++++++++ front/tests/assets/linked-train-const.ts | 43 +++++++ .../linkedTrain/anteriorLinkedTrainTable.json | 32 +++++ .../posteriorLinkedTrainTable.json | 32 +++++ .../assets/trainSchedule/train_schedules.json | 42 +++---- .../pages/stdcm-linked-train-page-model.ts | 108 +++++++++++++++-- front/tests/pages/stdcm-page-model.ts | 75 ++++++++---- 7 files changed, 391 insertions(+), 52 deletions(-) create mode 100644 front/tests/014-stdcm-linked-train.spec.ts create mode 100644 front/tests/assets/linked-train-const.ts create mode 100644 front/tests/assets/stdcm/linkedTrain/anteriorLinkedTrainTable.json create mode 100644 front/tests/assets/stdcm/linkedTrain/posteriorLinkedTrainTable.json diff --git a/front/tests/014-stdcm-linked-train.spec.ts b/front/tests/014-stdcm-linked-train.spec.ts new file mode 100644 index 00000000000..6e78aab0b7b --- /dev/null +++ b/front/tests/014-stdcm-linked-train.spec.ts @@ -0,0 +1,111 @@ +import type { Infra, TowedRollingStock } from 'common/api/osrdEditoastApi'; + +import { fastRollingStockName } from './assets/project-const'; +import HomePage from './pages/home-page-model'; +import STDCMLinkedTrainPage from './pages/stdcm-linked-train-page-model'; +import STDCMPage, { type ConsistFields } from './pages/stdcm-page-model'; +import test from './test-logger'; +import { waitForInfraStateToBeCached } from './utils'; +import { getInfra, setTowedRollingStock } from './utils/api-setup'; + +test.use({ + launchOptions: { + slowMo: 500, // Give the interface time to update between actions + }, +}); +test.describe('Verify stdcm simulation page', () => { + test.slow(); // Mark test as slow due to multiple steps + + test.use({ viewport: { width: 1920, height: 1080 } }); + let homePage: HomePage; + let stdcmLinkedTrainPage: STDCMLinkedTrainPage; + let stdcmPage: STDCMPage; + let infra: Infra; + let createdTowedRollingStock: TowedRollingStock; + let towedConsistDetails: ConsistFields; + + const fastRollingStockPrefilledValues = { + tonnage: '190', + length: '46', + maxSpeed: '220', + }; + const towedRollingStockPrefilledValues = { + tonnage: '46', + length: '26', + maxSpeed: '180', + }; + + test.beforeAll('Fetch infrastructure', async () => { + infra = await getInfra(); + createdTowedRollingStock = await setTowedRollingStock(); + towedConsistDetails = { + tractionEngine: fastRollingStockName, + towedRollingStock: createdTowedRollingStock.name, + }; + }); + + test.beforeEach('Navigate to the STDCM page', async ({ page }) => { + [homePage, stdcmPage, stdcmLinkedTrainPage] = [ + new HomePage(page), + new STDCMPage(page), + new STDCMLinkedTrainPage(page), + ]; + // Navigate to STDCM page + 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 anterior linked train', async ({ browserName }) => { + await stdcmPage.fillAndVerifyConsistDetails( + towedConsistDetails, + fastRollingStockPrefilledValues.tonnage, + fastRollingStockPrefilledValues.length, + fastRollingStockPrefilledValues.maxSpeed, + towedRollingStockPrefilledValues.tonnage, + towedRollingStockPrefilledValues.length, + towedRollingStockPrefilledValues.maxSpeed + ); + await stdcmLinkedTrainPage.anteriorLinkedPathDetails(); + await stdcmPage.fillAndVerifyViaDetails({ + viaNumber: 1, + ciSearchText: 'nS', + }); + await stdcmPage.fillDestinationDetailsLight(); + await stdcmPage.launchSimulation(); + await stdcmPage.verifyTableData( + './tests/assets/stdcm/linkedTrain/anteriorLinkedTrainTable.json' + ); + await stdcmPage.retainSimulation(); + await stdcmPage.downloadSimulation(browserName, true); + }); + + /** *************** Test 2 **************** */ + test('Verify STDCM posterior linked train', async ({ browserName }) => { + await stdcmPage.fillAndVerifyConsistDetails( + towedConsistDetails, + fastRollingStockPrefilledValues.tonnage, + fastRollingStockPrefilledValues.length, + fastRollingStockPrefilledValues.maxSpeed, + towedRollingStockPrefilledValues.tonnage, + towedRollingStockPrefilledValues.length, + towedRollingStockPrefilledValues.maxSpeed + ); + await stdcmLinkedTrainPage.posteriorLinkedPathDetails(); + await stdcmPage.fillAndVerifyViaDetails({ + viaNumber: 1, + ciSearchText: 'mid_east', + }); + await stdcmPage.fillOriginDetailsLight('respectDestinationSchedule', true); + await stdcmPage.launchSimulation(); + await stdcmPage.verifyTableData( + './tests/assets/stdcm/linkedTrain/posteriorLinkedTrainTable.json' + ); + await stdcmPage.retainSimulation(); + await stdcmPage.downloadSimulation(browserName, true); + }); +}); diff --git a/front/tests/assets/linked-train-const.ts b/front/tests/assets/linked-train-const.ts new file mode 100644 index 00000000000..f3f66c1ddad --- /dev/null +++ b/front/tests/assets/linked-train-const.ts @@ -0,0 +1,43 @@ +const LINKED_TRAIN_DETAILS = { + anterior: { + trainName: 'Train10', + trainDate: '17/10/24', + trainDetails: [ + { + trainName: 'Train10', + segments: [ + ['17/10/24', '11:45', 'South_East_station', 'SES'], + ['17/10/24', '11:59', 'Mid_East_station', 'MES'], + ], + }, + ], + dynamicOriginCi: 'MES Mid_East_station', + dynamicOriginCh: 'BV', + originArrival: 'preciseTime', + dateOriginArrival: '17/10/24', + timeOriginArrival: '12:29', + toleranceOriginArrival: '-30/+30', + toleranceFields: { min: '-15', max: '+15', isAnterior: true }, + }, + posterior: { + trainName: 'TrAiN14', + trainDate: '17/10/24', + trainDetails: [ + { + trainName: 'Train14', + segments: [ + ['17/10/24', '14:10', 'North_East_station', 'NES'], + ['17/10/24', '14:17', 'Mid_East_station', 'MES'], + ], + }, + ], + dynamicDestinationCi: 'NES North_East_station', + dynamicDestinationCh: 'BV', + destinationArrival: 'preciseTime', + dateDestinationArrival: '17/10/24', + timeDestinationArrival: '13:40', + toleranceDestinationArrival: '-30/+30', + toleranceFields: { min: '-05', max: '+10', isAnterior: false }, + }, +}; +export default LINKED_TRAIN_DETAILS; diff --git a/front/tests/assets/stdcm/linkedTrain/anteriorLinkedTrainTable.json b/front/tests/assets/stdcm/linkedTrain/anteriorLinkedTrainTable.json new file mode 100644 index 00000000000..df5027cc413 --- /dev/null +++ b/front/tests/assets/stdcm/linkedTrain/anteriorLinkedTrainTable.json @@ -0,0 +1,32 @@ +[ + { + "index": 1, + "operationalPoint": "Mid_East_station", + "code": "BV", + "endStop": "", + "passageStop": "", + "startStop": "12:29", + "weight": "236t", + "refEngine": "" + }, + { + "index": 2, + "operationalPoint": "North_station", + "code": "BV", + "endStop": "13:18", + "passageStop": "4 min", + "startStop": "13:22", + "weight": "=", + "refEngine": "=" + }, + { + "index": 5, + "operationalPoint": "South_station", + "code": "BV", + "endStop": "13:43", + "passageStop": "", + "startStop": "", + "weight": "236t", + "refEngine": "" + } +] diff --git a/front/tests/assets/stdcm/linkedTrain/posteriorLinkedTrainTable.json b/front/tests/assets/stdcm/linkedTrain/posteriorLinkedTrainTable.json new file mode 100644 index 00000000000..89d680027dc --- /dev/null +++ b/front/tests/assets/stdcm/linkedTrain/posteriorLinkedTrainTable.json @@ -0,0 +1,32 @@ +[ + { + "index": 1, + "operationalPoint": "North_West_station", + "code": "BV", + "endStop": "", + "passageStop": "", + "startStop": "11:11", + "weight": "236t", + "refEngine": "" + }, + { + "index": 2, + "operationalPoint": "Mid_East_station", + "code": "BV", + "endStop": "12:38", + "passageStop": "3 min", + "startStop": "12:41", + "weight": "=", + "refEngine": "=" + }, + { + "index": 5, + "operationalPoint": "North_East_station", + "code": "BV", + "endStop": "13:40", + "passageStop": "", + "startStop": "", + "weight": "236t", + "refEngine": "" + } +] diff --git a/front/tests/assets/trainSchedule/train_schedules.json b/front/tests/assets/trainSchedule/train_schedules.json index 9fe67d9ac01..fc5b82aeea5 100644 --- a/front/tests/assets/trainSchedule/train_schedules.json +++ b/front/tests/assets/trainSchedule/train_schedules.json @@ -1,6 +1,6 @@ [ { - "train_name": "Train 1", + "train_name": "Train1", "labels": ["Tag-1", "SS-NS"], "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T03:00:00Z", @@ -26,7 +26,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 2", + "train_name": "Train2", "labels": ["Tag-2", "WS-SES"], "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T03:15:00Z", @@ -61,7 +61,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 3", + "train_name": "Train3", "labels": ["Tag-3", "MWS-NES"], "rolling_stock_name": "DUAL-MODE_RS_E2E", "start_time": "2024-10-17T05:00:00Z", @@ -95,7 +95,7 @@ "options": { "use_electrical_profiles": false } }, { - "train_name": "Train 4", + "train_name": "Train4", "labels": ["Tag-4", "WS-SES"], "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T05:15:00Z", @@ -138,7 +138,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 5", + "train_name": "Train5", "labels": ["Tag-5", "SWE-MES"], "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T07:30:00Z", @@ -172,7 +172,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 6", + "train_name": "Train6", "labels": ["Tag-6", "WS-NS"], "rolling_stock_name": "DUAL-MODE_RS_E2E", "start_time": "2024-10-17T07:45:00Z", @@ -206,7 +206,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 7", + "train_name": "Train7", "labels": ["Tag-7", "WS-NES"], "rolling_stock_name": "FAST_RS_E2E", "start_time": "2024-10-17T08:37:00Z", @@ -233,7 +233,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 8", + "train_name": "Train8", "labels": ["Tag-8", "SS-MES"], "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T08:56:01Z", @@ -267,7 +267,7 @@ "options": { "use_electrical_profiles": false } }, { - "train_name": "Train 9", + "train_name": "Train9", "labels": ["Tag-9", "MWS-NES"], "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T09:35:59Z", @@ -293,7 +293,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 10", + "train_name": "Train10", "labels": ["Tag-10", "SES-MES"], "rolling_stock_name": "DUAL-MODE_RS_E2E", "start_time": "2024-10-17T09:45:31Z", @@ -320,7 +320,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 11", + "train_name": "Train11", "labels": [], "rolling_stock_name": "", "start_time": "2024-10-17T09:45:43Z", @@ -347,7 +347,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 12", + "train_name": "Train12", "labels": ["Tag-12"], "rolling_stock_name": "", "start_time": "2024-10-17T10:16:03Z", @@ -375,7 +375,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 13", + "train_name": "Train13", "labels": ["Tag-13", "SES-SWS"], "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T10:48:00Z", @@ -409,7 +409,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 14", + "train_name": "Train14", "labels": ["Tag-14", "NES-MES"], "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T12:10:00Z", @@ -435,7 +435,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 15", + "train_name": "Train15", "labels": ["Tag-15"], "rolling_stock_name": "IMPROBABLE_RS_E2E", "start_time": "2024-10-17T12:30:00Z", @@ -462,7 +462,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 16", + "train_name": "Train16", "labels": ["Tag-16", "SES-WS"], "rolling_stock_name": "DUAL-MODE_RS_E2E", "start_time": "2024-10-17T13:00:00Z", @@ -496,7 +496,7 @@ "options": { "use_electrical_profiles": false } }, { - "train_name": "Train 17", + "train_name": "Train17", "labels": ["Tag-17", "WS-SES"], "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T14:05:00Z", @@ -538,7 +538,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 18", + "train_name": "Train18", "labels": ["Tag-18", "SWS-MWS"], "rolling_stock_name": "SLOW_RS_E2E", "start_time": "2024-10-17T14:15:08Z", @@ -564,7 +564,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 19", + "train_name": "Train19", "labels": ["Tag-19"], "rolling_stock_name": "", "start_time": "2024-10-17T15:55:00Z", @@ -590,7 +590,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 20", + "train_name": "Train20", "labels": ["Tag-20", "SS-WS"], "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T16:00:00Z", @@ -632,7 +632,7 @@ "options": { "use_electrical_profiles": true } }, { - "train_name": "Train 21", + "train_name": "Train21", "labels": ["Tag-21", "NWS-NES"], "rolling_stock_name": "ELECTRIC_RS_E2E", "start_time": "2024-10-17T19:05:00Z", diff --git a/front/tests/pages/stdcm-linked-train-page-model.ts b/front/tests/pages/stdcm-linked-train-page-model.ts index 197cdc19c00..c4ff70170c3 100644 --- a/front/tests/pages/stdcm-linked-train-page-model.ts +++ b/front/tests/pages/stdcm-linked-train-page-model.ts @@ -5,8 +5,6 @@ import LINKED_TRAIN_DETAILS from '../assets/linked-train-const'; import { DEFAULT_DETAILS } from '../assets/stdcm-const'; class STDCMLinkedTrainPage extends STDCMPage { - readonly anteriorLinkedTrainContainer: Locator; - readonly anteriorDeleteLinkedPathButton: Locator; readonly anteriorLinkedTrainField: Locator; @@ -15,7 +13,7 @@ class STDCMLinkedTrainPage extends STDCMPage { readonly anteriorLinkedTrainSearchButton: Locator; - readonly posteriorLinkedTrainContainer: Locator; + readonly anteriorLinkedTrainResultInfosButton: Locator; readonly posteriorDeleteLinkedPathButton: Locator; @@ -25,11 +23,10 @@ class STDCMLinkedTrainPage extends STDCMPage { readonly posteriorLinkedTrainSearchButton: Locator; + readonly posteriorLinkedTrainResultInfosButton: Locator; + constructor(readonly page: Page) { super(page); - this.anteriorLinkedTrainContainer = page.locator( - '.stdcm-linked-train-search-container.anterior-linked-train' - ); this.anteriorDeleteLinkedPathButton = this.anteriorLinkedTrainContainer.getByTestId( 'linked-train-delete-button' ); @@ -38,9 +35,6 @@ class STDCMLinkedTrainPage extends STDCMPage { this.anteriorLinkedTrainSearchButton = this.anteriorLinkedTrainContainer.getByTestId( 'linked-train-search-button' ); - this.posteriorLinkedTrainContainer = page.locator( - '.stdcm-linked-train-search-container.posterior-linked-train' - ); this.posteriorDeleteLinkedPathButton = this.posteriorLinkedTrainContainer.getByTestId( 'linked-train-delete-button' ); @@ -50,6 +44,12 @@ class STDCMLinkedTrainPage extends STDCMPage { this.posteriorLinkedTrainSearchButton = this.posteriorLinkedTrainContainer.getByTestId( 'linked-train-search-button' ); + this.anteriorLinkedTrainResultInfosButton = this.anteriorLinkedTrainContainer.locator( + '.linked-train-result-infos' + ); + this.posteriorLinkedTrainResultInfosButton = this.posteriorLinkedTrainContainer.locator( + '.linked-train-result-infos' + ); } // Add an anterior and posterior linked path card, verify default fields, and delete it @@ -71,5 +71,95 @@ class STDCMLinkedTrainPage extends STDCMPage { await expect(this.posteriorLinkedTrainDate).not.toBeVisible(); await expect(this.posteriorLinkedTrainSearchButton).not.toBeVisible(); } + + // Get anterior or posterior searched linked train details + async getLinkedTrainDetails(isAnterior: boolean = false) { + const trainResultInfosButton = isAnterior + ? this.posteriorLinkedTrainResultInfosButton + : this.anteriorLinkedTrainResultInfosButton; + await trainResultInfosButton.waitFor(); + + // Extract and process train details + return trainResultInfosButton.evaluateAll((buttons) => + buttons.map((button) => { + const trainName = button.querySelector('.train-name')?.textContent?.trim() || ''; + const segments = Array.from(button.querySelectorAll('.d-flex'), (segment) => + Array.from( + segment.querySelectorAll('.opDetails'), + (detail) => detail.textContent?.trim() || '' + ) + ); + return { trainName, segments }; + }) + ); + } + + // Add an anterior linked train and fill the path fields + async anteriorLinkedPathDetails() { + const { + trainName, + trainDate, + trainDetails, + dynamicOriginCi, + dynamicOriginCh, + originArrival, + dateOriginArrival, + timeOriginArrival, + toleranceOriginArrival, + toleranceFields, + } = LINKED_TRAIN_DETAILS.anterior; + await this.anteriorAddLinkedPathButton.click(); + await this.anteriorLinkedTrainField.fill(trainName); + await this.anteriorLinkedTrainDate.fill(trainDate); + await this.anteriorLinkedTrainSearchButton.click(); + await this.anteriorLinkedTrainResultInfosButton.click(); + const actualTrainDetails = await this.getLinkedTrainDetails(); + expect(actualTrainDetails).toEqual(trainDetails); + await expect(this.dynamicOriginCi).toHaveValue(dynamicOriginCi); + await expect(this.dynamicOriginCh).toHaveValue(dynamicOriginCh); + await expect(this.originArrival).toHaveValue(originArrival); + await expect(this.dateOriginArrival).toHaveValue(dateOriginArrival); + await expect(this.timeOriginArrival).toHaveValue(timeOriginArrival); + await expect(this.toleranceOriginArrival).toHaveValue(toleranceOriginArrival); + await this.fillToleranceField( + toleranceFields.min, + toleranceFields.max, + toleranceFields.isAnterior + ); + } + + // Add an posterior linked train and fill the path fields + async posteriorLinkedPathDetails() { + const { + trainName, + trainDate, + trainDetails, + dynamicDestinationCi, + dynamicDestinationCh, + destinationArrival, + dateDestinationArrival, + timeDestinationArrival, + toleranceDestinationArrival, + toleranceFields, + } = LINKED_TRAIN_DETAILS.posterior; + await this.posteriorAddLinkedPathButton.click(); + await this.posteriorLinkedTrainField.fill(trainName); + await this.posteriorLinkedTrainDate.fill(trainDate); + await this.posteriorLinkedTrainSearchButton.click(); + await this.posteriorLinkedTrainResultInfosButton.click(); + const actualTrainDetails = await this.getLinkedTrainDetails(true); + expect(actualTrainDetails).toEqual(trainDetails); + await expect(this.dynamicDestinationCi).toHaveValue(dynamicDestinationCi); + await expect(this.dynamicDestinationCh).toHaveValue(dynamicDestinationCh); + await expect(this.destinationArrival).toHaveValue(destinationArrival); + await expect(this.dateDestinationArrival).toHaveValue(dateDestinationArrival); + await expect(this.timeDestinationArrival).toHaveValue(timeDestinationArrival); + await expect(this.toleranceDestinationArrival).toHaveValue(toleranceDestinationArrival); + await this.fillToleranceField( + toleranceFields.min, + toleranceFields.max, + toleranceFields.isAnterior + ); + } } export default STDCMLinkedTrainPage; diff --git a/front/tests/pages/stdcm-page-model.ts b/front/tests/pages/stdcm-page-model.ts index 644b0a7f02d..22968ed2a0f 100644 --- a/front/tests/pages/stdcm-page-model.ts +++ b/front/tests/pages/stdcm-page-model.ts @@ -1,3 +1,6 @@ +import fs from 'fs'; +import path from 'path'; + import { expect, type Locator, type Page } from '@playwright/test'; import enTranslations from '../../public/locales/en/stdcm.json'; @@ -34,7 +37,7 @@ export interface ConsistFields { maxSpeed?: string; speedLimitTag?: string; } -const EXPECT_TO_PASS_TIMEOUT = 90_000; // Since toPass ignores custom expect timeouts, this timeout is set to account for all actions within the function. + const MINIMUM_SIMULATION_NUMBER = 1; class STDCMPage { @@ -144,6 +147,8 @@ class STDCMPage { readonly downloadSimulationButton: Locator; + readonly downloadLink: Locator; + readonly startNewQueryButton: Locator; readonly startNewQueryWithDataButton: Locator; @@ -245,7 +250,8 @@ class STDCMPage { this.incrementButton = page.locator('.minute-button', { hasText: '+1mn' }); this.allViasButton = page.getByTestId('all-vias-button'); this.retainSimulationButton = page.getByTestId('retain-simulation-button'); - this.downloadSimulationButton = page.getByTestId('download-simulation-button'); + this.downloadSimulationButton = page.locator('.download-simulation a[download]'); + this.downloadLink = page.locator('.download-simulation a'); this.startNewQueryButton = page.getByTestId('start-new-query-button'); this.startNewQueryWithDataButton = page.getByTestId('start-new-query-with-data-button'); this.originMarker = this.mapContainer.locator('img[alt="origin"]'); @@ -553,16 +559,20 @@ class STDCMPage { } // Fill origin section - async fillOriginDetailsLight() { + async fillOriginDetailsLight(arrivalTypeOverride: string = '', isPrecise: boolean = false) { const { input, chValue, arrivalDate, arrivalTime, tolerance, arrivalType } = LIGHT_ORIGIN_DETAILS; await this.dynamicOriginCi.fill(input); await this.suggestionNWS.click(); - await expect(this.dynamicOriginCh).toHaveValue(chValue); - await expect(this.originArrival).toHaveValue(arrivalType); - await this.dateOriginArrival.fill(arrivalDate); - await this.timeOriginArrival.fill(arrivalTime); - await this.fillToleranceField(tolerance.negative, tolerance.positive, true); + if (isPrecise && arrivalTypeOverride) { + await this.originArrival.selectOption(arrivalTypeOverride); + } else { + await expect(this.dynamicOriginCh).toHaveValue(chValue); + await expect(this.originArrival).toHaveValue(arrivalType); + await this.dateOriginArrival.fill(arrivalDate); + await this.timeOriginArrival.fill(arrivalTime); + await this.fillToleranceField(tolerance.negative, tolerance.positive, true); + } } // Fill destination section @@ -712,23 +722,44 @@ class STDCMPage { await expect(this.startNewQueryWithDataButton).toBeVisible(); } - async downloadSimulation(browserName: string) { - await expect(async () => { - const downloadPromise = this.page.waitForEvent('download'); - await this.downloadSimulationButton.dispatchEvent('click'); - const download = await downloadPromise.catch(() => { - throw new Error('Download event was not triggered.'); - }); + async downloadSimulation(browserName: string, isLinkedTrain: boolean = false): Promise { + try { + // Wait until there are no network requests for stability + await this.page.waitForLoadState('networkidle'); - // Verify filename and save the download - const suggestedFilename = download.suggestedFilename(); + // Get the download link element and suggested filename + const suggestedFilename = await this.downloadLink.getAttribute('download'); expect(suggestedFilename).toMatch(/^Stdcm.*\.pdf$/); - const downloadPath = `./tests/stdcm-results/${browserName}/${suggestedFilename}`; - await download.saveAs(downloadPath); + + const downloadDir = isLinkedTrain + ? path.join('./tests/stdcm-results/linkedTrain', browserName) + : path.join('./tests/stdcm-results', browserName); + const downloadPath = path.join(downloadDir, suggestedFilename!); + + await fs.promises.mkdir(downloadDir, { recursive: true }); + + // Get the file content from the `blob:` URL + const fileContent = await this.downloadSimulationButton.evaluate(async (el) => { + if (!(el instanceof HTMLAnchorElement)) { + throw new Error('Element is not an anchor tag'); + } + + const response = await fetch(el.href); + if (!response.ok) { + throw new Error(`Failed to fetch the blob: ${response.status} ${response.statusText}`); + } + + const buffer = await response.arrayBuffer(); + return Array.from(new Uint8Array(buffer)); + }); + + // Write the file to the local file system + await fs.promises.writeFile(downloadPath, Buffer.from(fileContent)); + logger.info(`The PDF was successfully downloaded to: ${downloadPath}`); - }).toPass({ - timeout: EXPECT_TO_PASS_TIMEOUT, - }); + } catch (error) { + logger.error('Failed to download simulation', error); + } } async startNewQuery() {