diff --git a/front/src/modules/rollingStock/components/RollingStockSelector/SearchRollingStock.tsx b/front/src/modules/rollingStock/components/RollingStockSelector/SearchRollingStock.tsx index 8c988af148a..4f612fcaff8 100644 --- a/front/src/modules/rollingStock/components/RollingStockSelector/SearchRollingStock.tsx +++ b/front/src/modules/rollingStock/components/RollingStockSelector/SearchRollingStock.tsx @@ -114,7 +114,7 @@ const SearchRollingStock = ({
- + {filteredRollingStockList.length > 0 ? `${filteredRollingStockList.length} ${t('resultsFound')}` : t('noResultFound')} diff --git a/front/tests/002-project-management.spec.ts b/front/tests/002-project-management.spec.ts index 3f4d9a8f7f5..d7af2910fb1 100644 --- a/front/tests/002-project-management.spec.ts +++ b/front/tests/002-project-management.spec.ts @@ -4,7 +4,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { Project } from 'common/api/osrdEditoastApi'; import projectData from './assets/operationStudies/project.json'; -import { deleteApiRequest, getApiRequest, postApiRequest } from './assets/utils'; +import { deleteApiRequest, getApiRequest, postApiRequest } from './utils/index'; import PlaywrightCommonPage from './pages/common-page-model'; import { PlaywrightHomePage } from './pages/home-page-model'; import { ProjectPage } from './pages/project-page-model'; @@ -60,7 +60,7 @@ test.describe('Test if operationnal study : project workflow is working properly expect(objectives).not.toEqual(null); if (objectives !== null) expect(objectives.replace(/[^A-Za-z0-9]/g, '')).toContain( - (project.objectives ?? "").replace(/[^A-Za-z0-9]/g, '') + (project.objectives ?? '').replace(/[^A-Za-z0-9]/g, '') ); expect(await projectPage.getProjectFinancialsInfos.textContent()).toContain( projectData.funders diff --git a/front/tests/003-study-management.spec.ts b/front/tests/003-study-management.spec.ts index 6176f2051c4..71e465a28e2 100644 --- a/front/tests/003-study-management.spec.ts +++ b/front/tests/003-study-management.spec.ts @@ -4,7 +4,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { Project, Study } from 'common/api/osrdEditoastApi'; import studyData from './assets/operationStudies/study.json'; -import { getProject, postApiRequest } from './assets/utils'; +import { getProject, postApiRequest } from './utils/index'; import PlaywrightCommonPage from './pages/common-page-model'; import { PlaywrightHomePage } from './pages/home-page-model'; import { StudyPage } from './pages/study-page-model'; diff --git a/front/tests/004-scenario-management.spec.ts b/front/tests/004-scenario-management.spec.ts index 1ec6ea95363..ac28d486721 100644 --- a/front/tests/004-scenario-management.spec.ts +++ b/front/tests/004-scenario-management.spec.ts @@ -4,7 +4,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { Infra, Project, Scenario, Study } from 'common/api/osrdEditoastApi'; import scenarioData from './assets/operationStudies/scenario.json'; -import { getInfra, getProject, getStudy, postApiRequest } from './assets/utils'; +import { getInfra, getProject, getStudy, postApiRequest } from './utils/index'; import PlaywrightCommonPage from './pages/common-page-model'; import { PlaywrightHomePage } from './pages/home-page-model'; import ScenarioPage from './pages/scenario-page-model'; diff --git a/front/tests/005-operational-studies.spec.ts b/front/tests/005-operational-studies.spec.ts index acf5e8073c5..d4b116d8b04 100644 --- a/front/tests/005-operational-studies.spec.ts +++ b/front/tests/005-operational-studies.spec.ts @@ -4,7 +4,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { Infra, Project, RollingStock, Scenario, Study } from 'common/api/osrdEditoastApi'; import scenarioData from './assets/operationStudies/scenario.json'; -import { getProject, getStudy, getRollingStock, postApiRequest, getInfra } from './assets/utils'; +import { getProject, getStudy, getRollingStock, postApiRequest, getInfra } from './utils/index'; import { PlaywrightHomePage } from './pages/home-page-model'; import RollingStockSelectorPage from './pages/rolling-stock-selector-page'; import PlaywrightScenarioPage from './pages/scenario-page-model'; diff --git a/front/tests/008-allowances.spec.ts b/front/tests/008-allowances.spec.ts index 12449995bd1..ed42048c6a7 100644 --- a/front/tests/008-allowances.spec.ts +++ b/front/tests/008-allowances.spec.ts @@ -1,7 +1,7 @@ import { test } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import createCompleteScenario, { allowancesManagement } from './assets/utils'; +import createCompleteScenario, { allowancesManagement } from './utils/scenario-utils'; import PlaywrightScenarioPage from './pages/scenario-page-model'; let scenarioName: string; diff --git a/front/tests/009-rollingstock-editor.spec.ts b/front/tests/009-rollingstock-editor.spec.ts index 8799aec2234..c3d87a481f5 100644 --- a/front/tests/009-rollingstock-editor.spec.ts +++ b/front/tests/009-rollingstock-editor.spec.ts @@ -4,11 +4,13 @@ import path from 'path'; import { test, expect } from '@playwright/test'; import { - findAndDeleteRollingStock, + findAndDeleteRollingStocks, generateUniqueName, verifyAndCheckInputById, fillAndCheckInputById, -} from './assets/utils'; + addRollingStock, +} from './utils/index'; +import RollingStockSelectorPage from './pages/rolling-stock-selector-page'; import PlaywrightRollingstockEditorPage from './pages/rollingstock-editor-page-model'; // Correct path to load rolling stock details from JSON @@ -16,7 +18,14 @@ const rollingstockDetailsPath = path.resolve( __dirname, '../tests/assets/rollingStock/rollingstockDetails.json' ); +// Correct path to load electrical and themal rolling stock from JSON +const rollingstockPath = path.resolve( + __dirname, + '../tests/assets/rollingStock/thermal-electric_rolling_stock.json' +); const rollingstockDetails = JSON.parse(fs.readFileSync(rollingstockDetailsPath, 'utf-8')); +const rollingStockJson = JSON.parse(fs.readFileSync(rollingstockPath, 'utf8')); +const thermalElectricRollingStockName = 'thermal-electric_rolling_stock'; test.describe('Rollingstock editor page', () => { let uniqueRollingStockName: string; @@ -29,16 +38,22 @@ test.describe('Rollingstock editor page', () => { uniqueDeletedRollingStockName = await generateUniqueName('D_RSN'); // Check and delete the specified rolling stocks if they exist - await findAndDeleteRollingStock(uniqueRollingStockName); - await findAndDeleteRollingStock(uniqueUpdatedRollingStockName); - await findAndDeleteRollingStock(uniqueDeletedRollingStockName); + await findAndDeleteRollingStocks([ + uniqueRollingStockName, + uniqueUpdatedRollingStockName, + uniqueDeletedRollingStockName, + thermalElectricRollingStockName, + ]); }); test.afterEach(async () => { // Clean up by deleting the created or updated rolling stock - await findAndDeleteRollingStock(uniqueRollingStockName); - await findAndDeleteRollingStock(uniqueUpdatedRollingStockName); - await findAndDeleteRollingStock(uniqueDeletedRollingStockName); + await findAndDeleteRollingStocks([ + uniqueRollingStockName, + uniqueUpdatedRollingStockName, + uniqueDeletedRollingStockName, + thermalElectricRollingStockName, + ]); }); test('should correctly create a new rolling stock', async ({ page }) => { @@ -189,4 +204,92 @@ test.describe('Rollingstock editor page', () => { rollingStockEditorPage.page.getByTestId(uniqueDeletedRollingStockName) ).toBeHidden(); }); + test('should correctly filter a rolling stock', async ({ page }) => { + const rollingStockEditorPage = new PlaywrightRollingstockEditorPage(page); + const rollingStockSelectorPage = new RollingStockSelectorPage(page); + // Navigate to rolling stock editor page + await rollingStockEditorPage.navigateToPage(); + + // Extract and check the initial count of rolling stock + const initialRollingStockFoundNumber = + await rollingStockSelectorPage.getRollingStockSearchNumber(); + + // Perform a filtering action for electric rolling stock + await rollingStockSelectorPage.electricRollingStockFilter(); + + // Verify that filtering reduces the count and all the RS have electic icons + expect(await rollingStockSelectorPage.getElectricRollingStockIcons.count()).toEqual( + await rollingStockSelectorPage.getRollingStockSearchNumber() + ); + + // Clear electric filter + await rollingStockSelectorPage.electricRollingStockFilter(); + + // Perform a filtering action for thermal rolling stock + await rollingStockSelectorPage.thermalRollingStockFilter(); + + // Verify that filtering reduces the count and all the RS have thermal icons + expect(await rollingStockSelectorPage.getThermalRollingStockIcons.count()).toEqual( + await rollingStockSelectorPage.getRollingStockSearchNumber() + ); + + // Perform a filtering action for combined thermal-electric rolling stock + await rollingStockSelectorPage.electricRollingStockFilter(); + + // Verify that filtering reduces the count and all the RS have thermal and electric icons + expect(await rollingStockSelectorPage.getThermalElectricRollingStockIcons.count()).toEqual( + await rollingStockSelectorPage.getRollingStockSearchNumber() + ); + + // Clear filters + await rollingStockSelectorPage.electricRollingStockFilter(); + await rollingStockSelectorPage.thermalRollingStockFilter(); + + // Verify that the count of rolling stock is back to the initial number + expect(await rollingStockSelectorPage.getRollingStockList.count()).toEqual( + initialRollingStockFoundNumber + ); + }); + + test('should correctly search for a rolling stock', async ({ page }) => { + const rollingStockEditorPage = new PlaywrightRollingstockEditorPage(page); + const rollingStockSelectorPage = new RollingStockSelectorPage(page); + // Add a rolling stock via postAPI + await addRollingStock(thermalElectricRollingStockName, rollingStockJson); + + // Navigate to rolling stock editor page + await rollingStockEditorPage.navigateToPage(); + + // Extract and check the initial count of rolling stock + const initialRollingStockFoundNumber = + await rollingStockSelectorPage.getRollingStockSearchNumber(); + + // Search for the specific rolling stock + await rollingStockEditorPage.searchRollingStock(thermalElectricRollingStockName); + expect( + rollingStockEditorPage.page.getByTestId( + `rollingstock-thermal-${thermalElectricRollingStockName}` + ) + ).toBeDefined(); + + // Verify that the first rolling stock has the thermal and electric icon + await expect(rollingStockSelectorPage.getThermalRollingStockFirstIcon).toBeVisible(); + await expect(rollingStockSelectorPage.getElectricRollingStockFirstIcon).toBeVisible(); + + // Clear the search + await rollingStockEditorPage.clearSearchRollingStock(); + + // Verify that the count of rolling stock is back to the initial number + expect(await rollingStockSelectorPage.getRollingStockList.count()).toEqual( + initialRollingStockFoundNumber + ); + // Search for a non existing rolling stock + await rollingStockEditorPage.searchRollingStock( + `${thermalElectricRollingStockName}-no-results` + ); + + // Verify that the count of rolling stock is 0 (No results Found) + await expect(rollingStockSelectorPage.getNoRollingStockResult).toBeVisible(); + expect(await rollingStockSelectorPage.getRollingStockSearchNumber()).toEqual(0); + }); }); diff --git a/front/tests/assets/rollingStock/thermal-electric_rolling_stock.json b/front/tests/assets/rollingStock/thermal-electric_rolling_stock.json new file mode 100644 index 00000000000..33b21da0a70 --- /dev/null +++ b/front/tests/assets/rollingStock/thermal-electric_rolling_stock.json @@ -0,0 +1,447 @@ +{ + "railjson_version": "3.2", + "name": "thermal-electric_rolling_stock", + "effort_curves": { + "modes": { + "25000V": { + "curves": [ + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "25000V", + "power_restriction_code": "C1" + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 450000.0, 445235.2941176471, 440470.5882352941, 435705.8823529412, + 430411.7647058823, 423264.70588235295, 416117.6470588235, 408970.5882352941, + 398484.1628959276, 383823.5294117647, 369162.89592760184, 348806.7226890756, + 328386.55462184874, 288330.8823529411, 210904.411764706, 192705.88235294115, + 186352.9411764706, 180000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "25000V", + "power_restriction_code": "C2" + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 400000.0, 395764.705882353, 391529.4117647059, 387294.11764705885, + 382588.2352941177, 376235.29411764705, 369882.3529411765, 363529.4117647059, + 354208.1447963801, 341176.4705882353, 328144.7963800905, 310050.42016806727, + 291899.1596638655, 256294.11764705877, 187470.5882352942, 171294.11764705885, + 165647.05882352943, 160000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "22500V", + "power_restriction_code": "C1" + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 405000.0, 400711.7647058824, 396423.5294117647, 392135.2941176471, + 387370.5882352941, 380938.2352941177, 374505.8823529412, 368073.5294117647, + 358635.74660633487, 345441.17647058825, 332246.6063348417, 313926.0504201681, + 295547.8991596639, 259497.794117647, 189813.9705882354, 173435.29411764705, + 167717.64705882355, 162000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "22500V", + "power_restriction_code": "C2" + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 360000.0, 356188.2352941177, 352376.4705882353, 348564.705882353, 344329.4117647059, + 338611.7647058824, 332894.11764705885, 327176.4705882353, 318787.3303167421, + 307058.82352941175, 295330.3167420815, 279045.3781512605, 262709.243697479, + 230664.7058823529, 168723.52941176482, 154164.70588235295, 149082.35294117648, + 144000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "20000V", + "power_restriction_code": "C1" + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 360000.0, 356188.2352941177, 352376.4705882353, 348564.70588235295, + 344329.4117647059, 338611.76470588235, 332894.11764705885, 327176.4705882353, + 318787.33031674207, 307058.8235294118, 295330.3167420815, 279045.37815126055, + 262709.24369747896, 230664.7058823529, 168723.5294117648, 154164.70588235295, + 149082.35294117648, 144000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "20000V", + "power_restriction_code": "C2" + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 320000.0, 316611.7647058824, 313223.52941176476, 309835.2941176471, + 306070.58823529416, 300988.23529411765, 295905.8823529412, 290823.52941176476, + 283366.51583710406, 272941.17647058825, 262515.83710407245, 248040.33613445383, + 233519.32773109243, 205035.29411764705, 149976.4705882354, 137035.29411764708, + 132517.64705882355, 128000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": null, + "power_restriction_code": "C1" + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 450000.0, 445235.2941176471, 440470.5882352941, 435705.8823529412, + 430411.7647058823, 423264.70588235295, 416117.6470588235, 408970.5882352941, + 398484.1628959276, 383823.5294117647, 369162.89592760184, 348806.7226890756, + 328386.55462184874, 288330.8823529411, 210904.411764706, 192705.88235294115, + 186352.9411764706, 180000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": null, + "power_restriction_code": "C2" + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 400000.0, 395764.705882353, 391529.4117647059, 387294.11764705885, + 382588.2352941177, 376235.29411764705, 369882.3529411765, 363529.4117647059, + 354208.1447963801, 341176.4705882353, 328144.7963800905, 310050.42016806727, + 291899.1596638655, 256294.11764705877, 187470.5882352942, 171294.11764705885, + 165647.05882352943, 160000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "25000V", + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 500000.0, 494705.8823529412, 489411.7647058823, 484117.6470588235, + 478235.29411764705, 470294.1176470588, 462352.9411764706, 454411.7647058823, + 442760.1809954751, 426470.5882352941, 410180.9954751131, 387563.025210084, + 364873.9495798319, 320367.64705882344, 234338.23529411777, 214117.64705882352, + 207058.82352941175, 200000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "22500V", + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 450000.0, 445235.2941176471, 440470.5882352941, 435705.8823529412, + 430411.7647058823, 423264.70588235295, 416117.6470588235, 408970.5882352941, + 398484.1628959276, 383823.5294117647, 369162.89592760184, 348806.7226890756, + 328386.55462184874, 288330.8823529411, 210904.411764706, 192705.88235294115, + 186352.9411764706, 180000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": "20000V", + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 5.294117647058823, 10.588235294117649, 15.882352941176473, 21.176470588235293, + 26.470588235294116, 31.764705882352946, 37.05882352941176, 42.35294117647059, + 47.64705882352941, 52.94117647058823, 58.23529411764706, 63.52941176470589, + 68.82352941176471, 74.11764705882352, 79.41176470588235, 84.70588235294117, 90.0 + ], + "max_efforts": [ + 400000.0, 395764.705882353, 391529.4117647059, 387294.11764705885, + 382588.2352941177, 376235.29411764705, 369882.3529411765, 363529.4117647059, + 354208.1447963801, 341176.4705882353, 328144.7963800905, 310050.42016806727, + 291899.1596638655, 256294.11764705877, 187470.5882352942, 171294.11764705885, + 165647.05882352943, 160000.0 + ] + } + }, + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": null, + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 5.277777777777778, 10.555555555555555, 15.833333333333332, 21.11111111111111, + 26.38888888888889, 31.666666666666664, 36.94444444444444, 42.22222222222222, + 47.77777777777778, 53.05555555555556, 58.33333333333333, 63.61111111111111, + 68.88888888888889, 74.16666666666667, 79.44444444444444, 84.72222222222221, 90.0 + ], + "max_efforts": [ + 500000.0, 494705.8823529412, 489411.7647058823, 484117.6470588235, + 478235.29411764705, 470294.1176470588, 462352.9411764706, 454411.7647058823, + 442760.1809954751, 426470.5882352941, 410180.9954751131, 387563.025210084, + 364873.9495798319, 320367.64705882344, 234338.23529411777, 214117.64705882352, + 207058.82352941175, 200000.0 + ] + } + }, + { + "cond": { + "comfort": "AC", + "electrical_profile_level": null, + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 5.277777777777778, 10.555555555555555, 15.833333333333332, 21.11111111111111, + 26.38888888888889, 31.666666666666664, 36.94444444444444, 42.22222222222222, + 47.77777777777778, 53.05555555555556, 58.33333333333333, 63.61111111111111, + 68.88888888888889, 74.16666666666667, 79.44444444444444, 84.72222222222221, 90.0 + ], + "max_efforts": [ + 510000.0, 504705.8823529412, 499411.7647058823, 494117.6470588235, + 488235.29411764705, 480294.1176470588, 472352.9411764706, 464411.7647058823, + 452760.1809954751, 436470.5882352941, 420180.9954751131, 397563.025210084, + 374873.9495798319, 330367.64705882344, 244338.23529411777, 224117.64705882352, + 217058.82352941175, 210000.0 + ] + } + }, + { + "cond": { + "comfort": "HEATING", + "electrical_profile_level": null, + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 5.277777777777778, 10.555555555555555, 15.833333333333332, 21.11111111111111, + 26.38888888888889, 31.666666666666664, 36.94444444444444, 42.22222222222222, + 47.77777777777778, 53.05555555555556, 58.33333333333333, 63.61111111111111, + 68.88888888888889, 74.16666666666667, 79.44444444444444, 84.72222222222221, 90.0 + ], + "max_efforts": [ + 490000.0, 484705.8823529412, 469411.7647058823, 474117.6470588235, + 468235.29411764705, 460294.1176470588, 452352.9411764706, 444411.7647058823, + 452760.1809954751, 416470.5882352941, 400180.9954751131, 377563.025210084, + 354873.9495798319, 310367.64705882344, 224338.23529411777, 204117.64705882352, + 197058.82352941175, 199000.0 + ] + } + } + ], + "default_curve": { + "speeds": [ + 0.0, 5.277777777777778, 10.555555555555555, 15.833333333333332, 21.11111111111111, + 26.38888888888889, 31.666666666666664, 36.94444444444444, 42.22222222222222, + 47.77777777777778, 53.05555555555556, 58.33333333333333, 63.61111111111111, + 68.88888888888889, 74.16666666666667, 79.44444444444444, 84.72222222222221, 90.0 + ], + "max_efforts": [ + 500000.0, 494705.8823529412, 489411.7647058823, 484117.6470588235, 478235.29411764705, + 470294.1176470588, 462352.9411764706, 454411.7647058823, 442760.1809954751, + 426470.5882352941, 410180.9954751131, 387563.025210084, 364873.9495798319, + 320367.64705882344, 234338.23529411777, 214117.64705882352, 207058.82352941175, 200000.0 + ] + }, + "is_electric": true + }, + "thermal": { + "curves": [ + { + "cond": { + "comfort": "STANDARD", + "electrical_profile_level": null, + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 2.7777777777777777, 5.555555555555555, 8.333333333333334, 11.11111111111111, + 13.88888888888889, 16.666666666666668, 19.444444444444443, 22.22222222222222, 25.0, + 27.77777777777778, 30.555555555555554, 33.333333333333336 + ], + "max_efforts": [ + 126000.0, 126000.0, 126000.0, 102750.0, 80340.0, 65379.99999999999, 54900.0, + 47210.0, 41360.0, 36790.0, 33120.0, 30110.0, 27600.0 + ] + } + }, + { + "cond": { + "comfort": "AC", + "electrical_profile_level": null, + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 1.3888888888888888, 5.555555555555555, 8.333333333333334, 11.11111111111111, + 13.88888888888889, 16.666666666666668, 19.444444444444443, 22.77777777777778, 25.0, + 27.77777777777778, 30.555555555555554, 33.333333333333336 + ], + "max_efforts": [ + 235000.0, 232500.0, 224000.0, 219000.0, 213500.0, 208000.0, 203000.0, 198000.0, + 192000.0, 175000.0, 157500.0, 143000.0, 131000.0 + ] + } + }, + { + "cond": { + "comfort": "HEATING", + "electrical_profile_level": null, + "power_restriction_code": null + }, + "curve": { + "speeds": [ + 0.0, 1.3888888888888888, 5.555555555555555, 8.333333333333334, 11.11111111111111, + 13.88888888888889, 16.666666666666668, 19.444444444444443, 22.77777777777778, 25.0, + 27.77777777777778, 30.555555555555554 + ], + "max_efforts": [ + 235000.0, 232500.0, 224000.0, 219000.0, 213500.0, 208000.0, 203000.0, 198000.0, + 192000.0, 175000.0, 157500.0, 143000.0 + ] + } + } + ], + "default_curve": { + "speeds": [ + 0.0, 2.7777777777777777, 5.555555555555555, 8.333333333333334, 11.11111111111111, + 13.88888888888889, 16.666666666666668, 19.444444444444443, 22.22222222222222, 25.0, + 27.77777777777778, 30.555555555555554, 33.333333333333336 + ], + "max_efforts": [ + 126000.0, 126000.0, 126000.0, 102750.0, 80340.0, 65379.99999999999, 54900.0, 47210.0, + 41360.0, 36790.0, 33120.0, 30110.0, 27600.0 + ] + }, + "is_electric": false + } + }, + "default_mode": "thermal" + }, + "metadata": { + "detail": "thermo-electric", + "family": "", + "type": "", + "grouping": "", + "series": "", + "subseries": "", + "unit": "", + "number": "", + "reference": "thermo-electric" + }, + "length": 350.0, + "max_speed": 44.44444444444444, + "startup_time": 12.0, + "startup_acceleration": 0.06, + "comfort_acceleration": 0.54, + "gamma": { + "type": "CONST", + "value": 0.5 + }, + "inertia_coefficient": 1.05, + "base_power_class": "5", + "mass": 900000.0, + "rolling_resistance": { + "type": "davis", + "A": 4400.0, + "B": 195.67674, + "C": 12.00002688 + }, + "loading_gauge": "G1", + "power_restrictions": { + "C2": "1", + "C1": "3" + }, + "energy_sources": [], + "locked": false, + "electrical_power_startup_time": 6.0, + "raise_pantograph_time": 16.0, + "version": 1, + "supported_signaling_systems": ["BAL", "BAPR", "TVM300", "TVM430"], + "liveries": [] +} diff --git a/front/tests/assets/utils.ts b/front/tests/assets/utils.ts deleted file mode 100644 index f0a2e117eb4..00000000000 --- a/front/tests/assets/utils.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { type Page, request, expect } from '@playwright/test'; -import { v4 as uuidv4 } from 'uuid'; - -import type { Project, Scenario, Study, RollingStock, Infra } from 'common/api/osrdEditoastApi'; - -import scenarioData from './operationStudies/scenario.json'; -import { PlaywrightHomePage } from '../pages/home-page-model'; -import RollingStockSelectorPage from '../pages/rolling-stock-selector-page'; -import PlaywrightScenarioPage from '../pages/scenario-page-model'; -// API requests - -const getApiContext = async () => - request.newContext({ - baseURL: 'http://localhost:4000', - }); - -export const getApiRequest = async ( - url: string, - params?: { [key: string]: string | number | boolean } -) => { - const apiContext = await getApiContext(); - const response = await apiContext.get(url, { params }); - return response.json(); -}; - -export const postApiRequest = async ( - url: string, - data?: T, - params?: { [key: string]: string | number | boolean } -) => { - const apiContext = await getApiContext(); - const response = await apiContext.post(url, { data, params }); - return response.json(); -}; - -export const deleteApiRequest = async (url: string) => { - const apiContext = await getApiContext(); - const response = await apiContext.delete(url); - return response; -}; - -// API calls for beforeAll setup in tests - -export const findOneInResults = (results: T[], name: string) => - results.find((result) => result.name === name); - -export const getInfra = async () => { - const { results } = await getApiRequest(`/api/infra/`); - const infra = findOneInResults(results, 'small_infra_test_e2e') as Infra; - return infra; -}; - -export const getProject = async () => { - const { results } = await getApiRequest(`/api/projects/`); - const project = findOneInResults(results, 'project_test_e2e') as Project; - return project; -}; - -export const getStudy = async (projectId: number) => { - const { results } = await getApiRequest(`/api/projects/${projectId}/studies/`); - const study = findOneInResults(results, 'study_test_e2e') as Study; - return study; -}; - -export const getScenario = async (projectId: number, studyId: number) => { - const { results } = await getApiRequest( - `/api/projects/${projectId}/studies/${studyId}/scenarios/` - ); - const scenario = findOneInResults(results, 'scenario_test_e2e') as Scenario; - return scenario; -}; - -export const getRollingStock = async () => { - const { results } = await getApiRequest(`/api/light_rolling_stock/`, { page_size: 500 }); - const rollingStock = findOneInResults( - results, - 'rollingstock_1500_25000_test_e2e' - ) as RollingStock; - return rollingStock; -}; - -// Find and delete rolling stock with the given name -export async function findAndDeleteRollingStock(rollingStockName: string) { - const rollingStocks = await getApiRequest(`/api/light_rolling_stock/`, { page_size: 500 }); - - const rollingStockToDeleteCreated = rollingStocks.results.find( - (r: RollingStock) => r.name === rollingStockName - ); - if (rollingStockToDeleteCreated) { - await deleteApiRequest(`/api/rolling_stock/${rollingStockToDeleteCreated.id}/`); - } -} - -// Fill and check input by ID -// Note: This method check if the locator uses ID or TestID and fills it with the input value -export async function fillAndCheckInputById( - page: Page, - inputId: string, - value: string | number, - isTestId = false -) { - const input = isTestId ? page.getByTestId(inputId) : page.locator(`#${inputId}`); - - await input.click(); - await input.fill(`${value}`); - expect(await input.inputValue()).toBe(`${value}`); -} - -// Verify input by ID -// Note: This method check if the locator uses ID or TestID and verifies its content -export async function verifyAndCheckInputById( - page: Page, - inputId: string, - expectedValue: string | number, - isTestId = false -) { - const input = isTestId ? page.getByTestId(inputId) : page.locator(`#${inputId}`); - - expect(await input.inputValue()).toContain(`${expectedValue}`); -} - -// Generate unique name (used for creating rolling stock) -export const generateUniqueName = async (baseName: string) => { - // Generate a UUID and truncate it to 6 characters - const uuidSegment = uuidv4().slice(0, 6); - return `${baseName}-${uuidSegment}`; -}; -// Scenario creation -export default async function createCompleteScenario( - page: Page, - trainScheduleName: string, - trainCount: string, - delta: string -) { - const smallInfra = (await getInfra()) as Infra; - const project = await getProject(); - const study = await getStudy(project.id); - const rollingStock = await getRollingStock(); - - const scenario = await postApiRequest( - `/api/projects/${project.id}/studies/${study.id}/scenarios`, - { - ...scenarioData, - name: `${scenarioData.name} ${uuidv4()}`, - study_id: study.id, - infra_id: smallInfra.id, - } - ); - - const playwrightHomePage = new PlaywrightHomePage(page); - const scenarioPage = new PlaywrightScenarioPage(page); - - await page.goto( - `/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}` - ); - - await playwrightHomePage.page.getByTestId('scenarios-add-train-schedule-button').click(); - - await scenarioPage.setTrainScheduleName(trainScheduleName); - await scenarioPage.setNumberOfTrains(trainCount); - await scenarioPage.setDelta(delta); - - // ***************** Select Rolling Stock ***************** - const playwrightRollingstockModalPage = new RollingStockSelectorPage(playwrightHomePage.page); - await playwrightRollingstockModalPage.openRollingstockModal(); - - await playwrightRollingstockModalPage.searchRollingstock('rollingstock_1500_25000_test_e2e'); - - const rollingstockCard = playwrightRollingstockModalPage.getRollingstockCardByTestID( - `rollingstock-${rollingStock.name}` - ); - - await rollingstockCard.click(); - await rollingstockCard.locator('button').click(); - - // ***************** Select Origin/Destination ***************** - await scenarioPage.openTabByDataId('tab-pathfinding'); - await scenarioPage.getPathfindingByTriGramSearch('MWS', 'NES'); - - // ***************** Create train ***************** - await scenarioPage.addTrainSchedule(); - await scenarioPage.page.waitForSelector('.dots-loader', { state: 'hidden' }); - await scenarioPage.checkTrainHasBeenAdded(); - await scenarioPage.returnSimulationResult(); -} - -// Allowances management - -export async function allowancesManagement( - scenarioPage: PlaywrightScenarioPage, - scenarioName: string, - allowanceType: 'standard' | 'engineering' -) { - await expect(scenarioPage.getTimetableList).toBeVisible(); - - await scenarioPage.getBtnByName(scenarioName).hover(); - await scenarioPage.page.getByTestId('edit-train').click(); - - await scenarioPage.openAllowancesModule(); - await expect(scenarioPage.getAllowancesModule).toBeVisible(); - - if (allowanceType === 'standard') { - await scenarioPage.setStandardAllowance(); - } else { - await scenarioPage.setEngineeringAllowance(); - await scenarioPage.clickSuccessBtn(); - await expect(scenarioPage.getAllowancesEngineeringSettings).toHaveAttribute('disabled'); - } - - await scenarioPage.page.getByTestId('submit-edit-train-schedule').click(); - await scenarioPage.page.waitForSelector('.scenario-details-name'); - expect(await scenarioPage.isAllowanceWorking()).toEqual(true); - - // TODO: check if the allowances are taken into account in the scenario page (waiting for issue # 6695 to be fixed) -} diff --git a/front/tests/global-setup.ts b/front/tests/global-setup.ts index b288b60730d..837d607eaa3 100644 --- a/front/tests/global-setup.ts +++ b/front/tests/global-setup.ts @@ -13,7 +13,7 @@ import type { import projectData from './assets/operationStudies/project.json'; import scenarioData from './assets/operationStudies/scenario.json'; import studyData from './assets/operationStudies/study.json'; -import { getApiRequest, postApiRequest } from './assets/utils'; +import { getApiRequest, postApiRequest } from './utils/index'; async function createDataForTests() { const smallInfraRailjson: RailJson = JSON.parse( diff --git a/front/tests/global-teardown.ts b/front/tests/global-teardown.ts index 583106229b8..530ffb8a506 100644 --- a/front/tests/global-teardown.ts +++ b/front/tests/global-teardown.ts @@ -2,7 +2,7 @@ import { test as setup } from '@playwright/test'; import type { Infra, Project, RollingStock } from 'common/api/osrdEditoastApi'; -import { deleteApiRequest, getApiRequest } from './assets/utils'; +import { deleteApiRequest, getApiRequest } from './utils/index'; setup('teardown', async () => { const infras = await getApiRequest(`/api/infra/`); diff --git a/front/tests/pages/rolling-stock-selector-page.ts b/front/tests/pages/rolling-stock-selector-page.ts index 68c5f9bf6a4..965f92acf16 100644 --- a/front/tests/pages/rolling-stock-selector-page.ts +++ b/front/tests/pages/rolling-stock-selector-page.ts @@ -1,9 +1,7 @@ import { type Locator, type Page, expect } from '@playwright/test'; import BasePage from './base-page'; -import rollingstockTranslation from '../../public/locales/fr/rollingstock.json'; - -const electricCheckboxTranslation = rollingstockTranslation.electric; +import { extractNumberFromString } from '../utils/index'; export default class RollingStockSelectorPage extends BasePage { readonly rollingStockSelectorButton: Locator; @@ -16,15 +14,31 @@ export default class RollingStockSelectorPage extends BasePage { readonly rollingStockListItem: Locator; - readonly getRollingStockSearch: Locator; - - readonly getRollingStockSearchFilter: Locator; + readonly getRollingStockModalSearch: Locator; readonly rollingStockMiniCards: Locator; readonly getRollingstockSpanNames: Locator; - readonly electricalCheckbox: Locator; + readonly getElectricRollingStockFilter: Locator; + + readonly getThermalRollingStockFilter: Locator; + + readonly getRollingStockSearchResult: Locator; + + readonly getThermalRollingStockIcons: Locator; + + readonly getElectricRollingStockIcons: Locator; + + readonly getElectricRollingStockFirstIcon: Locator; + + readonly getThermalRollingStockFirstIcon: Locator; + + readonly getRollingStockList: Locator; + + readonly getThermalElectricRollingStockIcons: Locator; + + readonly getNoRollingStockResult: Locator; constructor(page: Page) { super(page); @@ -35,13 +49,23 @@ export default class RollingStockSelectorPage extends BasePage { this.rollingStockListItem = page.locator('.rollingstock-container'); this.getResultsFound = page.locator('.modal-dialog').locator('small').first(); - this.getRollingStockSearch = this.rollingStockSelectorModal.locator('#searchfilter'); - this.getRollingStockSearchFilter = page.locator('.rollingstock-search-filters'); + this.getRollingStockModalSearch = this.rollingStockSelectorModal.locator('#searchfilter'); this.rollingStockMiniCards = page.locator('.rollingstock-selector-minicard'); this.getRollingstockSpanNames = page.locator('.rollingstock-minicard-name'); - this.electricalCheckbox = this.rollingStockSelectorModal.locator('label').filter({ - hasText: electricCheckboxTranslation, - }); + this.getElectricRollingStockFilter = page.locator('label[for="elec"]'); + this.getThermalRollingStockFilter = page.locator('label[for="thermal"]'); + this.getRollingStockSearchResult = page.getByTestId('search-results-text'); + this.getThermalRollingStockIcons = page.locator('.rollingstock-footer-specs .text-pink'); + this.getElectricRollingStockIcons = page.locator('.rollingstock-footer-specs .text-primary'); + this.getThermalElectricRollingStockIcons = page + .locator('.rollingstock-footer-specs .rollingstock-tractionmode:has(.text-pink)') + .filter({ + has: page.locator('.text-primary'), + }); + this.getElectricRollingStockFirstIcon = this.getElectricRollingStockIcons.first(); + this.getThermalRollingStockFirstIcon = this.getThermalRollingStockIcons.first(); + this.getRollingStockList = page.locator('.rollingstock-editor-list .rollingstock-title'); + this.getNoRollingStockResult = page.locator('.rollingstock-empty'); } async openRollingstockModal() { @@ -54,11 +78,11 @@ export default class RollingStockSelectorPage extends BasePage { } async searchRollingstock(rollingstockName: string) { - await this.getRollingStockSearch.fill(rollingstockName); + await this.getRollingStockModalSearch.fill(rollingstockName); } async selectRollingStock(rollingStockName: string) { - await this.getRollingStockSearch.fill(rollingStockName); + await this.getRollingStockModalSearch.fill(rollingStockName); const rollingstockItem = this.rollingStockList.getByTestId(`rollingstock-${rollingStockName}`); await rollingstockItem.click(); await rollingstockItem.locator('.rollingstock-footer-buttons > button').click(); @@ -79,4 +103,19 @@ export default class RollingStockSelectorPage extends BasePage { async closeRollingstockModal() { await this.rollingStockSelectorModal.locator('.close').click(); } + + // Select Combustion engine RS filter + async thermalRollingStockFilter() { + await this.getThermalRollingStockFilter.click(); + } + + // Select Electic RS filter + async electricRollingStockFilter() { + await this.getElectricRollingStockFilter.click(); + } + + // Get the number of RS from the search result text + async getRollingStockSearchNumber(): Promise { + return extractNumberFromString(await this.getRollingStockSearchResult.innerText()); + } } diff --git a/front/tests/pages/rollingstock-editor-page-model.ts b/front/tests/pages/rollingstock-editor-page-model.ts index ad41570d65e..62e4cd9e1f8 100644 --- a/front/tests/pages/rollingstock-editor-page-model.ts +++ b/front/tests/pages/rollingstock-editor-page-model.ts @@ -1,7 +1,7 @@ import { expect, type Locator, type Page } from '@playwright/test'; import PlaywrightCommonPage from './common-page-model'; -import { fillAndCheckInputById } from '../assets/utils'; +import { fillAndCheckInputById } from '../utils/index'; export default class PlaywrightRollingstockEditorPage extends PlaywrightCommonPage { readonly getNewRollingstockButton: Locator; @@ -71,6 +71,8 @@ export default class PlaywrightRollingstockEditorPage extends PlaywrightCommonPa // Navigate to the Rolling Stock Editor Page async navigateToPage() { await this.page.goto('/rolling-stock-editor/'); + // Wait for the page to reach the network idle state + await this.page.waitForLoadState('networkidle'); await this.removeViteOverlay(); } @@ -162,6 +164,7 @@ export default class PlaywrightRollingstockEditorPage extends PlaywrightCommonPa } // Set spreadsheet row value + // TODO: Refactor to eliminate ESLint errors async setSpreadsheetRow(data: { row: number; velocity: string; effort: string }[]) { for (const { row, effort, velocity } of data) { const velocityCell = this.getVelocityCellByRow(row); @@ -222,6 +225,7 @@ export default class PlaywrightRollingstockEditorPage extends PlaywrightCommonPa .getByTitle(powerRestrictionValue, { exact: true }) .click(); } + // TODO: Refactor to eliminate ESLint errors for (const rowData of data) { const rowIndex = data.indexOf(rowData) + 1; const velocityCell = this.getVelocityCellByRow(rowIndex); @@ -242,7 +246,7 @@ export default class PlaywrightRollingstockEditorPage extends PlaywrightCommonPa .getByRole('button', { name: powerRestrictionValue }) .click(); } - + // TODO: Refactor to eliminate ESLint errors for (const rowData of expectedData) { const rowIndex = expectedData.indexOf(rowData) + 1; const velocityCell = await this.getVelocityCellByRowValue(rowIndex); diff --git a/front/tests/utils/index.ts b/front/tests/utils/index.ts new file mode 100644 index 00000000000..5161adf8c98 --- /dev/null +++ b/front/tests/utils/index.ts @@ -0,0 +1,139 @@ +// TODO: Dispatch the functions in differents files + +import { type Page, request, expect } from '@playwright/test'; +import { v4 as uuidv4 } from 'uuid'; + +import type { Project, Scenario, Study, RollingStock, Infra } from 'common/api/osrdEditoastApi'; + +// API requests + +const getApiContext = async () => + request.newContext({ + baseURL: 'http://localhost:4000', + }); + +export const getApiRequest = async ( + url: string, + params?: { [key: string]: string | number | boolean } +) => { + const apiContext = await getApiContext(); + const response = await apiContext.get(url, { params }); + return response.json(); +}; + +export const postApiRequest = async ( + url: string, + data?: T, + params?: { [key: string]: string | number | boolean } +) => { + const apiContext = await getApiContext(); + const response = await apiContext.post(url, { data, params }); + return response.json(); +}; + +export const deleteApiRequest = async (url: string) => { + const apiContext = await getApiContext(); + const response = await apiContext.delete(url); + return response; +}; + +// API calls for beforeAll setup in tests + +export const findOneInResults = (results: T[], name: string) => + results.find((result) => result.name === name); + +export const getInfra = async () => { + const { results } = await getApiRequest(`/api/infra/`); + const infra = findOneInResults(results, 'small_infra_test_e2e') as Infra; + return infra; +}; + +export const getProject = async () => { + const { results } = await getApiRequest(`/api/projects/`); + const project = findOneInResults(results, 'project_test_e2e') as Project; + return project; +}; + +export const getStudy = async (projectId: number) => { + const { results } = await getApiRequest(`/api/projects/${projectId}/studies/`); + const study = findOneInResults(results, 'study_test_e2e') as Study; + return study; +}; + +export const getScenario = async (projectId: number, studyId: number) => { + const { results } = await getApiRequest( + `/api/projects/${projectId}/studies/${studyId}/scenarios/` + ); + const scenario = findOneInResults(results, 'scenario_test_e2e') as Scenario; + return scenario; +}; + +export const getRollingStock = async () => { + const { results } = await getApiRequest(`/api/light_rolling_stock/`, { page_size: 500 }); + const rollingStock = findOneInResults( + results, + 'rollingstock_1500_25000_test_e2e' + ) as RollingStock; + return rollingStock; +}; +// Add a rolling Stock +export async function addRollingStock(rollingStockName: string, rollingStockJson: JSON) { + await postApiRequest('/api/rolling_stock/', { + ...rollingStockJson, + name: rollingStockName, + }); +} +// Find and delete rolling stock with the given name +export async function findAndDeleteRollingStocks(rollingStockNames: string[]) { + const rollingStocks = await getApiRequest(`/api/light_rolling_stock/`, { page_size: 500 }); + + const deleteRequests = rollingStockNames.map(async (name) => { + const rollingStockToDelete = rollingStocks.results.find((r: RollingStock) => r.name === name); + if (rollingStockToDelete) { + await deleteApiRequest(`/api/rolling_stock/${rollingStockToDelete.id}/`); + } + }); + + await Promise.all(deleteRequests); +} + +// Fill and check input by ID +// Note: This method check if the locator uses ID or TestID and fills it with the input value +export async function fillAndCheckInputById( + page: Page, + inputId: string, + value: string | number, + isTestId = false +) { + const input = isTestId ? page.getByTestId(inputId) : page.locator(`#${inputId}`); + + await input.click(); + await input.fill(`${value}`); + expect(await input.inputValue()).toBe(`${value}`); +} + +// Verify input by ID +// Note: This method check if the locator uses ID or TestID and verifies its content +export async function verifyAndCheckInputById( + page: Page, + inputId: string, + expectedValue: string | number, + isTestId = false +) { + const input = isTestId ? page.getByTestId(inputId) : page.locator(`#${inputId}`); + + expect(await input.inputValue()).toContain(`${expectedValue}`); +} + +// Generate unique name (used for creating rolling stock) +export const generateUniqueName = async (baseName: string) => { + // Generate a UUID and truncate it to 6 characters + const uuidSegment = uuidv4().slice(0, 6); + return `${baseName}-${uuidSegment}`; +}; + +// Extracts the first sequence of digits found in the input string and returns it as a number or return 0 if no digits found +export async function extractNumberFromString(input: string): Promise { + const match = input.match(/\d+/); + return match ? parseInt(match[0], 10) : 0; +} diff --git a/front/tests/utils/scenario-utils.ts b/front/tests/utils/scenario-utils.ts new file mode 100644 index 00000000000..2dba5487988 --- /dev/null +++ b/front/tests/utils/scenario-utils.ts @@ -0,0 +1,95 @@ +import { type Page, expect } from '@playwright/test'; +import { v4 as uuidv4 } from 'uuid'; + +import scenarioData from '../assets/operationStudies/scenario.json'; +import { getInfra, getProject, getRollingStock, getStudy, postApiRequest } from '.'; +import { PlaywrightHomePage } from '../pages/home-page-model'; +import RollingStockSelectorPage from '../pages/rolling-stock-selector-page'; +import PlaywrightScenarioPage from '../pages/scenario-page-model'; + +// Scenario creation +export default async function createCompleteScenario( + page: Page, + trainScheduleName: string, + trainCount: string, + delta: string +) { + const smallInfra = await getInfra(); + const project = await getProject(); + const study = await getStudy(project.id); + const rollingStock = await getRollingStock(); + + const scenario = await postApiRequest( + `/api/projects/${project.id}/studies/${study.id}/scenarios`, + { + ...scenarioData, + name: `${scenarioData.name} ${uuidv4()}`, + study_id: study.id, + infra_id: smallInfra.id, + } + ); + + const playwrightHomePage = new PlaywrightHomePage(page); + const scenarioPage = new PlaywrightScenarioPage(page); + + await page.goto( + `/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}` + ); + + await playwrightHomePage.page.getByTestId('scenarios-add-train-schedule-button').click(); + + await scenarioPage.setTrainScheduleName(trainScheduleName); + await scenarioPage.setNumberOfTrains(trainCount); + await scenarioPage.setDelta(delta); + + // ***************** Select Rolling Stock ***************** + const playwrightRollingstockModalPage = new RollingStockSelectorPage(playwrightHomePage.page); + await playwrightRollingstockModalPage.openRollingstockModal(); + + await playwrightRollingstockModalPage.searchRollingstock('rollingstock_1500_25000_test_e2e'); + + const rollingstockCard = playwrightRollingstockModalPage.getRollingstockCardByTestID( + `rollingstock-${rollingStock.name}` + ); + + await rollingstockCard.click(); + await rollingstockCard.locator('button').click(); + + // ***************** Select Origin/Destination ***************** + await scenarioPage.openTabByDataId('tab-pathfinding'); + await scenarioPage.getPathfindingByTriGramSearch('MWS', 'NES'); + + // ***************** Create train ***************** + await scenarioPage.addTrainSchedule(); + await scenarioPage.page.waitForSelector('.dots-loader', { state: 'hidden' }); + await scenarioPage.checkTrainHasBeenAdded(); + await scenarioPage.returnSimulationResult(); +} + +// Allowances management + +export async function allowancesManagement( + scenarioPage: PlaywrightScenarioPage, + scenarioName: string, + allowanceType: 'standard' | 'engineering' +) { + await expect(scenarioPage.getTimetableList).toBeVisible(); + + await scenarioPage.getBtnByName(scenarioName).hover(); + await scenarioPage.page.getByTestId('edit-train').click(); + + await scenarioPage.openAllowancesModule(); + await expect(scenarioPage.getAllowancesModule).toBeVisible(); + + if (allowanceType === 'standard') { + await scenarioPage.setStandardAllowance(); + } else { + await scenarioPage.setEngineeringAllowance(); + await scenarioPage.clickSuccessBtn(); + await expect(scenarioPage.getAllowancesEngineeringSettings).toHaveAttribute('disabled'); + } + + await scenarioPage.page.getByTestId('submit-edit-train-schedule').click(); + await scenarioPage.page.waitForSelector('.scenario-details-name'); + expect(await scenarioPage.isAllowanceWorking()).toEqual(true); +}