diff --git a/front/playwright.config.ts b/front/playwright.config.ts index 8b18a21850a..0724f844873 100644 --- a/front/playwright.config.ts +++ b/front/playwright.config.ts @@ -1,56 +1,29 @@ import { defineConfig, devices } from '@playwright/test'; -/** - * Read environment variables from file. - * https://github.com/motdotla/dotenv - */ -// require('dotenv').config(); -/** - * See https://playwright.dev/docs/test-configuration. - */ export default defineConfig({ testDir: './tests', - /* Maximum time one test can run for. */ - timeout: process.env.CI ? 90 * 1000 : 180 * 1000, // 90 seconds in CI, otherwise 180 seconds + timeout: 90_000, expect: { toHaveScreenshot: { maxDiffPixelRatio: 0.02 }, - /** - * Maximum time expect() should wait for the condition to be met. - */ - timeout: process.env.CI ? 10 * 1000 : 30 * 1000, // 10 seconds in CI, otherwise 30 seconds + timeout: 10_000, }, - /* Run tests in files in parallel */ fullyParallel: true, - /* - * Limit parallelism in CI based on CPU capacity, - * running 50% of the available workers when in CI. - * Otherwise, run tests with a single worker. - */ - workers: process.env.CI ? '50%' : 1, - /* Fail the build on CI if you accidentally left test.only in the source code. */ + workers: '50%', forbidOnly: !!process.env.CI, - /* Retry up to 2 times on CI, and 1 time otherwise */ - retries: process.env.CI ? 2 : 1, - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + retries: 1, use: { - /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ - actionTimeout: 0, - /* Base URL to use in actions like `await page.goto('/')`. */ + navigationTimeout: 30_000, + actionTimeout: 15_000, baseURL: process.env.BASE_URL || 'http://localhost:4000', - - /* Collect trace and video when retrying the first failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', video: 'on-first-retry', - - /* Set locale and timezone */ locale: 'fr', timezoneId: 'Europe/Paris', }, reporter: process.env.CI ? 'github' : [['line'], ['html']], - /* Configure projects for major browsers */ projects: [ { name: 'setup', testMatch: 'global-setup.ts', teardown: 'teardown' }, { diff --git a/front/tests/001-home-page.spec.ts b/front/tests/001-home-page.spec.ts index 369cbb4d448..412a3a09d8a 100644 --- a/front/tests/001-home-page.spec.ts +++ b/front/tests/001-home-page.spec.ts @@ -1,17 +1,17 @@ import { expect } from '@playwright/test'; +import test from './logging-fixture'; import HomePage from './pages/home-page-model'; -import test from './test-logger'; +import { getTranslations } from './utils'; import enTranslations from '../public/locales/en/home/home.json'; import frTranslations from '../public/locales/fr/home/home.json'; test.describe('Home page OSRD', () => { let homePage: HomePage; - let OSRDLanguage: string; + test.beforeEach('Navigate to the home page', async ({ page }) => { homePage = new HomePage(page); await homePage.goToHomePage(); - OSRDLanguage = await homePage.getOSRDLanguage(); }); test.afterEach('Returns to the home page', async () => { @@ -20,8 +20,10 @@ test.describe('Home page OSRD', () => { /** *************** Test 1 **************** */ test('Verify the links for different pages in Home Page', async () => { - // Determine the correct translations based on the selected language - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); // List of expected links on the home page const expectedLinks = [ diff --git a/front/tests/002-project-management.spec.ts b/front/tests/002-project-management.spec.ts index 182376173d6..d4b2280ef22 100644 --- a/front/tests/002-project-management.spec.ts +++ b/front/tests/002-project-management.spec.ts @@ -1,21 +1,23 @@ import type { Project } from 'common/api/osrdEditoastApi'; import projectData from './assets/operationStudies/project.json'; -import HomePage from './pages/home-page-model'; +import test from './logging-fixture'; import ProjectPage from './pages/project-page-model'; -import test from './test-logger'; import { generateUniqueName } from './utils'; import { createProject } from './utils/setup-utils'; import { deleteProject } from './utils/teardown-utils'; test.describe('Validate the Operational Study Project workflow', () => { let project: Project; + let projectPage: ProjectPage; + + test.beforeEach(async ({ page }) => { + projectPage = new ProjectPage(page); + }); /** *************** Test 1 **************** */ test('Create a new project', async ({ page }) => { - const projectPage = new ProjectPage(page); - - // Navigate to the Operational Studies projects page + // Go to projects page await page.goto('/operational-studies/projects'); // Define a unique project name for the test @@ -49,12 +51,8 @@ test.describe('Validate the Operational Study Project workflow', () => { test('Update an existing project', async ({ page }) => { // Create a project project = await createProject(generateUniqueName(projectData.name)); - - // Navigate to the Operational Studies projects page await page.goto('/operational-studies/projects'); - const homePage = new HomePage(page); - const projectPage = new ProjectPage(page); // Open the created project by name using the project page model await projectPage.openProjectByTestId(project.name); @@ -69,8 +67,8 @@ test.describe('Validate the Operational Study Project workflow', () => { }); // Navigate back to the Operational Studies page via the home page - await homePage.goToHomePage(); - await homePage.goToOperationalStudiesPage(); + await projectPage.goToHomePage(); + await projectPage.goToOperationalStudiesPage(); // Reopen the updated project and validate the updated data await projectPage.openProjectByTestId(`${project.name} (updated)`); @@ -90,11 +88,8 @@ test.describe('Validate the Operational Study Project workflow', () => { test('Delete a project', async ({ page }) => { // Create a project project = await createProject(generateUniqueName(projectData.name)); - - // Navigate to the Operational Studies projects page await page.goto('/operational-studies/projects'); - const projectPage = new ProjectPage(page); // Find the project by name and delete it using the page model await projectPage.openProjectByTestId(project.name); await projectPage.deleteProject(project.name); diff --git a/front/tests/003-study-management.spec.ts b/front/tests/003-study-management.spec.ts index 18eb6e63efa..49a19409696 100644 --- a/front/tests/003-study-management.spec.ts +++ b/front/tests/003-study-management.spec.ts @@ -3,41 +3,40 @@ import { v4 as uuidv4 } from 'uuid'; import type { Project, Study } from 'common/api/osrdEditoastApi'; import studyData from './assets/operationStudies/study.json'; -import HomePage from './pages/home-page-model'; +import test from './logging-fixture'; import StudyPage from './pages/study-page-model'; -import test from './test-logger'; -import { formatDateToDayMonthYear, generateUniqueName } from './utils'; +import { generateUniqueName, getTranslations } from './utils'; import { getProject } from './utils/api-setup'; +import { formatDateToDayMonthYear } from './utils/date'; import { createStudy } from './utils/setup-utils'; import { deleteStudy } from './utils/teardown-utils'; import enTranslations from '../public/locales/en/operationalStudies/study.json'; import frTranslations from '../public/locales/fr/operationalStudies/study.json'; test.describe('Validate the Study creation workflow', () => { + let studyPage: StudyPage; let project: Project; let study: Study; - let OSRDLanguage: string; - test.beforeAll(' Retrieve a project', async () => { + let translations: typeof enTranslations | typeof frTranslations; + test.beforeAll(' Retrieve a project and the translation', async () => { project = await getProject(); + translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); }); - test.beforeEach('Create a new study linked to the project', async ({ page }) => { - const homePage = new HomePage(page); - await homePage.goToHomePage(); - OSRDLanguage = await homePage.getOSRDLanguage(); + test.beforeEach(async ({ page }) => { + studyPage = new StudyPage(page); }); /** *************** Test 1 **************** */ test('Create a new study', async ({ page }) => { - const studyPage = new StudyPage(page); // Navigate to project page await page.goto(`/operational-studies/projects/${project.id}`); - - // Set translations based on the language - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; const studyName = `${studyData.name} ${uuidv4()}`; // Unique study name const todayDateISO = new Date().toISOString().split('T')[0]; // Get today's date in ISO format - const expectedDate = formatDateToDayMonthYear(todayDateISO, OSRDLanguage); + const expectedDate = formatDateToDayMonthYear(todayDateISO); // Create a new study using the study page model await studyPage.createStudy({ name: studyName, @@ -72,14 +71,12 @@ test.describe('Validate the Study creation workflow', () => { /** *************** Test 2 **************** */ test('Update an existing study', async ({ page }) => { - const studyPage = new StudyPage(page); // Create a study study = await createStudy(project.id, generateUniqueName(studyData.name)); // Navigate to study page await page.goto(`/operational-studies/projects/${project.id}/studies/${study.id}`); - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; const tomorrowDateISO = new Date(Date.now() + 86400000).toISOString().split('T')[0]; // Get tomorrow's date in ISO format - const expectedDate = formatDateToDayMonthYear(tomorrowDateISO, OSRDLanguage); + const expectedDate = formatDateToDayMonthYear(tomorrowDateISO); // Update the study with new values await studyPage.updateStudy({ name: `${study.name} (updated)`, @@ -122,8 +119,6 @@ test.describe('Validate the Study creation workflow', () => { // Create a study study = await createStudy(project.id, generateUniqueName(studyData.name)); - const studyPage = new StudyPage(page); - // Navigate to the list of studies for the project await page.goto(`/operational-studies/projects/${project.id}`); diff --git a/front/tests/004-scenario-management.spec.ts b/front/tests/004-scenario-management.spec.ts index 21e5895bd90..d09176a7740 100644 --- a/front/tests/004-scenario-management.spec.ts +++ b/front/tests/004-scenario-management.spec.ts @@ -4,14 +4,15 @@ import type { ElectricalProfileSet, Project, Scenario, Study } from 'common/api/ import scenarioData from './assets/operationStudies/scenario.json'; import { infrastructureName } from './assets/project-const'; +import test from './logging-fixture'; import ScenarioPage from './pages/scenario-page-model'; -import test from './test-logger'; import { generateUniqueName } from './utils'; import { deleteApiRequest, getProject, getStudy, setElectricalProfile } from './utils/api-setup'; import createScenario from './utils/scenario'; import { deleteScenario } from './utils/teardown-utils'; test.describe('Validate the Scenario creation workflow', () => { + let scenarioPage: ScenarioPage; let project: Project; let study: Study; let scenario: Scenario; @@ -27,10 +28,12 @@ test.describe('Validate the Scenario creation workflow', () => { await deleteApiRequest(`/api/electrical_profile_set/${electricalProfileSet.id}/`); }); + test.beforeEach(async ({ page }) => { + scenarioPage = new ScenarioPage(page); + }); + /** *************** Test 1 **************** */ test('Create a new scenario', async ({ page }) => { - const scenarioPage = new ScenarioPage(page); - // Navigate to the study page for the selected project await page.goto(`/operational-studies/projects/${project.id}/studies/${study.id}`); @@ -59,8 +62,6 @@ test.describe('Validate the Scenario creation workflow', () => { // Set up a scenario ({ project, study, scenario } = await createScenario()); - const scenarioPage = new ScenarioPage(page); - // Navigate to the specific scenario page await page.goto(`/operational-studies/projects/${project.id}/studies/${study.id}`); await scenarioPage.openScenarioByTestId(scenario.name); @@ -98,8 +99,6 @@ test.describe('Validate the Scenario creation workflow', () => { // Set up a scenario ({ project, study, scenario } = await createScenario()); - // Navigate to the specific scenario page - const scenarioPage = new ScenarioPage(page); // Navigate to the specific scenario page await page.goto(`/operational-studies/projects/${project.id}/studies/${study.id}`); await scenarioPage.openScenarioByTestId(scenario.name); diff --git a/front/tests/005-operational-studies.spec.ts b/front/tests/005-operational-studies.spec.ts index 226cb054813..b8ad1235ddf 100644 --- a/front/tests/005-operational-studies.spec.ts +++ b/front/tests/005-operational-studies.spec.ts @@ -9,10 +9,10 @@ import type { } from 'common/api/osrdEditoastApi'; import { electricRollingStockName } from './assets/project-const'; +import test from './logging-fixture'; import RoutePage from './pages/op-route-page-model'; import OperationalStudiesPage from './pages/operational-studies-page-model'; import RollingStockSelectorPage from './pages/rollingstock-selector-page-model'; -import test from './test-logger'; import { waitForInfraStateToBeCached } from './utils'; import { getInfra, getRollingStock } from './utils/api-setup'; import createScenario from './utils/scenario'; @@ -20,6 +20,10 @@ import { deleteScenario } from './utils/teardown-utils'; test.describe('Verify simulation configuration in operational studies', () => { test.slow(); + + let rollingstockPage: RollingStockSelectorPage; + let operationalStudiesPage: OperationalStudiesPage; + let routePage: RoutePage; let project: Project; let study: Study; let scenario: Scenario; @@ -31,7 +35,13 @@ test.describe('Verify simulation configuration in operational studies', () => { infra = await getInfra(); }); - test.beforeEach('Set up the project, study, and scenario', async () => { + test.beforeEach('Set up the project, study, and scenario', async ({ page }) => { + [rollingstockPage, operationalStudiesPage, routePage] = [ + new RollingStockSelectorPage(page), + new OperationalStudiesPage(page), + new RoutePage(page), + ]; + ({ project, study, scenario } = await createScenario()); }); @@ -42,11 +52,6 @@ test.describe('Verify simulation configuration in operational studies', () => { /** *************** Test **************** */ test('Pathfinding with rolling stock and composition code', async ({ page }) => { // Page models - const [rollingstockPage, operationalStudiesPage, routePage] = [ - new RollingStockSelectorPage(page), - new OperationalStudiesPage(page), - new RoutePage(page), - ]; // Navigate to the scenario page for the given project and study await page.goto( diff --git a/front/tests/006-stdcm.spec.ts b/front/tests/006-stdcm.spec.ts index 37e50ca2cd8..8004ad33e52 100644 --- a/front/tests/006-stdcm.spec.ts +++ b/front/tests/006-stdcm.spec.ts @@ -1,26 +1,23 @@ import type { Infra, TowedRollingStock } from 'common/api/osrdEditoastApi'; import { electricRollingStockName, fastRollingStockName } from './assets/project-const'; -import HomePage from './pages/home-page-model'; +import test from './logging-fixture'; import STDCMLinkedTrainPage from './pages/stdcm-linked-train-page-model'; -import STDCMPage, { type ConsistFields } from './pages/stdcm-page-model'; -import test from './test-logger'; +import STDCMPage from './pages/stdcm-page-model'; import { handleAndVerifyInput, waitForInfraStateToBeCached } from './utils'; import { getInfra, setTowedRollingStock } from './utils/api-setup'; +import type { ConsistFields } from './utils/types'; -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 stdcmPage: STDCMPage; + let stdcmLinkedTrainPage: STDCMLinkedTrainPage; + let infra: Infra; - let OSRDLanguage: string; let createdTowedRollingStock: TowedRollingStock; + const UPDATED_ORIGIN_ARRIVAL_DATE = '18/10/24'; const consistDetails: ConsistFields = { tractionEngine: electricRollingStockName, @@ -51,22 +48,19 @@ test.describe('Verify stdcm simulation page', () => { }); 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(); + [stdcmPage, stdcmLinkedTrainPage] = [new STDCMPage(page), new STDCMLinkedTrainPage(page)]; await page.goto('/stdcm'); - await page.waitForLoadState('domcontentloaded', { timeout: 30_000 }); - await homePage.removeViteOverlay(); + await page.waitForLoadState('networkidle'); + await stdcmPage.removeViteOverlay(); // Wait for infra to be in 'CACHED' state before proceeding await waitForInfraStateToBeCached(infra.id); }); /** *************** Test 1 **************** */ - test('Verify default STDCM page', async ({ page }) => { + test('Verify default STDCM page', async () => { // Verify visibility of STDCM elements and handle default fields - const [stdcmPage, stdcmLinkedTrainPage] = [new STDCMPage(page), new STDCMLinkedTrainPage(page)]; + await stdcmPage.verifyStdcmElementsVisibility(); await stdcmPage.verifyAllDefaultPageFields(); await stdcmPage.addAndDeletedDefaultVia(); @@ -74,10 +68,8 @@ test.describe('Verify stdcm simulation page', () => { }); /** *************** Test 2 **************** */ - test('Launch STDCM simulation with all stops', async ({ page }) => { + test('Launch STDCM simulation with all stops', async () => { // Populate STDCM page with origin, destination, and via details, then verify - const stdcmPage = new STDCMPage(page); - await stdcmPage.fillAndVerifyConsistDetails( consistDetails, tractionEnginePrefilledValues.tonnage, @@ -85,11 +77,11 @@ test.describe('Verify stdcm simulation page', () => { tractionEnginePrefilledValues.maxSpeed ); await stdcmPage.fillAndVerifyOriginDetails(); - await stdcmPage.fillAndVerifyDestinationDetails(OSRDLanguage); + await stdcmPage.fillAndVerifyDestinationDetails(); const viaDetails = [ { viaNumber: 1, ciSearchText: 'mid_west' }, { viaNumber: 2, ciSearchText: 'mid_east' }, - { viaNumber: 3, ciSearchText: 'nS', language: OSRDLanguage }, + { viaNumber: 3, ciSearchText: 'nS' }, ]; for (const viaDetail of viaDetails) { @@ -101,12 +93,12 @@ test.describe('Verify stdcm simulation page', () => { }); /** *************** Test 3 **************** */ - test('Launch simulation with and without capacity for towed rolling stock', async ({ page }) => { + test('Launch simulation with and without capacity for towed rolling stock', async () => { const towedConsistDetails: ConsistFields = { tractionEngine: fastRollingStockName, towedRollingStock: createdTowedRollingStock.name, }; - const stdcmPage = new STDCMPage(page); + await stdcmPage.fillAndVerifyConsistDetails( towedConsistDetails, fastRollingStockPrefilledValues.tonnage, @@ -125,14 +117,12 @@ test.describe('Verify stdcm simulation page', () => { // Run first simulation without capacity await stdcmPage.launchSimulation(); await stdcmPage.verifySimulationDetails({ - language: OSRDLanguage, simulationNumber: 1, }); // Update tonnage and launch a second simulation with capacity await handleAndVerifyInput(stdcmPage.dateOriginArrival, UPDATED_ORIGIN_ARRIVAL_DATE); await stdcmPage.launchSimulation(); await stdcmPage.verifySimulationDetails({ - language: OSRDLanguage, simulationNumber: 2, simulationLengthAndDuration: '51 km — 2h 35min', }); diff --git a/front/tests/007-op-rollingstock-tab.spec.ts b/front/tests/007-op-rollingstock-tab.spec.ts index dbed68250f9..0ad53abb890 100644 --- a/front/tests/007-op-rollingstock-tab.spec.ts +++ b/front/tests/007-op-rollingstock-tab.spec.ts @@ -9,15 +9,17 @@ import type { } from 'common/api/osrdEditoastApi'; import { dualModeRollingStockName, electricRollingStockName } from './assets/project-const'; +import test from './logging-fixture'; import OperationalStudiesPage from './pages/operational-studies-page-model'; import RollingStockSelectorPage from './pages/rollingstock-selector-page-model'; -import test from './test-logger'; import { waitForInfraStateToBeCached } from './utils'; import { getInfra, getRollingStock } from './utils/api-setup'; import createScenario from './utils/scenario'; import { deleteScenario } from './utils/teardown-utils'; test.describe('Rolling stock Tab Verification', () => { + let operationalStudiesPage: OperationalStudiesPage; + let rollingStockSelector: RollingStockSelectorPage; let project: Project; let study: Study; let scenario: Scenario; @@ -35,19 +37,19 @@ test.describe('Rolling stock Tab Verification', () => { }); test.beforeEach('Navigate to the scenario page', async ({ page }) => { + [operationalStudiesPage, rollingStockSelector] = [ + new OperationalStudiesPage(page), + new RollingStockSelectorPage(page), + ]; await page.goto( `/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}` ); - // Ensure infrastructure is loaded before each test // Wait for infra to be in 'CACHED' state before proceeding await waitForInfraStateToBeCached(infra.id); }); /** *************** Test 1 **************** */ - test('Select a rolling stock for operational study', async ({ page }) => { - const operationalStudiesPage = new OperationalStudiesPage(page); - const rollingStockSelector = new RollingStockSelectorPage(page); - + test('Select a rolling stock for operational study', async () => { // Add a train and verify the presence of warnings await operationalStudiesPage.clickOnAddTrainButton(); await operationalStudiesPage.verifyTabWarningPresence(); @@ -77,10 +79,7 @@ test.describe('Rolling stock Tab Verification', () => { }); /** *************** Test 2 **************** */ - test('Modify a rolling stock for operational study', async ({ page }) => { - const operationalStudiesPage = new OperationalStudiesPage(page); - const rollingStockSelector = new RollingStockSelectorPage(page); - + test('Modify a rolling stock for operational study', async () => { // Add a train and select the electric rolling stock await operationalStudiesPage.clickOnAddTrainButton(); await rollingStockSelector.openEmptyRollingStockSelector(); diff --git a/front/tests/008-train-schedule.spec.ts b/front/tests/008-train-schedule.spec.ts index 19d6d5b72b5..f99c2896f77 100644 --- a/front/tests/008-train-schedule.spec.ts +++ b/front/tests/008-train-schedule.spec.ts @@ -5,19 +5,20 @@ import { trainScheduleScenarioName, trainScheduleStudyName, } from './assets/project-const'; -import HomePage from './pages/home-page-model'; +import test from './logging-fixture'; import OperationalStudiesTimetablePage from './pages/op-timetable-page-model'; -import test from './test-logger'; import { waitForInfraStateToBeCached } from './utils'; import { getInfra, getProject, getScenario, getStudy } from './utils/api-setup'; test.describe('Verify train schedule elements and filters', () => { test.slow(); + test.use({ viewport: { width: 1920, height: 1080 } }); + + let opTimetablePage: OperationalStudiesTimetablePage; let project: Project; let study: Study; let scenario: Scenario; let infra: Infra; - let OSRDLanguage: string; // Constants for expected train counts const TOTAL_TRAINS = 21; @@ -27,6 +28,7 @@ test.describe('Verify train schedule elements and filters', () => { const NOT_HONORED_TRAINS = 3; const VALID_AND_HONORED_TRAINS = 14; const INVALID_AND_NOT_HONORED_TRAINS = 0; + test.beforeAll('Fetch project, study and scenario with train schedule', async () => { project = await getProject(trainScheduleProjectName); study = await getStudy(project.id, trainScheduleStudyName); @@ -35,9 +37,7 @@ test.describe('Verify train schedule elements and filters', () => { }); test.beforeEach('Navigate to scenario page before each test', async ({ page }) => { - const homePage = new HomePage(page); - await homePage.goToHomePage(); - OSRDLanguage = await homePage.getOSRDLanguage(); + opTimetablePage = new OperationalStudiesTimetablePage(page); await page.goto( `/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}` ); @@ -46,47 +46,30 @@ test.describe('Verify train schedule elements and filters', () => { }); /** *************** Test 1 **************** */ - test('Loading trains and verifying simulation result', async ({ page }) => { - const opTimetablePage = new OperationalStudiesTimetablePage(page); - + test('Loading trains and verifying simulation result', async () => { // Verify train count, invalid train messages, and train simulation results await opTimetablePage.verifyTrainCount(TOTAL_TRAINS); - await opTimetablePage.verifyInvalidTrainsMessageVisibility(OSRDLanguage); + await opTimetablePage.verifyInvalidTrainsMessageVisibility(); await opTimetablePage.checkSelectedTimetableTrain(); - await opTimetablePage.filterValidityAndVerifyTrainCount(OSRDLanguage, 'Valid', VALID_TRAINS); + await opTimetablePage.filterValidityAndVerifyTrainCount('Valid', VALID_TRAINS); await opTimetablePage.verifyEachTrainSimulation(); }); /** *************** Test 2 **************** */ - test('Filtering imported trains', async ({ page }) => { - const opTimetablePage = new OperationalStudiesTimetablePage(page); - + test('Filtering imported trains', async () => { // Verify train count and apply different filters for validity and honored status await opTimetablePage.verifyTrainCount(TOTAL_TRAINS); + await opTimetablePage.filterValidityAndVerifyTrainCount('Invalid', INVALID_TRAINS); + await opTimetablePage.filterValidityAndVerifyTrainCount('All', TOTAL_TRAINS); + await opTimetablePage.filterHonoredAndVerifyTrainCount('Honored', HONORED_TRAINS); + await opTimetablePage.filterValidityAndVerifyTrainCount('Valid', VALID_AND_HONORED_TRAINS); + await opTimetablePage.filterHonoredAndVerifyTrainCount('Not honored', NOT_HONORED_TRAINS); await opTimetablePage.filterValidityAndVerifyTrainCount( - OSRDLanguage, - 'Invalid', - INVALID_TRAINS - ); - await opTimetablePage.filterValidityAndVerifyTrainCount(OSRDLanguage, 'All', TOTAL_TRAINS); - await opTimetablePage.filterHonoredAndVerifyTrainCount(OSRDLanguage, 'Honored', HONORED_TRAINS); - await opTimetablePage.filterValidityAndVerifyTrainCount( - OSRDLanguage, - 'Valid', - VALID_AND_HONORED_TRAINS - ); - await opTimetablePage.filterHonoredAndVerifyTrainCount( - OSRDLanguage, - 'Not honored', - NOT_HONORED_TRAINS - ); - await opTimetablePage.filterValidityAndVerifyTrainCount( - OSRDLanguage, 'Invalid', INVALID_AND_NOT_HONORED_TRAINS ); - await opTimetablePage.filterHonoredAndVerifyTrainCount(OSRDLanguage, 'All', INVALID_TRAINS); - await opTimetablePage.filterValidityAndVerifyTrainCount(OSRDLanguage, 'All', TOTAL_TRAINS); + await opTimetablePage.filterHonoredAndVerifyTrainCount('All', INVALID_TRAINS); + await opTimetablePage.filterValidityAndVerifyTrainCount('All', TOTAL_TRAINS); // Verify train composition filters with predefined filter codes and expected counts const compositionFilters = [ @@ -97,11 +80,7 @@ test.describe('Verify train schedule elements and filters', () => { ]; for (const filter of compositionFilters) { - await opTimetablePage.clickCodeCompoTrainFilterButton( - OSRDLanguage, - filter.code, - filter.count - ); + await opTimetablePage.clickCodeCompoTrainFilterButton(filter.code, filter.count); } }); }); diff --git a/front/tests/009-rollingstock-editor.spec.ts b/front/tests/009-rollingstock-editor.spec.ts index ed60c324ae1..585da7bf1da 100644 --- a/front/tests/009-rollingstock-editor.spec.ts +++ b/front/tests/009-rollingstock-editor.spec.ts @@ -1,9 +1,9 @@ import { expect } from '@playwright/test'; import { dualModeRollingStockName, electricRollingStockName } from './assets/project-const'; +import test from './logging-fixture'; import RollingstockEditorPage from './pages/rollingstock-editor-page-model'; import RollingStockSelectorPage from './pages/rollingstock-selector-page-model'; -import test from './test-logger'; import { generateUniqueName, verifyAndCheckInputById, @@ -13,6 +13,8 @@ import { import { deleteRollingStocks } from './utils/teardown-utils'; test.describe('Rollingstock editor page tests', () => { + let rollingStockEditorPage: RollingstockEditorPage; + let rollingStockSelectorPage: RollingStockSelectorPage; let uniqueRollingStockName: string; let uniqueUpdatedRollingStockName: string; let uniqueDeletedRollingStockName: string; @@ -22,7 +24,11 @@ test.describe('Rollingstock editor page tests', () => { test.beforeEach( 'Generate unique names and ensure all existing RS are deleted', async ({ page }) => { - const rollingStockEditorPage = new RollingstockEditorPage(page); + [rollingStockEditorPage, rollingStockSelectorPage] = [ + new RollingstockEditorPage(page), + new RollingStockSelectorPage(page), + ]; + uniqueRollingStockName = generateUniqueName('RSN'); uniqueUpdatedRollingStockName = generateUniqueName('U_RSN'); uniqueDeletedRollingStockName = generateUniqueName('D_RSN'); @@ -40,8 +46,6 @@ test.describe('Rollingstock editor page tests', () => { /** *************** Test 1 **************** */ test('Create a new rolling stock', async ({ page }) => { - const rollingStockEditorPage = new RollingstockEditorPage(page); - // Start the rolling stock creation process await rollingStockEditorPage.clickOnNewRollingstockButton(); @@ -104,8 +108,6 @@ test.describe('Rollingstock editor page tests', () => { /** *************** Test 2 **************** */ test('Duplicate and modify a rolling stock', async ({ page }) => { - const rollingStockEditorPage = new RollingstockEditorPage(page); - // Select the existing electric rolling stock and duplicate it await rollingStockEditorPage.selectRollingStock(electricRollingStockName); await rollingStockEditorPage.duplicateRollingStock(); @@ -135,8 +137,6 @@ test.describe('Rollingstock editor page tests', () => { /** *************** Test 3 **************** */ test('Duplicate and delete a rolling stock', async ({ page }) => { - const rollingStockEditorPage = new RollingstockEditorPage(page); - const rollingStockSelectorPage = new RollingStockSelectorPage(page); // Duplicate and change the name of the rolling stock await rollingStockEditorPage.selectRollingStock(electricRollingStockName); await rollingStockEditorPage.duplicateRollingStock(); @@ -158,9 +158,7 @@ test.describe('Rollingstock editor page tests', () => { }); /** *************** Test 4 **************** */ - test('Filtering rolling stocks', async ({ page }) => { - const rollingStockSelectorPage = new RollingStockSelectorPage(page); - + test('Filtering rolling stocks', async () => { // Get the initial rolling stock count const initialRollingStockFoundNumber = await rollingStockSelectorPage.getRollingStockSearchNumber(); @@ -194,10 +192,7 @@ test.describe('Rollingstock editor page tests', () => { }); /** *************** Test 5 **************** */ - test('Search for a rolling stock', async ({ page }) => { - const rollingStockEditorPage = new RollingstockEditorPage(page); - const rollingStockSelectorPage = new RollingStockSelectorPage(page); - + test('Search for a rolling stock', async () => { const initialRollingStockFoundNumber = await rollingStockSelectorPage.getRollingStockSearchNumber(); diff --git a/front/tests/010-op-route-tab.spec.ts b/front/tests/010-op-route-tab.spec.ts index 411f4995958..57ee031152d 100644 --- a/front/tests/010-op-route-tab.spec.ts +++ b/front/tests/010-op-route-tab.spec.ts @@ -1,11 +1,10 @@ import type { Infra, Project, Scenario, Study } from 'common/api/osrdEditoastApi'; import { electricRollingStockName } from './assets/project-const'; -import HomePage from './pages/home-page-model'; +import test from './logging-fixture'; import RoutePage from './pages/op-route-page-model'; import OperationalStudiesPage from './pages/operational-studies-page-model'; import RollingStockSelectorPage from './pages/rollingstock-selector-page-model'; -import test from './test-logger'; import { waitForInfraStateToBeCached } from './utils'; import { getInfra } from './utils/api-setup'; import createScenario from './utils/scenario'; @@ -13,10 +12,13 @@ import { deleteScenario } from './utils/teardown-utils'; test.describe('Route Tab Verification', () => { test.slow(); + + let operationalStudiesPage: OperationalStudiesPage; + let rollingstockSelectorPage: RollingStockSelectorPage; + let routePage: RoutePage; let project: Project; let study: Study; let scenario: Scenario; - let OSRDLanguage: string; let infra: Infra; test.beforeAll('Set up the scenario', async () => { @@ -31,15 +33,12 @@ test.describe('Route Tab Verification', () => { test.beforeEach( 'Navigate to the scenario page and select the rolling stock before each test', async ({ page }) => { - const [operationalStudiesPage, rollingstockSelectorPage, homePage] = [ + [operationalStudiesPage, rollingstockSelectorPage, routePage] = [ new OperationalStudiesPage(page), new RollingStockSelectorPage(page), - new HomePage(page), + new RoutePage(page), ]; - await homePage.goToHomePage(); - OSRDLanguage = await homePage.getOSRDLanguage(); - await page.goto( `/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}` ); @@ -58,12 +57,9 @@ test.describe('Route Tab Verification', () => { ); /** *************** Test 1 **************** */ - test('Select a route for operational study', async ({ page, browserName }) => { - const operationalStudiesPage = new OperationalStudiesPage(page); - const routePage = new RoutePage(page); - + test('Select a route for operational study', async ({ browserName }) => { // Verify that no route is initially selected - await routePage.verifyNoSelectedRoute(OSRDLanguage); + await routePage.verifyNoSelectedRoute(); // Perform pathfinding by station trigrams and verify map markers in Chromium await routePage.performPathfindingByTrigram('WS', 'NES', 'MES'); @@ -77,10 +73,7 @@ test.describe('Route Tab Verification', () => { }); /** *************** Test 2 **************** */ - test('Adding waypoints to a route for operational study', async ({ page, browserName }) => { - const operationalStudiesPage = new OperationalStudiesPage(page); - const routePage = new RoutePage(page); - + test('Adding waypoints to a route for operational study', async ({ browserName }) => { // Perform pathfinding by station trigrams await routePage.performPathfindingByTrigram('WS', 'NES'); @@ -108,11 +101,8 @@ test.describe('Route Tab Verification', () => { /** *************** Test 3 **************** */ test('Reversing and deleting waypoints in a route for operational study', async ({ - page, browserName, }) => { - const routePage = new RoutePage(page); - // Perform pathfinding by station trigrams and verify map markers in Chromium await routePage.performPathfindingByTrigram('WS', 'SES', 'MWS'); const expectedMapMarkersValues = ['West_station', 'South_East_station', 'Mid_West_station']; @@ -128,8 +118,8 @@ test.describe('Route Tab Verification', () => { } // Delete operational points and verify no selected route - await routePage.clickOnDeleteOPButtons(OSRDLanguage); - await routePage.verifyNoSelectedRoute(OSRDLanguage); + await routePage.clickOnDeleteOPButtons(); + await routePage.verifyNoSelectedRoute(); // Perform pathfinding again and verify map markers in Chromium await routePage.performPathfindingByTrigram('WS', 'SES', 'MWS'); @@ -139,6 +129,6 @@ test.describe('Route Tab Verification', () => { // Delete the itinerary and verify no selected route await routePage.clickDeleteItineraryButton(); - await routePage.verifyNoSelectedRoute(OSRDLanguage); + await routePage.verifyNoSelectedRoute(); }); }); diff --git a/front/tests/011-op-times-and-stops-tab.spec.ts b/front/tests/011-op-times-and-stops-tab.spec.ts index 1e064c6679b..3f74eda3f9b 100644 --- a/front/tests/011-op-times-and-stops-tab.spec.ts +++ b/front/tests/011-op-times-and-stops-tab.spec.ts @@ -3,32 +3,36 @@ import { expect } from '@playwright/test'; import type { Infra, Project, Scenario, Study } from 'common/api/osrdEditoastApi'; import { dualModeRollingStockName } from './assets/project-const'; -import HomePage from './pages/home-page-model'; +import test from './logging-fixture'; import OperationalStudiesInputTablePage from './pages/op-input-table-page-model'; import OperationalStudiesOutputTablePage from './pages/op-output-table-page-model'; import RoutePage from './pages/op-route-page-model'; import OperationalStudiesPage from './pages/operational-studies-page-model'; import RollingStockSelectorPage from './pages/rollingstock-selector-page-model'; -import test from './test-logger'; -import { readJsonFile, waitForInfraStateToBeCached } from './utils'; +import { getTranslations, readJsonFile, waitForInfraStateToBeCached } from './utils'; import { getInfra } from './utils/api-setup'; -import { cleanWhitespace, cleanWhitespaceInArray, type StationData } from './utils/dataNormalizer'; +import { cleanWhitespace, cleanWhitespaceInArray } from './utils/dataNormalizer'; import createScenario from './utils/scenario'; import scrollContainer from './utils/scrollHelper'; import { deleteScenario } from './utils/teardown-utils'; +import type { StationData } from './utils/types'; import enTranslations from '../public/locales/en/timesStops.json'; import frTranslations from '../public/locales/fr/timesStops.json'; test.describe('Times and Stops Tab Verification', () => { test.slow(); - // Set viewport to ensure correct element visibility and interaction test.use({ viewport: { width: 1920, height: 1080 } }); + let operationalStudiesPage: OperationalStudiesPage; + let rollingStockPage: RollingStockSelectorPage; + let routePage: RoutePage; + let opInputTablePage: OperationalStudiesInputTablePage; + let opOutputTablePage: OperationalStudiesOutputTablePage; let project: Project; let study: Study; let scenario: Scenario; let infra: Infra; - let OSRDLanguage: string; + let translations: typeof enTranslations | typeof frTranslations; // Load test data for table inputs and expected results const initialInputsData: CellData[] = readJsonFile( @@ -63,23 +67,25 @@ test.describe('Times and Stops Tab Verification', () => { type TranslationKeys = keyof typeof enTranslations; - test.beforeAll('Fetch infrastructure', async () => { + test.beforeAll('Fetch infrastructure and get translation', async () => { infra = await getInfra(); + translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); }); test.beforeEach( 'Navigate to Times and Stops tab with rolling stock and route set', async ({ page }) => { - const [operationalStudiesPage, routePage, rollingStockPage, homePage] = [ + [operationalStudiesPage, routePage, rollingStockPage, opInputTablePage, opOutputTablePage] = [ new OperationalStudiesPage(page), new RoutePage(page), new RollingStockSelectorPage(page), - new HomePage(page), + new OperationalStudiesInputTablePage(page), + new OperationalStudiesOutputTablePage(page), ]; - await homePage.goToHomePage(); - OSRDLanguage = await homePage.getOSRDLanguage(); - // Set up scenario for operational study ({ project, study, scenario } = await createScenario()); @@ -93,10 +99,9 @@ test.describe('Times and Stops Tab Verification', () => { // Setup train configuration and schedule await operationalStudiesPage.clickOnAddTrainButton(); - await operationalStudiesPage.setTrainScheduleName('Train-name-e2e-test'); - await page.waitForTimeout(500); // Wait for any async actions to complete await operationalStudiesPage.setTrainStartTime('11:22:40'); await rollingStockPage.selectRollingStock(dualModeRollingStockName); + await operationalStudiesPage.setTrainScheduleName('Train-name-e2e-test'); // Perform route pathfinding await operationalStudiesPage.clickOnRouteTab(); @@ -113,15 +118,6 @@ test.describe('Times and Stops Tab Verification', () => { }); test('should correctly set and display times and stops tables', async ({ page }) => { - const [opInputTablePage, opOutputTablePage, operationalStudiesPage, routePage] = [ - new OperationalStudiesInputTablePage(page), - new OperationalStudiesOutputTablePage(page), - new OperationalStudiesPage(page), - new RoutePage(page), - ]; - - // Set translations based on selected language - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; const expectedColumnNames = cleanWhitespaceInArray([ translations.name, translations.ch, @@ -148,7 +144,6 @@ test.describe('Times and Stops Tab Verification', () => { cell.stationName, translatedHeader, cell.value, - OSRDLanguage, cell.marginForm ); } @@ -177,18 +172,10 @@ test.describe('Times and Stops Tab Verification', () => { // Scroll and extract output table data for verification await scrollContainer(page, '.time-stop-outputs .time-stops-datasheet .dsg-container'); - await opOutputTablePage.getOutputTableData(outputExpectedCellData, OSRDLanguage); + await opOutputTablePage.getOutputTableData(outputExpectedCellData); }); - test('should correctly update and clear input table row', async ({ page }) => { - const [opInputTablePage, operationalStudiesPage, routePage] = [ - new OperationalStudiesInputTablePage(page), - new OperationalStudiesPage(page), - new RoutePage(page), - ]; - - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; - + test('should correctly update and clear input table row', async () => { // Fill table cells with initial input data for (const cell of initialInputsData) { const translatedHeader = cleanWhitespace(translations[cell.header]); @@ -196,7 +183,6 @@ test.describe('Times and Stops Tab Verification', () => { cell.stationName, translatedHeader, cell.value, - OSRDLanguage, cell.marginForm ); } @@ -211,7 +197,6 @@ test.describe('Times and Stops Tab Verification', () => { cell.stationName, translatedHeader, cell.value, - OSRDLanguage, cell.marginForm ); } diff --git a/front/tests/012-op-simulation-settings-tab.spec.ts b/front/tests/012-op-simulation-settings-tab.spec.ts index ee813b1801c..99530abadda 100644 --- a/front/tests/012-op-simulation-settings-tab.spec.ts +++ b/front/tests/012-op-simulation-settings-tab.spec.ts @@ -9,7 +9,7 @@ import type { } from 'common/api/osrdEditoastApi'; import { improbableRollingStockName } from './assets/project-const'; -import HomePage from './pages/home-page-model'; +import test from './logging-fixture'; import OperationalStudiesInputTablePage from './pages/op-input-table-page-model'; import OperationalStudiesOutputTablePage from './pages/op-output-table-page-model'; import RoutePage from './pages/op-route-page-model'; @@ -18,21 +18,40 @@ import OperationalStudiesSimulationSettingsPage from './pages/op-simulation-sett import OperationalStudiesTimetablePage from './pages/op-timetable-page-model'; import OperationalStudiesPage from './pages/operational-studies-page-model'; import RollingStockSelectorPage from './pages/rollingstock-selector-page-model'; -import test from './test-logger'; -import { performOnSpecificOSAndBrowser, readJsonFile, waitForInfraStateToBeCached } from './utils'; +import { + getTranslations, + performOnSpecificOSAndBrowser, + readJsonFile, + waitForInfraStateToBeCached, +} from './utils'; import { deleteApiRequest, getInfra, setElectricalProfile } from './utils/api-setup'; -import { cleanWhitespace, type StationData } from './utils/dataNormalizer'; +import { cleanWhitespace } from './utils/dataNormalizer'; import createScenario from './utils/scenario'; import scrollContainer from './utils/scrollHelper'; import { deleteScenario } from './utils/teardown-utils'; +import type { StationData } from './utils/types'; import enTranslations from '../public/locales/en/timesStops.json'; import frTranslations from '../public/locales/fr/timesStops.json'; test.describe('Simulation Settings Tab Verification', () => { test.slow(); - // Set viewport to avoid scrolling issues and ensure elements are attached to the DOM test.use({ viewport: { width: 1920, height: 1080 } }); + let operationalStudiesPage: OperationalStudiesPage; + let rollingStockPage: RollingStockSelectorPage; + let routePage: RoutePage; + let opInputTablePage: OperationalStudiesInputTablePage; + let opOutputTablePage: OperationalStudiesOutputTablePage; + let opSimulationSettingsPage: OperationalStudiesSimulationSettingsPage; + let simulationResultPage: OpSimulationResultPage; + let opTimetablePage: OperationalStudiesTimetablePage; + let electricalProfileSet: ElectricalProfileSet; + let project: Project; + let study: Study; + let scenario: Scenario; + let infra: Infra; + let translations: typeof enTranslations | typeof frTranslations; + const expectedCellDataElectricalProfileON: StationData[] = readJsonFile( './tests/assets/operationStudies/simulationSettings/electricalProfiles/electricalProfileON.json' ); @@ -57,14 +76,7 @@ test.describe('Simulation Settings Tab Verification', () => { './tests/assets/operationStudies/simulationSettings/allSettings.json' ); - let electricalProfileSet: ElectricalProfileSet; - let project: Project; - let study: Study; - let scenario: Scenario; - let infra: Infra; - let OSRDLanguage: string; type TranslationKeys = keyof typeof enTranslations; - let stabilityTimeout: number; // Define CellData interface for table cell data interface CellData { @@ -77,6 +89,10 @@ test.describe('Simulation Settings Tab Verification', () => { test.beforeAll('Add electrical profile via API and fetch infrastructure', async () => { electricalProfileSet = await setElectricalProfile(); infra = await getInfra(); + translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); }); test.afterAll('Delete the electrical profile', async () => { @@ -87,15 +103,25 @@ test.describe('Simulation Settings Tab Verification', () => { test.beforeEach( 'Navigate to Times and Stops tab with rolling stock and route set', async ({ page }) => { - stabilityTimeout = 1000; - const [operationalStudiesPage, routePage, rollingStockPage, homePage] = [ + [ + operationalStudiesPage, + routePage, + rollingStockPage, + opInputTablePage, + opOutputTablePage, + opSimulationSettingsPage, + simulationResultPage, + opTimetablePage, + ] = [ new OperationalStudiesPage(page), new RoutePage(page), new RollingStockSelectorPage(page), - new HomePage(page), + new OperationalStudiesInputTablePage(page), + new OperationalStudiesOutputTablePage(page), + new OperationalStudiesSimulationSettingsPage(page), + new OpSimulationResultPage(page), + new OperationalStudiesTimetablePage(page), ]; - await homePage.goToHomePage(); - OSRDLanguage = await homePage.getOSRDLanguage(); // Create a new scenario ({ project, study, scenario } = await createScenario( undefined, @@ -109,16 +135,15 @@ test.describe('Simulation Settings Tab Verification', () => { await page.goto( `/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}` ); - await homePage.removeViteOverlay(); + await operationalStudiesPage.removeViteOverlay(); // Wait for infra to be in 'CACHED' state before proceeding await waitForInfraStateToBeCached(infra.id); // Add a new train and set its properties await operationalStudiesPage.clickOnAddTrainButton(); await operationalStudiesPage.setTrainScheduleName('Train-name-e2e-test'); - await page.waitForTimeout(stabilityTimeout); - await operationalStudiesPage.setTrainStartTime('11:22:40'); - // Select electric rolling stock await rollingStockPage.selectRollingStock(improbableRollingStockName); + await operationalStudiesPage.setTrainStartTime('11:22:40'); + // Perform pathfinding await operationalStudiesPage.clickOnRouteTab(); await routePage.performPathfindingByTrigram('WS', 'SES', 'MWS'); @@ -127,29 +152,12 @@ test.describe('Simulation Settings Tab Verification', () => { await scrollContainer(page, '.time-stops-datasheet .dsg-container'); } ); + test.afterEach('Delete the created scenario', async () => { await deleteScenario(project.id, study.id, scenario.name); }); test('Activate electrical profiles', async ({ page, browserName }) => { - const [ - operationalStudiesPage, - opInputTablePage, - opTimetablePage, - opOutputTablePage, - opSimulationSettingsPage, - simulationResultPage, - ] = [ - new OperationalStudiesPage(page), - new OperationalStudiesInputTablePage(page), - new OperationalStudiesTimetablePage(page), - new OperationalStudiesOutputTablePage(page), - new OperationalStudiesSimulationSettingsPage(page), - new OpSimulationResultPage(page), - ]; - - // Project selected language - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; const cell: CellData = { stationName: 'Mid_East_station', header: 'stopTime', @@ -161,8 +169,7 @@ test.describe('Simulation Settings Tab Verification', () => { await opInputTablePage.fillTableCellByStationAndHeader( cell.stationName, translatedHeader, - cell.value, - OSRDLanguage + cell.value ); // Activate electrical profiles await operationalStudiesPage.clickOnSimulationSettingsTab(); @@ -187,14 +194,13 @@ test.describe('Simulation Settings Tab Verification', () => { } ); await scrollContainer(page, '.time-stop-outputs .time-stops-datasheet .dsg-container'); - await opOutputTablePage.getOutputTableData(expectedCellDataElectricalProfileON, OSRDLanguage); + await opOutputTablePage.getOutputTableData(expectedCellDataElectricalProfileON); await opTimetablePage.clickOnTimetableCollapseButton(); // Deactivate electrical profiles and verify output results await opTimetablePage.clickOnEditTrain(); await operationalStudiesPage.clickOnSimulationSettingsTab(); await opSimulationSettingsPage.deactivateElectricalProfile(); await opTimetablePage.clickOnEditTrainSchedule(); - await page.waitForTimeout(stabilityTimeout); // Waiting for the timetable to update due to a slight latency await opTimetablePage.getTrainArrivalTime('11:52'); await opTimetablePage.clickOnScenarioCollapseButton(); await opOutputTablePage.verifyTimesStopsDataSheetVisibility(); @@ -210,26 +216,9 @@ test.describe('Simulation Settings Tab Verification', () => { actionName: 'visual assertion', } ); - await opOutputTablePage.getOutputTableData(expectedCellDataElectricalProfileOFF, OSRDLanguage); + await opOutputTablePage.getOutputTableData(expectedCellDataElectricalProfileOFF); }); test('Activate composition code', async ({ page, browserName }) => { - const [ - operationalStudiesPage, - opInputTablePage, - opTimetablePage, - opOutputTablePage, - opSimulationSettingsPage, - simulationResultPage, - ] = [ - new OperationalStudiesPage(page), - new OperationalStudiesInputTablePage(page), - new OperationalStudiesTimetablePage(page), - new OperationalStudiesOutputTablePage(page), - new OperationalStudiesSimulationSettingsPage(page), - new OpSimulationResultPage(page), - ]; - - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; const cell: CellData = { stationName: 'Mid_East_station', header: 'stopTime', @@ -240,8 +229,7 @@ test.describe('Simulation Settings Tab Verification', () => { await opInputTablePage.fillTableCellByStationAndHeader( cell.stationName, translatedHeader, - cell.value, - OSRDLanguage + cell.value ); // Select a specific composition code option await operationalStudiesPage.clickOnSimulationSettingsTab(); @@ -267,14 +255,13 @@ test.describe('Simulation Settings Tab Verification', () => { } ); await scrollContainer(page, '.time-stop-outputs .time-stops-datasheet .dsg-container'); - await opOutputTablePage.getOutputTableData(expectedCellDataCodeCompoON, OSRDLanguage); + await opOutputTablePage.getOutputTableData(expectedCellDataCodeCompoON); await opTimetablePage.clickOnTimetableCollapseButton(); // Remove the composition code option and verify the changes await opTimetablePage.clickOnEditTrain(); await operationalStudiesPage.clickOnSimulationSettingsTab(); await opSimulationSettingsPage.selectCodeCompoOption('__PLACEHOLDER__'); await opTimetablePage.clickOnEditTrainSchedule(); - await page.waitForTimeout(stabilityTimeout); await opTimetablePage.getTrainArrivalTime('11:52'); await opTimetablePage.clickOnScenarioCollapseButton(); await opOutputTablePage.verifyTimesStopsDataSheetVisibility(); @@ -290,26 +277,9 @@ test.describe('Simulation Settings Tab Verification', () => { actionName: 'visual assertion', } ); - await opOutputTablePage.getOutputTableData(expectedCellDataCodeCompoOFF, OSRDLanguage); + await opOutputTablePage.getOutputTableData(expectedCellDataCodeCompoOFF); }); test('Activate linear and mareco margin', async ({ page, browserName }) => { - const [ - operationalStudiesPage, - opInputTablePage, - opTimetablePage, - opOutputTablePage, - opSimulationSettingsPage, - simulationResultPage, - ] = [ - new OperationalStudiesPage(page), - new OperationalStudiesInputTablePage(page), - new OperationalStudiesTimetablePage(page), - new OperationalStudiesOutputTablePage(page), - new OperationalStudiesSimulationSettingsPage(page), - new OpSimulationResultPage(page), - ]; - - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; const inputTableData: CellData[] = [ { stationName: 'Mid_East_station', @@ -329,7 +299,7 @@ test.describe('Simulation Settings Tab Verification', () => { cell.stationName, translatedHeader, cell.value, - OSRDLanguage, + cell.marginForm ); } @@ -356,14 +326,13 @@ test.describe('Simulation Settings Tab Verification', () => { } ); await scrollContainer(page, '.time-stop-outputs .time-stops-datasheet .dsg-container'); - await opOutputTablePage.getOutputTableData(expectedCellDataLinearMargin, OSRDLanguage); + await opOutputTablePage.getOutputTableData(expectedCellDataLinearMargin); await opTimetablePage.clickOnTimetableCollapseButton(); // Modify the margin to 'Mareco' and verify the changes await opTimetablePage.clickOnEditTrain(); await operationalStudiesPage.clickOnSimulationSettingsTab(); await opSimulationSettingsPage.activateMarecoMargin(); await opTimetablePage.clickOnEditTrainSchedule(); - await page.waitForTimeout(stabilityTimeout); await opTimetablePage.getTrainArrivalTime('11:54'); await opTimetablePage.clickOnScenarioCollapseButton(); await opOutputTablePage.verifyTimesStopsDataSheetVisibility(); @@ -379,26 +348,9 @@ test.describe('Simulation Settings Tab Verification', () => { actionName: 'visual assertion', } ); - await opOutputTablePage.getOutputTableData(expectedCellDataMarecoMargin, OSRDLanguage); + await opOutputTablePage.getOutputTableData(expectedCellDataMarecoMargin); }); test('Add all the simulation settings', async ({ page, browserName }) => { - const [ - operationalStudiesPage, - opInputTablePage, - opTimetablePage, - opOutputTablePage, - opSimulationSettingsPage, - simulationResultPage, - ] = [ - new OperationalStudiesPage(page), - new OperationalStudiesInputTablePage(page), - new OperationalStudiesTimetablePage(page), - new OperationalStudiesOutputTablePage(page), - new OperationalStudiesSimulationSettingsPage(page), - new OpSimulationResultPage(page), - ]; - - const translations = OSRDLanguage === 'English' ? enTranslations : frTranslations; const inputTableData: CellData[] = [ { stationName: 'Mid_East_station', @@ -418,7 +370,7 @@ test.describe('Simulation Settings Tab Verification', () => { cell.stationName, translatedHeader, cell.value, - OSRDLanguage, + cell.marginForm ); } @@ -447,6 +399,6 @@ test.describe('Simulation Settings Tab Verification', () => { } ); await scrollContainer(page, '.time-stop-outputs .time-stops-datasheet .dsg-container'); - await opOutputTablePage.getOutputTableData(expectedCellDataForAllSettings, OSRDLanguage); + await opOutputTablePage.getOutputTableData(expectedCellDataForAllSettings); }); }); diff --git a/front/tests/013-stdcm-simulation-sheet.spec.ts b/front/tests/013-stdcm-simulation-sheet.spec.ts index 103d7d16c9d..8485ce0929e 100644 --- a/front/tests/013-stdcm-simulation-sheet.spec.ts +++ b/front/tests/013-stdcm-simulation-sheet.spec.ts @@ -6,26 +6,21 @@ 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 test from './logging-fixture'; +import STDCMPage from './pages/stdcm-page-model'; import { waitForInfraStateToBeCached } from './utils'; import { getInfra } from './utils/api-setup'; import { findFirstPdf, verifySimulationContent } from './utils/simulationSheet'; -import type { Simulation } from './utils/types'; +import type { ConsistFields, Simulation } from './utils/types'; -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 stdcmPage: STDCMPage; let infra: Infra; - let OSRDLanguage: string; + const consistDetails: ConsistFields = { tractionEngine: electricRollingStockName, tonnage: '950', @@ -44,22 +39,18 @@ test.describe('Verify stdcm simulation page', () => { }); 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(); + stdcmPage = new STDCMPage(page); await page.goto('/stdcm'); - await page.waitForLoadState('domcontentloaded', { timeout: 30_000 }); - await homePage.removeViteOverlay(); + await page.waitForLoadState('networkidle'); + await stdcmPage.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 }) => { + test('Verify STDCM stops and simulation sheet', async ({ browserName, context }) => { // Populate STDCM page with origin, destination, and via details - const stdcmPage = new STDCMPage(page); await stdcmPage.fillAndVerifyConsistDetails( consistDetails, tractionEnginePrefilledValues.tonnage, @@ -90,9 +81,8 @@ test.describe('Verify stdcm simulation page', () => { // 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(); + const newStdcmPage = new STDCMPage(newPage); + await newStdcmPage.verifyAllDefaultPageFields(); }); /** *************** Test 2 **************** */ @@ -107,7 +97,7 @@ test.describe('Verify stdcm simulation page', () => { const pdfBuffer = fs.readFileSync(pdfFilePath); const pdfData = await pdfParse(pdfBuffer); const pdfText = pdfData.text; - const expectedSimulation: Simulation = simulationSheetDetails(OSRDLanguage); + const expectedSimulation: Simulation = simulationSheetDetails(); verifySimulationContent(pdfText, expectedSimulation); }); }); diff --git a/front/tests/014-stdcm-linked-train.spec.ts b/front/tests/014-stdcm-linked-train.spec.ts index 6e78aab0b7b..37342a6559f 100644 --- a/front/tests/014-stdcm-linked-train.spec.ts +++ b/front/tests/014-stdcm-linked-train.spec.ts @@ -1,25 +1,20 @@ import type { Infra, TowedRollingStock } from 'common/api/osrdEditoastApi'; import { fastRollingStockName } from './assets/project-const'; -import HomePage from './pages/home-page-model'; +import test from './logging-fixture'; import STDCMLinkedTrainPage from './pages/stdcm-linked-train-page-model'; -import STDCMPage, { type ConsistFields } from './pages/stdcm-page-model'; -import test from './test-logger'; +import STDCMPage from './pages/stdcm-page-model'; import { waitForInfraStateToBeCached } from './utils'; import { getInfra, setTowedRollingStock } from './utils/api-setup'; +import type { ConsistFields } from './utils/types'; -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 stdcmLinkedTrainPage: STDCMLinkedTrainPage; + let infra: Infra; let createdTowedRollingStock: TowedRollingStock; let towedConsistDetails: ConsistFields; @@ -45,15 +40,11 @@ test.describe('Verify stdcm simulation page', () => { }); test.beforeEach('Navigate to the STDCM page', async ({ page }) => { - [homePage, stdcmPage, stdcmLinkedTrainPage] = [ - new HomePage(page), - new STDCMPage(page), - new STDCMLinkedTrainPage(page), - ]; + [stdcmPage, stdcmLinkedTrainPage] = [new STDCMPage(page), new STDCMLinkedTrainPage(page)]; // Navigate to STDCM page await page.goto('/stdcm'); - await page.waitForLoadState('domcontentloaded', { timeout: 30_000 }); - await homePage.removeViteOverlay(); + await page.waitForLoadState('networkidle'); + await stdcmPage.removeViteOverlay(); // Wait for infra to be in 'CACHED' state before proceeding await waitForInfraStateToBeCached(infra.id); diff --git a/front/tests/assets/simulation-sheet-const.ts b/front/tests/assets/simulation-sheet-const.ts index 337d7a035c3..d89f619f8d0 100644 --- a/front/tests/assets/simulation-sheet-const.ts +++ b/front/tests/assets/simulation-sheet-const.ts @@ -1,11 +1,14 @@ import enTranslations from '../../public/locales/en/stdcm-simulation-report-sheet.json'; import frTranslations from '../../public/locales/fr/stdcm-simulation-report-sheet.json'; +import { getTranslations } from '../utils'; import { getLocalizedDateString } from '../utils/date'; import type { Simulation } from '../utils/types'; -const simulationSheetDetails = (selectedLanguage: string): Simulation => { - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; - console.info(selectedLanguage); +const simulationSheetDetails = (): Simulation => { + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); return { header: { toolDescription: translations.warningMessage, @@ -13,7 +16,7 @@ const simulationSheetDetails = (selectedLanguage: string): Simulation => { }, applicationDate: translations.applicationDate, - applicationDateValue: getLocalizedDateString('2024-10-17', selectedLanguage), + applicationDateValue: getLocalizedDateString('2024-10-17'), trainDetails: { compositionCode: translations.speedLimitByTag, diff --git a/front/tests/assets/timeout-const.ts b/front/tests/assets/timeout-const.ts new file mode 100644 index 00000000000..3adc5660fed --- /dev/null +++ b/front/tests/assets/timeout-const.ts @@ -0,0 +1,4 @@ +export const LOAD_PAGE_TIMEOUT = 30_000; +export const SIMULATION_RESULT_TIMEOUT = 30_000; +export const STDCM_SIMULATION_TIMEOUT = 30_000; +export const EXPLICIT_UI_STABILITY_TIMEOUT = 1_000; diff --git a/front/tests/global-setup.ts b/front/tests/global-setup.ts index b469bd137e9..439355062bd 100644 --- a/front/tests/global-setup.ts +++ b/front/tests/global-setup.ts @@ -4,12 +4,13 @@ import ROLLING_STOCK_NAMES, { globalProjectName, trainScheduleProjectName, } from './assets/project-const'; -import { logger } from './test-logger'; +import { logger } from './logging-fixture'; +import HomePage from './pages/home-page-model'; import { getStdcmEnvironment } from './utils/api-setup'; import { createDataForTests } from './utils/setup-utils'; import { deleteProject, deleteRollingStocks } from './utils/teardown-utils'; -setup('setup', async () => { +setup('setup', async ({ page }) => { const stdcmEnvironment = await getStdcmEnvironment(); if (stdcmEnvironment) { process.env.STDCM_ENVIRONMENT = JSON.stringify(stdcmEnvironment); @@ -17,10 +18,15 @@ setup('setup', async () => { logger.info('Starting test data setup ...'); - await deleteProject(trainScheduleProjectName); - await deleteProject(globalProjectName); + await Promise.all([deleteProject(trainScheduleProjectName), deleteProject(globalProjectName)]); await deleteRollingStocks(ROLLING_STOCK_NAMES); await createDataForTests(); + + logger.info('Retrieving project language ...'); + const homePage = new HomePage(page); + await homePage.goToHomePage(); + process.env.PROJECT_LANGUAGE = await homePage.getOSRDLanguage(); + logger.info('Test data setup completed successfully.'); }); diff --git a/front/tests/global-teardown.ts b/front/tests/global-teardown.ts index 186a5e9eaa8..5933d7b3a1a 100644 --- a/front/tests/global-teardown.ts +++ b/front/tests/global-teardown.ts @@ -6,7 +6,7 @@ import ROLLING_STOCK_NAMES, { globalProjectName, trainScheduleProjectName, } from './assets/project-const'; -import { logger } from './test-logger'; +import { logger } from './logging-fixture'; import { setStdcmEnvironment } from './utils/api-setup'; import { deleteProject, deleteRollingStocks } from './utils/teardown-utils'; diff --git a/front/tests/test-logger.ts b/front/tests/logging-fixture.ts similarity index 100% rename from front/tests/test-logger.ts rename to front/tests/logging-fixture.ts diff --git a/front/tests/pages/common-page-model.ts b/front/tests/pages/common-page-model.ts index e9e3e5de948..8a027a5faf6 100644 --- a/front/tests/pages/common-page-model.ts +++ b/front/tests/pages/common-page-model.ts @@ -1,5 +1,7 @@ import { expect, type Locator, type Page } from '@playwright/test'; +import { logger } from '../logging-fixture'; + class CommonPage { readonly page: Page; @@ -44,7 +46,15 @@ class CommonPage { async closeToastNotification(): Promise { const closeToastElements = await this.closeToastButton.all(); - await Promise.all(closeToastElements.map((closeToastElement) => closeToastElement.click())); + await Promise.all( + closeToastElements.map(async (closeToastElement) => { + try { + await closeToastElement.click(); + } catch { + logger.warn('Toast disappeared before it could be clicked'); + } + }) + ); } } diff --git a/front/tests/pages/op-input-table-page-model.ts b/front/tests/pages/op-input-table-page-model.ts index ac9c782c29c..0219d0da094 100644 --- a/front/tests/pages/op-input-table-page-model.ts +++ b/front/tests/pages/op-input-table-page-model.ts @@ -2,6 +2,7 @@ import { type Locator, type Page, expect } from '@playwright/test'; import enTranslations from '../../public/locales/en/timesStops.json'; import frTranslations from '../../public/locales/fr/timesStops.json'; +import { getTranslations } from '../utils'; import { cleanWhitespace } from '../utils/dataNormalizer'; class OperationalStudiesInputTablePage { @@ -35,10 +36,12 @@ class OperationalStudiesInputTablePage { stationName: string, header: string, fillValue: string, - selectedLanguage: string, inputPlaceholder?: string ) { - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); const expectedColumnHeader = cleanWhitespace(header); @@ -61,7 +64,7 @@ class OperationalStudiesInputTablePage { .first(); await rowLocator.waitFor({ state: 'attached' }); const cell = rowLocator.locator('.dsg-cell').nth(columnIndex); - await cell.waitFor({ state: 'visible', timeout: 5000 }); + await cell.waitFor(); await cell.dblclick(); // Fill the input field based on the presence of a placeholder @@ -107,7 +110,7 @@ class OperationalStudiesInputTablePage { for (let rowIndex = 1; rowIndex < rowCount; rowIndex += 1) { const rowCells = this.tableRows.nth(rowIndex).locator('.dsg-cell .dsg-input'); - await rowCells.first().waitFor({ state: 'visible', timeout: 5000 }); + await rowCells.first().waitFor(); const rowValues = await rowCells.evaluateAll((cells) => cells.map((cell) => cell.getAttribute('value')) ); diff --git a/front/tests/pages/op-output-table-page-model.ts b/front/tests/pages/op-output-table-page-model.ts index 0fbe1f638be..efac168285b 100644 --- a/front/tests/pages/op-output-table-page-model.ts +++ b/front/tests/pages/op-output-table-page-model.ts @@ -3,7 +3,10 @@ import { type Locator, type Page, expect } from '@playwright/test'; import OperationalStudiesTimetablePage from './op-timetable-page-model'; import enTranslations from '../../public/locales/en/timesStops.json'; import frTranslations from '../../public/locales/fr/timesStops.json'; -import { normalizeData, type StationData } from '../utils/dataNormalizer'; +import { LOAD_PAGE_TIMEOUT } from '../assets/timeout-const'; +import { getTranslations } from '../utils'; +import { normalizeData } from '../utils/dataNormalizer'; +import type { StationData } from '../utils/types'; class OperationalStudiesOutputTablePage extends OperationalStudiesTimetablePage { readonly columnHeaders: Locator; @@ -36,16 +39,19 @@ class OperationalStudiesOutputTablePage extends OperationalStudiesTimetablePage return headerMap; } - async getOutputTableData(expectedTableData: StationData[], selectedLanguage: string) { + async getOutputTableData(expectedTableData: StationData[]) { const actualTableData: StationData[] = []; - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); const headerIndexMap = await this.getHeaderIndexMap(); const rowCount = await this.tableRows.count(); // Iterate through each active row and extract data based on header mappings for (let rowIndex = 1; rowIndex < rowCount; rowIndex += 1) { const row = this.tableRows.nth(rowIndex); - await row.waitFor({ state: 'visible' }); + await row.waitFor(); // Extract cells from the current row const cells = row.locator('.dsg-cell.dsg-cell-disabled'); @@ -142,11 +148,10 @@ class OperationalStudiesOutputTablePage extends OperationalStudiesTimetablePage expect(normalizedActualData).toEqual(normalizedExpectedData); } - // Wait for the Times and Stops simulation data sheet to be fully loaded with a specified timeout (default: 60 seconds) - async verifyTimesStopsDataSheetVisibility(timeout = 60 * 1000): Promise { - await this.timesStopsDataSheet.waitFor({ state: 'visible', timeout }); - await this.page.waitForTimeout(1000); // Short delay for stabilization - await this.timesStopsDataSheet.scrollIntoViewIfNeeded({ timeout }); + // Wait for the Times and Stops simulation data sheet to be fully loaded + async verifyTimesStopsDataSheetVisibility(): Promise { + await this.timesStopsDataSheet.waitFor({ timeout: LOAD_PAGE_TIMEOUT }); + await this.timesStopsDataSheet.scrollIntoViewIfNeeded(); } } diff --git a/front/tests/pages/op-route-page-model.ts b/front/tests/pages/op-route-page-model.ts index a5a5a6febc2..7248da53b04 100644 --- a/front/tests/pages/op-route-page-model.ts +++ b/front/tests/pages/op-route-page-model.ts @@ -2,7 +2,7 @@ import { expect, type Locator, type Page } from '@playwright/test'; import enTranslations from '../../public/locales/en/operationalStudies/manageTrainSchedule.json'; import frTranslations from '../../public/locales/fr/operationalStudies/manageTrainSchedule.json'; -import { clickWithDelay } from '../utils'; +import { getTranslations } from '../utils'; class RoutePage { readonly page: Page; @@ -51,6 +51,8 @@ class RoutePage { readonly missingParamMessage: Locator; + readonly pathfindingLoader: Locator; + constructor(page: Page) { this.page = page; @@ -76,6 +78,7 @@ class RoutePage { this.viaModal = page.locator('.manage-vias-modal'); this.closeViaModalButton = page.getByLabel('Close'); this.missingParamMessage = page.getByTestId('missing-params-info'); + this.pathfindingLoader = page.locator('.dots-loader'); } // Get the name locator of a waypoint suggestion. @@ -153,9 +156,12 @@ class RoutePage { await this.deleteItineraryButton.click(); } - // Verify that no route is selected and displays appropriate messages based on language. - async verifyNoSelectedRoute(selectedLanguage: string) { - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + // Verify that no route is selected and displays appropriate messages. + async verifyNoSelectedRoute() { + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); const isNoOriginChosenVisible = await this.noOriginChosen.isVisible(); const isNoDestinationChosenVisible = await this.noDestinationChosen.isVisible(); @@ -192,7 +198,7 @@ class RoutePage { const expectedDestinationTrigram = await this.getDestinationLocatorByTrigram(destinationTrigram).innerText(); await this.clickSearchByTrigramSubmitButton(); - await this.page.waitForSelector('.dots-loader', { state: 'hidden' }); + await this.pathfindingLoader.waitFor({ state: 'hidden' }); await expect(this.searchByTrigramContainer).not.toBeVisible(); await expect(this.resultPathfindingDone).toBeVisible(); @@ -206,24 +212,26 @@ class RoutePage { } // Click the buttons to delete origin, destination, and via waypoints and verifies missing parameters message. - async clickOnDeleteOPButtons(selectedLanguage: string) { + async clickOnDeleteOPButtons() { // Ensure all buttons are rendered and visible before proceeding await Promise.all([ - this.viaDeleteButton.waitFor({ state: 'visible' }), - this.originDeleteButton.waitFor({ state: 'visible' }), - this.destinationDeleteButton.waitFor({ state: 'visible' }), + this.viaDeleteButton.waitFor(), + this.originDeleteButton.waitFor(), + this.destinationDeleteButton.waitFor(), ]); - // Click the buttons sequentially with waits to ensure UI stability - await clickWithDelay(this.viaDeleteButton); - await clickWithDelay(this.originDeleteButton); - await clickWithDelay(this.destinationDeleteButton); - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + await this.viaDeleteButton.click(); + await this.originDeleteButton.click(); + await this.destinationDeleteButton.click(); + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); const expectedMessage = translations.pathfindingMissingParams.replace( ': {{missingElements}}.', '' ); - await this.missingParamMessage.waitFor({ state: 'visible' }); + await this.missingParamMessage.waitFor(); const actualMessage = await this.missingParamMessage.innerText(); expect(actualMessage).toContain(expectedMessage); } @@ -231,7 +239,7 @@ class RoutePage { // Click the add buttons for the specified via names. async clickOnViaAddButtons(...viaNames: string[]) { for (const viaName of viaNames) { - await clickWithDelay(this.getAddButtonLocatorByViaName(viaName)); + await this.getAddButtonLocatorByViaName(viaName).click(); await expect(this.getDeleteButtonLocatorByViaName(viaName)).toBeVisible(); } } diff --git a/front/tests/pages/op-timetable-page-model.ts b/front/tests/pages/op-timetable-page-model.ts index 2b9433a458a..2d7bf7e4c4a 100644 --- a/front/tests/pages/op-timetable-page-model.ts +++ b/front/tests/pages/op-timetable-page-model.ts @@ -1,9 +1,10 @@ import { type Locator, type Page, expect } from '@playwright/test'; +import CommonPage from './common-page-model'; import enTranslations from '../../public/locales/en/operationalStudies/scenario.json'; import frTranslations from '../../public/locales/fr/operationalStudies/scenario.json'; -import { clickWithDelay } from '../utils'; -import CommonPage from './common-page-model'; +import { EXPLICIT_UI_STABILITY_TIMEOUT, SIMULATION_RESULT_TIMEOUT } from '../assets/timeout-const'; +import { getTranslations } from '../utils'; class OperationalStudiesTimetablePage extends CommonPage { readonly invalidTrainsMessage: Locator; @@ -41,6 +42,8 @@ class OperationalStudiesTimetablePage extends CommonPage { readonly scenarioSideMenu: Locator; + readonly simulationResult: Locator; + constructor(page: Page) { super(page); this.invalidTrainsMessage = page.getByTestId('invalid-trains-message'); @@ -61,12 +64,7 @@ class OperationalStudiesTimetablePage extends CommonPage { this.scenarioCollapseButton = page.getByTestId('scenario-collapse-button'); this.timetableCollapseButton = page.getByTestId('timetable-collapse-button'); this.scenarioSideMenu = page.getByTestId('scenario-sidemenu'); - } - - // Function to wait for an element to be visible and then assert its visibility - static async waitForElementVisibility(locator: Locator): Promise { - await locator.waitFor({ state: 'visible', timeout: 30 * 1000 }); - await expect(locator).toBeVisible(); + this.simulationResult = page.locator('.simulation-results'); } // Get the button locator of a train element. @@ -74,14 +72,12 @@ class OperationalStudiesTimetablePage extends CommonPage { return trainSelector.getByTestId('scenario-timetable-train-button'); } - // Wait for the simulation results to be in the DOM - async waitForSimulationResults(): Promise { - await this.page.waitForSelector('.simulation-results', { state: 'attached' }); - } - // Verify that the message "The timetable contains invalid trains" is visible - async verifyInvalidTrainsMessageVisibility(selectedLanguage: string): Promise { - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + async verifyInvalidTrainsMessageVisibility(): Promise { + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); const invalidTrainsMessageText = await this.invalidTrainsMessage.innerText(); expect(invalidTrainsMessageText).toEqual(translations.timetable.invalidTrains); } @@ -94,10 +90,10 @@ class OperationalStudiesTimetablePage extends CommonPage { // Verify that simulation results are displayed async verifySimulationResultsVisibility(): Promise { - await this.page.waitForLoadState('networkidle'); + await this.page.waitForLoadState('networkidle', { timeout: SIMULATION_RESULT_TIMEOUT }); const simulationResultsLocators = [ - // TODO: remove this commented code when the design of simationBar has been changed + // TODO: remove this commented code when the design of simulation bar has been changed // this.simulationBar, this.manchetteSpaceTimeChart, this.speedSpaceChart, @@ -106,9 +102,9 @@ class OperationalStudiesTimetablePage extends CommonPage { this.timesStopsDataSheet, ]; await Promise.all( - simulationResultsLocators.map((simulationResultsLocator) => - OperationalStudiesTimetablePage.waitForElementVisibility(simulationResultsLocator) - ) + simulationResultsLocators.map(async (simulationResultsLocator) => { + await expect(simulationResultsLocator).toBeVisible(); + }) ); } @@ -130,12 +126,14 @@ class OperationalStudiesTimetablePage extends CommonPage { // Filter train using composition codes button based on the provided translation and verify train count async clickCodeCompoTrainFilterButton( - selectedLanguage: string, filterTranslation: string | null, expectedTrainCount: number ): Promise { await this.timetableFilterButton.click(); - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); const filterButtonLocator = !filterTranslation ? this.page.getByRole('button', { @@ -157,11 +155,13 @@ class OperationalStudiesTimetablePage extends CommonPage { // Filter trains validity and verify their count async filterValidityAndVerifyTrainCount( - selectedLanguage: string, validityFilter: 'Valid' | 'Invalid' | 'All', expectedTrainCount: number ): Promise { - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); await this.timetableFilterButton.click(); const validityFilters = { @@ -177,11 +177,13 @@ class OperationalStudiesTimetablePage extends CommonPage { // Filter the honored trains and verify their count async filterHonoredAndVerifyTrainCount( - selectedLanguage: string, honoredFilter: 'Honored' | 'Not honored' | 'All', expectedTrainCount: number ): Promise { - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); await this.timetableFilterButton.click(); const honoredFilters = { @@ -200,8 +202,8 @@ class OperationalStudiesTimetablePage extends CommonPage { const trainCount = await this.timetableTrains.count(); for (let currentTrainIndex = 0; currentTrainIndex < trainCount; currentTrainIndex += 1) { - await this.page.waitForLoadState('networkidle'); - await this.waitForSimulationResults(); + await this.page.waitForLoadState(); + await this.simulationResult.waitFor(); const trainButton = OperationalStudiesTimetablePage.getTrainButton( this.timetableTrains.nth(currentTrainIndex) ); @@ -210,9 +212,8 @@ class OperationalStudiesTimetablePage extends CommonPage { } } - async verifyTimesStopsDataSheetVisibility(timeout = 60 * 1000): Promise { - // Wait for the Times and Stops simulation dataSheet to be fully loaded with a specified timeout (default: 60 seconds) - await expect(this.timesStopsDataSheet).toBeVisible({ timeout }); + async verifyTimesStopsDataSheetVisibility(): Promise { + await expect(this.timesStopsDataSheet).toBeVisible({ timeout: SIMULATION_RESULT_TIMEOUT }); await this.timesStopsDataSheet.scrollIntoViewIfNeeded(); } @@ -228,19 +229,20 @@ class OperationalStudiesTimetablePage extends CommonPage { async getTrainArrivalTime(expectedArrivalTime: string) { await expect(this.trainArrivalTime).toBeVisible(); + await this.page.waitForTimeout(EXPLICIT_UI_STABILITY_TIMEOUT); const actualArrivalTime = await this.trainArrivalTime.textContent(); expect(actualArrivalTime).toEqual(expectedArrivalTime); } async clickOnScenarioCollapseButton() { await expect(this.scenarioCollapseButton).toBeVisible(); - await clickWithDelay(this.scenarioCollapseButton); + await this.scenarioCollapseButton.click(); await expect(this.scenarioSideMenu).toBeHidden(); } async clickOnTimetableCollapseButton() { await expect(this.timetableCollapseButton).toBeVisible(); - await clickWithDelay(this.timetableCollapseButton); + await this.timetableCollapseButton.click(); await expect(this.scenarioSideMenu).toBeVisible(); } } diff --git a/front/tests/pages/operational-studies-page-model.ts b/front/tests/pages/operational-studies-page-model.ts index af358675d6b..c067a793da2 100644 --- a/front/tests/pages/operational-studies-page-model.ts +++ b/front/tests/pages/operational-studies-page-model.ts @@ -93,8 +93,9 @@ class OperationalStudiesPage extends CommonPage { async setTrainStartTime(departureTime: string) { const currentDate = new Date().toISOString().split('T')[0]; const startTime = `${currentDate}T${departureTime}`; - await this.startTimeField.waitFor({ state: 'visible' }); + await this.startTimeField.waitFor(); await this.startTimeField.fill(startTime); + await this.startTimeField.dispatchEvent('blur'); await expect(this.startTimeField).toHaveValue(startTime); } @@ -107,7 +108,7 @@ class OperationalStudiesPage extends CommonPage { } async checkPathfindingDistance(distance: string | RegExp) { - await this.page.waitForSelector('[data-testid="result-pathfinding-distance"]'); + await this.resultPathfindingDistance.waitFor(); await expect(this.resultPathfindingDistance).toHaveText(distance); } diff --git a/front/tests/pages/project-page-model.ts b/front/tests/pages/project-page-model.ts index fe6f230b3c6..ba3cb130804 100644 --- a/front/tests/pages/project-page-model.ts +++ b/front/tests/pages/project-page-model.ts @@ -1,6 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; -import CommonPage from './common-page-model'; +import HomePage from './home-page-model'; import { cleanText } from '../utils/dataNormalizer'; // Define the type for project details @@ -13,7 +13,7 @@ type ProjectDetails = { tags: string[]; }; -class ProjectPage extends CommonPage { +class ProjectPage extends HomePage { readonly projectNameLabel: Locator; readonly updateProjectButton: Locator; diff --git a/front/tests/pages/stdcm-page-model.ts b/front/tests/pages/stdcm-page-model.ts index 22968ed2a0f..260c915628a 100644 --- a/front/tests/pages/stdcm-page-model.ts +++ b/front/tests/pages/stdcm-page-model.ts @@ -15,8 +15,11 @@ import { VIA_STOP_TIMES, VIA_STOP_TYPES, } from '../assets/stdcm-const'; -import { logger } from '../test-logger'; -import { handleAndVerifyInput, readJsonFile } from '../utils'; +import { EXPLICIT_UI_STABILITY_TIMEOUT, STDCM_SIMULATION_TIMEOUT } from '../assets/timeout-const'; +import { logger } from '../logging-fixture'; +import { getTranslations, handleAndVerifyInput, readJsonFile } from '../utils'; +import HomePage from './home-page-model'; +import type { ConsistFields } from '../utils/types'; interface TableRow { index: number; @@ -29,20 +32,7 @@ interface TableRow { refEngine: string | null; } -export interface ConsistFields { - tractionEngine: string; - towedRollingStock?: string; - tonnage?: string; - length?: string; - maxSpeed?: string; - speedLimitTag?: string; -} - -const MINIMUM_SIMULATION_NUMBER = 1; - -class STDCMPage { - readonly page: Page; - +class STDCMPage extends HomePage { readonly debugButton: Locator; readonly notificationHeader: Locator; @@ -174,7 +164,7 @@ class STDCMPage { readonly helpButton: Locator; constructor(page: Page) { - this.page = page; + super(page); this.notificationHeader = page.locator('#notification'); this.debugButton = page.getByTestId('stdcm-debug-button'); this.helpButton = page.getByTestId('stdcm-help-button'); @@ -251,6 +241,7 @@ class STDCMPage { this.allViasButton = page.getByTestId('all-vias-button'); this.retainSimulationButton = page.getByTestId('retain-simulation-button'); this.downloadSimulationButton = page.locator('.download-simulation a[download]'); + 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'); @@ -365,6 +356,7 @@ class STDCMPage { // Add a via card, verify fields, and delete it async addAndDeletedDefaultVia() { await this.addViaButton.click(); + await this.page.waitForTimeout(EXPLICIT_UI_STABILITY_TIMEOUT); // Wait for the animation to complete await expect(this.getViaCI(1)).toHaveValue(''); await expect(this.getViaCH(1)).toHaveValue(''); await expect(this.getViaType(1)).toHaveValue(VIA_STOP_TYPES.PASSAGE_TIME); @@ -507,8 +499,8 @@ class STDCMPage { await expect(this.toleranceOriginArrival).not.toBeVisible(); } - // Fill and verify destination details based on selected language - async fillAndVerifyDestinationDetails(selectedLanguage: string) { + // Fill and verify destination details + async fillAndVerifyDestinationDetails() { const { input, suggestion, @@ -519,7 +511,10 @@ class STDCMPage { arrivalType, updatedDetails, } = DESTINATION_DETAILS; - const translations = selectedLanguage === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); // Fill destination input and verify suggestions await this.dynamicDestinationCi.fill(input); await this.verifyDestinationSouthSuggestions(); @@ -599,15 +594,16 @@ class STDCMPage { async fillAndVerifyViaDetails({ viaNumber, ciSearchText, - language, }: { viaNumber: number; ciSearchText: string; - language?: string; }): Promise { const { PASSAGE_TIME, SERVICE_STOP, DRIVER_SWITCH } = VIA_STOP_TYPES; const { serviceStop, driverSwitch } = VIA_STOP_TIMES; - const translations = language === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); const warning = this.getViaWarning(viaNumber); // Helper function to fill common fields const fillVia = async (selectedSuggestion: Locator) => { @@ -653,18 +649,11 @@ class STDCMPage { // Launch the simulation and check if simulation-related elements are visible async launchSimulation(): Promise { - await this.launchSimulationButton.waitFor({ state: 'visible' }); + await this.launchSimulationButton.waitFor(); await expect(this.launchSimulationButton).toBeEnabled(); await this.launchSimulationButton.click({ force: true }); - // Wait for simulation elements to load and validate their presence - await this.simulationList.waitFor({ timeout: 60_000 }); - const simulationElements = await this.simulationList.all(); - - if (simulationElements.length < MINIMUM_SIMULATION_NUMBER) { - throw new Error( - `Expected at least ${MINIMUM_SIMULATION_NUMBER} simulation, but found ${simulationElements.length}.` - ); - } + // Wait for simulation message "Calculation completed" + await this.simulationStatus.waitFor({ timeout: STDCM_SIMULATION_TIMEOUT }); // Check map result container visibility only for Chromium browser if (this.page.context().browser()?.browserType().name() === 'chromium') { await expect(this.mapResultContainer).toBeVisible(); @@ -779,15 +768,16 @@ class STDCMPage { } async verifySimulationDetails({ - language, simulationNumber, simulationLengthAndDuration, }: { - language: string; simulationNumber: number; simulationLengthAndDuration?: string | null; }): Promise { - const translations = language === 'English' ? enTranslations : frTranslations; + const translations = getTranslations({ + en: enTranslations, + fr: frTranslations, + }); const noCapacityLengthAndDuration = '— '; // Determine expected simulation name const isResultTableVisible = await this.simulationResultTable.isVisible(); diff --git a/front/tests/utils/api-setup.ts b/front/tests/utils/api-setup.ts index 7bc923eee05..22513d7cca3 100644 --- a/front/tests/utils/api-setup.ts +++ b/front/tests/utils/api-setup.ts @@ -27,7 +27,7 @@ import type { import electricalProfileSet from '../assets/operationStudies/simulationSettings/electricalProfiles/electricalProfile.json'; import { globalProjectName, globalStudyName, infrastructureName } from '../assets/project-const'; import towedRollingStockData from '../assets/stdcm/towedRollingStock/towedRollingStock.json'; -import { logger } from '../test-logger'; +import { logger } from '../logging-fixture'; /** * Initialize a new API request context with the base URL. diff --git a/front/tests/utils/dataNormalizer.ts b/front/tests/utils/dataNormalizer.ts index 5f73a4e3a7c..42b9038baec 100644 --- a/front/tests/utils/dataNormalizer.ts +++ b/front/tests/utils/dataNormalizer.ts @@ -1,23 +1,4 @@ -interface Margin { - theoretical: string; - theoreticalS: string; - actual: string; - difference: string; -} - -export interface StationData { - stationName: string; - stationCh: string; - trackName: string; - requestedArrival: string; - requestedDeparture: string; - stopTime: string; - signalReceptionClosed: boolean; - shortSlipDistance: boolean; - margin: Margin; - calculatedArrival: string; - calculatedDeparture: string; -} +import type { StationData } from './types'; /** * Trim leading/trailing whitespace and replaces multiple spaces with a single space. diff --git a/front/tests/utils/date.ts b/front/tests/utils/date.ts index b65de99e0b8..c370f15f67f 100644 --- a/front/tests/utils/date.ts +++ b/front/tests/utils/date.ts @@ -5,21 +5,25 @@ 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'; +export function getLocalizedDateString(dateString: string): string { + const projectLanguage = process.env.PROJECT_LANGUAGE; + let locale: string; + switch (projectLanguage) { + case 'Français': + locale = 'fr-FR'; + break; + case 'English': + locale = 'en-GB'; + break; + default: + throw new Error(`Unsupported project language: "${projectLanguage}".`); + } const date = new Date(dateString); return new Intl.DateTimeFormat(locale, { weekday: 'long', @@ -37,3 +41,30 @@ export function getLocalizedDateString(dateString: string, language: string): st */ export const createDateInSpecialTimeZone = (dateString: string, timeZone: string) => dayjs.tz(dateString, timeZone); + +/** + * Convert a date string from YYYY-MM-DD format to "DD mmm YYYY" format. + * @param dateString - The input date string in YYYY-MM-DD format. + * @returns The formatted date string in "DD mmm YYYY" format. + */ +export function formatDateToDayMonthYear(dateString: string): string { + const projectLanguage = process.env.PROJECT_LANGUAGE; + let locale: string; + switch (projectLanguage) { + case 'Français': + locale = 'fr-FR'; + break; + case 'English': + locale = 'en-GB'; + break; + default: + throw new Error(`Unsupported project language: "${projectLanguage}".`); + } + const date = new Date(dateString); + const formattedDate = date.toLocaleDateString(locale, { + day: 'numeric', + month: 'short', + year: 'numeric', + }); + return formattedDate.replace('.', ''); +} diff --git a/front/tests/utils/index.ts b/front/tests/utils/index.ts index b7e25ff9562..2fe13b163da 100644 --- a/front/tests/utils/index.ts +++ b/front/tests/utils/index.ts @@ -4,7 +4,7 @@ import { type Locator, type Page, expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; import { getInfraById } from './api-setup'; -import { logger } from '../test-logger'; +import { logger } from '../logging-fixture'; /** * Fill the input field identified by ID or TestID with the specified value and verifies it. @@ -77,18 +77,6 @@ export async function extractNumberFromString(input: string): Promise { */ export const readJsonFile = (path: string) => JSON.parse(fs.readFileSync(path, 'utf8')); -/** - * Click on the specified element and waits for a specified delay after the click. - * - * @param element - locator object representing the element to click. - * @param delay - Optional. The delay in milliseconds to wait after clicking the element. Defaults to 500ms. - * - * @returns {Promise} - A promise that resolves after the element is clicked and the delay has passed. - */ -export async function clickWithDelay(element: Locator, delay = 500): Promise { - await element.click(); - await element.page().waitForTimeout(delay); -} /** * Generic function to handle input fields. * @@ -103,33 +91,18 @@ export async function handleAndVerifyInput(inputField: Locator, value?: string): await expect(inputField).toHaveValue(value); } } -/** - * Convert a date string from YYYY-MM-DD format to "DD mmm YYYY" format. - * @param dateString - The input date string in YYYY-MM-DD format. - * @param OSRDLanguage - The current language of the application - * @returns The formatted date string in "DD mmm YYYY" format. - */ -export function formatDateToDayMonthYear(dateString: string, OSRDLanguage: string): string { - const locale = OSRDLanguage === 'English' ? 'en-GB' : 'fr-FR'; - const date = new Date(dateString); - const formattedDate = date.toLocaleDateString(locale, { - day: 'numeric', - month: 'short', - year: 'numeric', - }); - return formattedDate.replace('.', ''); -} + /** * Waits until the infrastructure state becomes 'CACHED' before proceeding to the next step. - * The function polls the `infra.state` every 10 seconds, up to a total of 3 minutes. + * The function polls the `infra.state` every 10 seconds, up to a total of 60 seconds. * Displays the total time taken for the state to reach 'CACHED'. * * @param infraId - The ID of the infrastructure to retrieve and check. - * @throws {Error} - Throws an error if the state does not become 'CACHED' within 5 minutes. + * @throws {Error} - Throws an error if the state does not become 'CACHED' within 60 seconds. * @returns {Promise} - Resolves when the state is 'CACHED'. */ export const waitForInfraStateToBeCached = async (infraId: number): Promise => { - const maxRetries = 18; // Total attempts (3 minutes / 10 seconds) + const maxRetries = 6; // Total attempts (60 seconds / 10 seconds) const delay = 10000; // Delay in milliseconds (10 seconds) const startTime = Date.now(); // Record start time @@ -188,3 +161,24 @@ export async function performOnSpecificOSAndBrowser( console.info(skipMessage); } } + +/** + * Utility function to get translations based on the project language. + * + * @param {string} projectLanguage - The language of the project + * @param {{ en: T; fr: T }} translations - An object containing translations for English and French. + * @returns {T} - The translation object corresponding to the specified project language. + * @throws {Error} - Throws an error if the project language is unsupported. + */ + +export function getTranslations(translations: { en: T; fr: T }): T { + const projectLanguage = process.env.PROJECT_LANGUAGE; + switch (projectLanguage) { + case 'Français': + return translations.fr; + case 'English': + return translations.en; + default: + throw new Error(`Unsupported project language: "${projectLanguage}".`); + } +} diff --git a/front/tests/utils/scrollHelper.ts b/front/tests/utils/scrollHelper.ts index d58b604ad53..5d3fd9a29ce 100644 --- a/front/tests/utils/scrollHelper.ts +++ b/front/tests/utils/scrollHelper.ts @@ -20,7 +20,7 @@ const scrollContainer = async ( { stepSize = 300, timeout = 20, scrollOffsetThreshold = 200 }: ScrollOptions = {} ): Promise => { // Locate the scrollable container on the page - await page.waitForSelector(containerSelector, { state: 'visible' }); + await page.waitForSelector(containerSelector); const container = await page.evaluateHandle( (selector: string) => document.querySelector(selector), containerSelector diff --git a/front/tests/utils/setup-utils.ts b/front/tests/utils/setup-utils.ts index 7cf7b538b0b..1196688073a 100644 --- a/front/tests/utils/setup-utils.ts +++ b/front/tests/utils/setup-utils.ts @@ -28,7 +28,7 @@ import { trainScheduleScenarioName, trainScheduleStudyName, } from '../assets/project-const'; -import { logger } from '../test-logger'; +import { logger } from '../logging-fixture'; import { createDateInSpecialTimeZone } from './date'; /** diff --git a/front/tests/utils/teardown-utils.ts b/front/tests/utils/teardown-utils.ts index 8d9228e44d5..2a039eaf15e 100644 --- a/front/tests/utils/teardown-utils.ts +++ b/front/tests/utils/teardown-utils.ts @@ -11,7 +11,7 @@ import { getScenario, getProject, } from './api-setup'; -import { logger } from '../test-logger'; +import { logger } from '../logging-fixture'; /** * Delete infrastructure by name if it exists. diff --git a/front/tests/utils/types.ts b/front/tests/utils/types.ts index 214f5270083..155c3a0d2fe 100644 --- a/front/tests/utils/types.ts +++ b/front/tests/utils/types.ts @@ -1,3 +1,4 @@ +// STDCM simulation pdf content type export type Simulation = { header: { toolDescription: string; @@ -118,3 +119,35 @@ export type Simulation = { disclaimer: string; }; }; + +// STDCM consist block type +export type ConsistFields = { + tractionEngine: string; + towedRollingStock?: string; + tonnage?: string; + length?: string; + maxSpeed?: string; + speedLimitTag?: string; +}; + +type Margin = { + theoretical: string; + theoreticalS: string; + actual: string; + difference: string; +}; + +// STDCM simulation table type +export type StationData = { + stationName: string; + stationCh: string; + trackName: string; + requestedArrival: string; + requestedDeparture: string; + stopTime: string; + signalReceptionClosed: boolean; + shortSlipDistance: boolean; + margin: Margin; + calculatedArrival: string; + calculatedDeparture: string; +};