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..67ece753928 --- /dev/null +++ b/front/tests/014-stdcm-linked-train.spec.ts @@ -0,0 +1,103 @@ +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 infra: Infra; + let createdTowedRollingStock: TowedRollingStock; + + const fastRollingStockPrefilledValues = { + tonnage: '190', + length: '45', + maxSpeed: '220', + }; + const towedRollingStockPrefilledValues = { + tonnage: '46', + length: '26', + maxSpeed: '180', + }; + + test.beforeAll('Fetch infrastructure', async () => { + infra = await getInfra(); + createdTowedRollingStock = await setTowedRollingStock(); + }); + + test.beforeEach('Navigate to the STDCM page', async ({ page }) => { + // Navigate to STDCM page + const homePage = new HomePage(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 ({ page, browserName }) => { + const towedConsistDetails: ConsistFields = { + tractionEngine: fastRollingStockName, + towedRollingStock: createdTowedRollingStock.name, + }; + const [stdcmPage, stdcmLinkedTrainPage] = [new STDCMPage(page), new STDCMLinkedTrainPage(page)]; + 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.retainSimulation(); + await stdcmPage.downloadSimulation(browserName, true); + }); + + /** *************** Test 2 **************** */ + test('Verify STDCM posterior linked train', async ({ page, browserName }) => { + const towedConsistDetails: ConsistFields = { + tractionEngine: fastRollingStockName, + towedRollingStock: createdTowedRollingStock.name, + }; + const [stdcmPage, stdcmLinkedTrainPage] = [new STDCMPage(page), new STDCMLinkedTrainPage(page)]; + 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.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..13d6c646080 --- /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: ' Mid_East_station', + dynamicOriginCh: '', + 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: ' North_East_station', + dynamicDestinationCh: '', + 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/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 3a66048d4ae..9c739287dc1 100644 --- a/front/tests/pages/stdcm-linked-train-page-model.ts +++ b/front/tests/pages/stdcm-linked-train-page-model.ts @@ -1,6 +1,7 @@ import { expect, type Locator, type Page } from '@playwright/test'; import STDCMPage from './stdcm-page-model'; +import LINKED_TRAIN_DETAILS from '../assets/linked-train-const'; class STDCMLinkedTrainPage extends STDCMPage { readonly anteriorLinkedTrainContainer: Locator; @@ -23,6 +24,10 @@ class STDCMLinkedTrainPage extends STDCMPage { readonly posteriorLinkedTrainSearchButton: Locator; + readonly anteriorLinkedTrainResultInfosButton: Locator; + + readonly posteriorLinkedTrainResultInfosButton: Locator; + constructor(readonly page: Page) { super(page); this.anteriorLinkedTrainContainer = page.locator( @@ -48,6 +53,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 @@ -69,5 +80,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 3d3a36db69f..70a7e89a5e8 100644 --- a/front/tests/pages/stdcm-page-model.ts +++ b/front/tests/pages/stdcm-page-model.ts @@ -549,16 +549,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 @@ -708,7 +712,7 @@ class STDCMPage { await expect(this.startNewQueryWithDataButton).toBeVisible(); } - async downloadSimulation(browserName: string) { + async downloadSimulation(browserName: string, isLinkedTrain: boolean = false) { await expect(async () => { const downloadPromise = this.page.waitForEvent('download'); await this.downloadSimulationButton.dispatchEvent('click');