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 86e75c42692..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';
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);
+}