Skip to content

Commit 0f97f32

Browse files
committed
front: add times and stops e2e test for operational studies
Signed-off-by: maymanaf <[email protected]>
1 parent 2709d30 commit 0f97f32

15 files changed

+776
-9
lines changed

front/src/modules/timesStops/TimesStopsInput.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const createClearViaButton = ({
4747
rowData.onStopSignal === true);
4848
if (isClearBtnShown) {
4949
return (
50-
<button type="button" onClick={removeVia}>
50+
<button data-testid="remove-via-button" type="button" onClick={removeVia}>
5151
5252
</button>
5353
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
import type { Project, Scenario, Study } from 'common/api/osrdEditoastApi';
4+
5+
import HomePage from './pages/home-page-model';
6+
import OperationalStudiesInputTablePage from './pages/op-input-table-page-model';
7+
import OperationalStudiesOutputTablePage from './pages/op-output-table-page-model';
8+
import OperationalStudiesTimetablePage from './pages/op-timetable-page-model';
9+
import OperationalStudiesPage from './pages/operational-studies-page-model';
10+
import ScenarioPage from './pages/scenario-page-model';
11+
import { readJsonFile } from './utils';
12+
import { cleanWhitespace, cleanWhitespaces, type StationData } from './utils/dataNormalizer';
13+
import setupScenario from './utils/scenario';
14+
import scrollContainer from './utils/scrollHelper';
15+
import enTranslations from '../public/locales/en/timesStops.json';
16+
import frTranslations from '../public/locales/fr/timesStops.json';
17+
18+
let project: Project;
19+
let study: Study;
20+
let scenario: Scenario;
21+
let selectedLanguage: string;
22+
23+
const dualRollingStockName = 'dual-mode_rollingstock_test_e2e';
24+
25+
const initialInputsData: CellData[] = readJsonFile(
26+
'./tests/assets/operationStudies/timesAndStops/initialInputs.json'
27+
);
28+
const updatedInputsData: CellData[] = readJsonFile(
29+
'./tests/assets/operationStudies/timesAndStops/updatedInputs.json'
30+
);
31+
const outputExpectedCellData: StationData[] = readJsonFile(
32+
'./tests/assets/operationStudies/timesAndStops/expectedOutputsCellsData.json'
33+
);
34+
const inputExpectedData = readJsonFile(
35+
'./tests/assets/operationStudies/timesAndStops/expectedInputsCellsData.json'
36+
);
37+
const updatedCellData = readJsonFile(
38+
'./tests/assets/operationStudies/timesAndStops/updatedInputsCellsData.json'
39+
);
40+
41+
const expectedViaValues = [
42+
{ name: 'Mid_West_station', ch: 'BV', uic: '3', km: 'KM 11.850' },
43+
{ name: 'Mid_East_station', ch: 'BV', uic: '4', km: 'KM 26.300' },
44+
];
45+
46+
type TranslationKeys = keyof typeof enTranslations;
47+
48+
// Define CellData interface for table cell data
49+
interface CellData {
50+
stationName: string;
51+
header: TranslationKeys;
52+
value: string;
53+
marginForm?: string;
54+
}
55+
56+
test.beforeEach(async ({ page }) => {
57+
// Create a new scenario
58+
({ project, study, scenario } = await setupScenario());
59+
60+
// Navigate to home page and retrieve the language setting
61+
const homePage = new HomePage(page);
62+
await homePage.goToHomePage();
63+
selectedLanguage = await homePage.getOSRDLanguage();
64+
65+
// Go to the specific operational study scenario page
66+
await page.goto(
67+
`/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}`
68+
);
69+
});
70+
71+
test.describe('Times and Stops Tab Verification', () => {
72+
// Set viewport to avoid scrolling issues and ensure elements are attached to the DOM
73+
test.use({ viewport: { width: 1920, height: 1080 } });
74+
75+
test('should correctly set and display times and stops tables', async ({ page }) => {
76+
// Page models
77+
const [
78+
opInputTablePage,
79+
opTimetablePage,
80+
opOutputTablePage,
81+
operationalStudiesPage,
82+
scenarioPage,
83+
] = [
84+
new OperationalStudiesInputTablePage(page),
85+
new OperationalStudiesTimetablePage(page),
86+
new OperationalStudiesOutputTablePage(page),
87+
new OperationalStudiesPage(page),
88+
new ScenarioPage(page),
89+
];
90+
91+
// Setup the initial train configuration and schedule
92+
await scenarioPage.checkInfraLoaded();
93+
await operationalStudiesPage.clickOnAddTrainBtn();
94+
await scenarioPage.setTrainScheduleName('Train-name-e2e-test');
95+
await page.waitForTimeout(500);
96+
await operationalStudiesPage.setTrainStartTime('11:22:40');
97+
await operationalStudiesPage.selectRollingStock(dualRollingStockName);
98+
99+
// Perform pathfinding
100+
await scenarioPage.openTabByDataId('tab-pathfinding');
101+
await operationalStudiesPage.performPathfindingByTrigram('WS', 'NES');
102+
103+
// Navigate to the Times and Stops tab and scroll into view
104+
await scenarioPage.openTabByDataId('tab-timesStops');
105+
await scrollContainer(page, '.time-stops-datasheet .dsg-container');
106+
107+
// Set column names based on the selected language
108+
const translations = selectedLanguage === 'English' ? enTranslations : frTranslations;
109+
const expectedColumnNames = cleanWhitespaces([
110+
translations.name,
111+
'Ch',
112+
translations.arrivalTime,
113+
translations.departureTime,
114+
translations.stopTime,
115+
translations.receptionOnClosedSignal,
116+
translations.theoreticalMargin,
117+
]);
118+
119+
// Verify that the actual column headers match the expected headers
120+
const actualColumnHeaders = cleanWhitespaces(
121+
await opInputTablePage.columnHeaders.allInnerTexts()
122+
);
123+
expect(actualColumnHeaders).toEqual(expectedColumnNames);
124+
125+
// Validate the initial active row count
126+
await opInputTablePage.verifyActiveRowsCount(2);
127+
128+
// Fill in table cells based on the predefined cell data
129+
for (const cell of initialInputsData) {
130+
const translatedHeader = cleanWhitespace(translations[cell.header]);
131+
await opInputTablePage.fillTableCellByStationAndHeader(
132+
cell.stationName,
133+
translatedHeader,
134+
cell.value,
135+
selectedLanguage,
136+
cell.marginForm
137+
);
138+
}
139+
140+
// Verify the table after modification
141+
await opInputTablePage.verifyActiveRowsCount(4);
142+
await opInputTablePage.verifyDeleteButtons(2);
143+
await opInputTablePage.verifyInputTableData(inputExpectedData);
144+
145+
// Switch to Pathfinding tab and validate waypoints
146+
await scenarioPage.openTabByDataId('tab-pathfinding');
147+
for (const [viaIndex, expectedValue] of expectedViaValues.entries()) {
148+
const droppedWaypoint = operationalStudiesPage.droppedWaypoints.nth(viaIndex);
149+
await OperationalStudiesPage.validateAddedWaypoint(
150+
droppedWaypoint,
151+
expectedValue.name,
152+
expectedValue.ch,
153+
expectedValue.uic
154+
);
155+
}
156+
157+
// Add the train schedule and verify simulation results
158+
await scenarioPage.addTrainSchedule();
159+
await scenarioPage.returnSimulationResult();
160+
opTimetablePage.verifyTimeStopsDatasheetVisibility();
161+
// Scroll and extract data from output table
162+
await scrollContainer(page, '.osrd-simulation-container .time-stops-datasheet .dsg-container');
163+
await opOutputTablePage.getOutputTableData(outputExpectedCellData, selectedLanguage);
164+
});
165+
166+
test('should correctly update and clear input table row', async ({ page }) => {
167+
// Page models
168+
const [opInputTablePage, operationalStudiesPage, scenarioPage] = [
169+
new OperationalStudiesInputTablePage(page),
170+
new OperationalStudiesPage(page),
171+
new ScenarioPage(page),
172+
];
173+
174+
// Setup initial train configuration
175+
await scenarioPage.checkInfraLoaded();
176+
await operationalStudiesPage.clickOnAddTrainBtn();
177+
await scenarioPage.setTrainScheduleName('Train-name-e2e-test');
178+
await page.waitForTimeout(500);
179+
await operationalStudiesPage.setTrainStartTime('11:22:40');
180+
await operationalStudiesPage.selectRollingStock(dualRollingStockName);
181+
182+
// Perform pathfinding and navigate to Times and Stops tab
183+
await scenarioPage.openTabByDataId('tab-pathfinding');
184+
await operationalStudiesPage.performPathfindingByTrigram('WS', 'NES');
185+
await scenarioPage.openTabByDataId('tab-timesStops');
186+
await scrollContainer(page, '.time-stops-datasheet .dsg-container');
187+
188+
const translations = selectedLanguage === 'English' ? enTranslations : frTranslations;
189+
// Fill in table cells based on the predefined cell data
190+
for (const cell of initialInputsData) {
191+
const translatedHeader = cleanWhitespace(translations[cell.header]);
192+
await opInputTablePage.fillTableCellByStationAndHeader(
193+
cell.stationName,
194+
translatedHeader,
195+
cell.value,
196+
selectedLanguage,
197+
cell.marginForm
198+
);
199+
}
200+
await opInputTablePage.verifyInputTableData(inputExpectedData);
201+
202+
// Update table inputs
203+
await opInputTablePage.verifyActiveRowsCount(4);
204+
for (const cell of updatedInputsData) {
205+
const translatedHeader = cleanWhitespace(translations[cell.header]);
206+
await opInputTablePage.fillTableCellByStationAndHeader(
207+
cell.stationName,
208+
translatedHeader,
209+
cell.value,
210+
selectedLanguage,
211+
cell.marginForm
212+
);
213+
}
214+
215+
// Delete a row and validate row count
216+
await opInputTablePage.verifyDeleteButtons(2);
217+
await opInputTablePage.deleteButtons.nth(0).click();
218+
await opInputTablePage.verifyActiveRowsCount(4);
219+
await opInputTablePage.verifyDeleteButtons(1);
220+
await opInputTablePage.verifyInputTableData(updatedCellData);
221+
222+
// Switch to Pathfinding tab and validate waypoints
223+
await scenarioPage.openTabByDataId('tab-pathfinding');
224+
for (const [viaIndex, expectedValue] of expectedViaValues.entries()) {
225+
const droppedWaypoint = operationalStudiesPage.droppedWaypoints.nth(viaIndex);
226+
await OperationalStudiesPage.validateAddedWaypoint(
227+
droppedWaypoint,
228+
expectedValue.name,
229+
expectedValue.ch,
230+
expectedValue.uic
231+
);
232+
}
233+
});
234+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"row": 1,
4+
"values": ["West_station", "BV", "11:22:40", "", "", "5%"]
5+
},
6+
{
7+
"row": 2,
8+
"values": ["Mid_West_station", "BV", "11:30:40", "11:35:40", "300", "1min/100km"]
9+
},
10+
{
11+
"row": 3,
12+
"values": ["Mid_East_station", "BV", "11:45:21", "11:47:25", "124", ""]
13+
},
14+
{
15+
"row": 4,
16+
"values": ["North_East_station", "BV", "", "", "0", ""]
17+
}
18+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
[
2+
{
3+
"stationName": "West_station",
4+
"stationCh": "BV",
5+
"requestedArrival": "11:22:40",
6+
"requestedDeparture": "",
7+
"stopTime": "",
8+
"signalReceptionClosed": false,
9+
"margin": {
10+
"theoretical": "5 %",
11+
"theoreticalS": "23 s",
12+
"actual": "23 s",
13+
"difference": "0 s"
14+
},
15+
"calculatedArrival": "11:22:40",
16+
"calculatedDeparture": ""
17+
},
18+
{
19+
"stationName": "Mid_West_station",
20+
"stationCh": "BV",
21+
"requestedArrival": "11:30:40",
22+
"requestedDeparture": "11:35:40",
23+
"stopTime": "300",
24+
"signalReceptionClosed": true,
25+
"margin": {
26+
"theoretical": "1 min/100km",
27+
"theoreticalS": "9 s",
28+
"actual": "167 s",
29+
"difference": "159 s"
30+
},
31+
"calculatedArrival": "11:30:39",
32+
"calculatedDeparture": "11:35:39"
33+
},
34+
{
35+
"stationName": "Mid_East_station",
36+
"stationCh": "BV",
37+
"requestedArrival": "11:45:21",
38+
"requestedDeparture": "11:47:25",
39+
"stopTime": "124",
40+
"signalReceptionClosed": false,
41+
"margin": {
42+
"theoretical": "",
43+
"theoreticalS": "12 s",
44+
"actual": "12 s",
45+
"difference": "0 s"
46+
},
47+
"calculatedArrival": "11:45:20",
48+
"calculatedDeparture": "11:47:24"
49+
},
50+
{
51+
"stationName": "North_East_station",
52+
"stationCh": "BV",
53+
"requestedArrival": "",
54+
"requestedDeparture": "",
55+
"stopTime": "0",
56+
"signalReceptionClosed": false,
57+
"margin": {
58+
"theoretical": "",
59+
"theoreticalS": "",
60+
"actual": "",
61+
"difference": ""
62+
},
63+
"calculatedArrival": "11:56:23",
64+
"calculatedDeparture": ""
65+
}
66+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[
2+
{
3+
"stationName": "West_station",
4+
"header": "theoreticalMargin",
5+
"value": "5%",
6+
"marginForm": "% ou min/100km"
7+
},
8+
{
9+
"stationName": "Mid_West_station",
10+
"header": "theoreticalMargin",
11+
"value": "1min/100km",
12+
"marginForm": "% ou min/100km"
13+
},
14+
{
15+
"stationName": "Mid_West_station",
16+
"header": "arrivalTime",
17+
"value": "11:30:40"
18+
},
19+
{
20+
"stationName": "Mid_West_station",
21+
"header": "stopTime",
22+
"value": "300"
23+
},
24+
{
25+
"stationName": "Mid_East_station",
26+
"header": "arrivalTime",
27+
"value": "11:45:21"
28+
},
29+
{
30+
"stationName": "Mid_East_station",
31+
"header": "stopTime",
32+
"value": "124"
33+
}
34+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"stationName": "West_station",
4+
"header": "theoreticalMargin",
5+
"value": "3%",
6+
"marginForm": "% ou min/100km"
7+
},
8+
{
9+
"stationName": "Mid_East_station",
10+
"header": "arrivalTime",
11+
"value": "12:58:19"
12+
},
13+
{
14+
"stationName": "Mid_East_station",
15+
"header": "stopTime",
16+
"value": "21"
17+
}
18+
]

0 commit comments

Comments
 (0)