Skip to content

Commit 3ecc5d5

Browse files
committed
front: reduce and remove unnecessary timeouts for e2e tests
Signed-off-by: maymanaf <med.aymen.naf@gmail.com>
1 parent ffe0dee commit 3ecc5d5

17 files changed

+108
-146
lines changed

front/playwright.config.ts

+6-33
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,29 @@
11
import { defineConfig, devices } from '@playwright/test';
2-
/**
3-
* Read environment variables from file.
4-
* https://github.com/motdotla/dotenv
5-
*/
6-
// require('dotenv').config();
72

8-
/**
9-
* See https://playwright.dev/docs/test-configuration.
10-
*/
113
export default defineConfig({
124
testDir: './tests',
135

14-
/* Maximum time one test can run for. */
15-
timeout: process.env.CI ? 90 * 1000 : 180 * 1000, // 90 seconds in CI, otherwise 180 seconds
6+
timeout: 90_000,
167
expect: {
178
toHaveScreenshot: { maxDiffPixelRatio: 0.02 },
18-
/**
19-
* Maximum time expect() should wait for the condition to be met.
20-
*/
21-
timeout: process.env.CI ? 10 * 1000 : 30 * 1000, // 10 seconds in CI, otherwise 30 seconds
9+
timeout: 10_000,
2210
},
2311

24-
/* Run tests in files in parallel */
2512
fullyParallel: true,
26-
/*
27-
* Limit parallelism in CI based on CPU capacity,
28-
* running 50% of the available workers when in CI.
29-
* Otherwise, run tests with a single worker.
30-
*/
31-
workers: process.env.CI ? '50%' : 1,
32-
/* Fail the build on CI if you accidentally left test.only in the source code. */
13+
workers: '50%',
3314
forbidOnly: !!process.env.CI,
34-
/* Retry up to 2 times on CI, and 1 time otherwise */
35-
retries: process.env.CI ? 2 : 1,
36-
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
15+
retries: 1,
3716
use: {
38-
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
39-
actionTimeout: 0,
40-
/* Base URL to use in actions like `await page.goto('/')`. */
17+
navigationTimeout: 30_000,
18+
actionTimeout: 15_000,
4119
baseURL: process.env.BASE_URL || 'http://localhost:4000',
42-
43-
/* Collect trace and video when retrying the first failed test. See https://playwright.dev/docs/trace-viewer */
4420
trace: 'on-first-retry',
4521
video: 'on-first-retry',
46-
47-
/* Set locale and timezone */
4822
locale: 'fr',
4923
timezoneId: 'Europe/Paris',
5024
},
5125
reporter: process.env.CI ? 'github' : [['line'], ['html']],
5226

53-
/* Configure projects for major browsers */
5427
projects: [
5528
{ name: 'setup', testMatch: 'global-setup.ts', teardown: 'teardown' },
5629
{

front/tests/003-study-management.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ test.describe('Validate the Study creation workflow', () => {
2626
fr: frTranslations,
2727
});
2828
});
29+
2930
test.beforeEach(async ({ page }) => {
3031
studyPage = new StudyPage(page);
3132
});

front/tests/006-stdcm.spec.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@ import { handleAndVerifyInput, waitForInfraStateToBeCached } from './utils';
77
import { getInfra, setTowedRollingStock } from './utils/api-setup';
88
import type { ConsistFields } from './utils/types';
99

10-
test.use({
11-
launchOptions: {
12-
slowMo: 500, // Give the interface time to update between actions
13-
},
14-
});
15-
1610
test.describe('Verify stdcm simulation page', () => {
1711
test.slow(); // Mark test as slow due to multiple steps
1812
test.use({ viewport: { width: 1920, height: 1080 } });
@@ -56,7 +50,7 @@ test.describe('Verify stdcm simulation page', () => {
5650
// Retrieve OSRD language and navigate to STDCM page
5751
stdcmPage = new STDCMPage(page);
5852
await page.goto('/stdcm');
59-
await page.waitForLoadState('load', { timeout: 30 * 1000 });
53+
await page.waitForLoadState('networkidle');
6054
await stdcmPage.removeViteOverlay();
6155

6256
// Wait for infra to be in 'CACHED' state before proceeding

front/tests/008-train-schedule.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ test.describe('Verify train schedule elements and filters', () => {
2929
const NOT_HONORED_TRAINS = 3;
3030
const VALID_AND_HONORED_TRAINS = 14;
3131
const INVALID_AND_NOT_HONORED_TRAINS = 0;
32+
3233
test.beforeAll('Fetch project, study and scenario with train schedule', async () => {
3334
project = await getProject(trainScheduleProjectName);
3435
study = await getStudy(project.id, trainScheduleStudyName);

front/tests/011-op-times-and-stops-tab.spec.ts

-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ test.describe('Times and Stops Tab Verification', () => {
101101

102102
// Setup train configuration and schedule
103103
await operationalStudiesPage.clickOnAddTrainButton();
104-
await page.waitForTimeout(500); // Wait for any async actions to complete
105104
await operationalStudiesPage.setTrainStartTime('11:22:40');
106105
await rollingStockPage.selectRollingStock(dualModeRollingStockName);
107106
await operationalStudiesPage.setTrainScheduleName('Train-name-e2e-test');

front/tests/012-op-simulation-settings-tab.spec.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ test.describe('Simulation Settings Tab Verification', () => {
7878
);
7979

8080
type TranslationKeys = keyof typeof enTranslations;
81-
let stabilityTimeout: number;
8281

8382
// Define CellData interface for table cell data
8483
interface CellData {
@@ -143,10 +142,10 @@ test.describe('Simulation Settings Tab Verification', () => {
143142
await waitForInfraStateToBeCached(infra.id);
144143
// Add a new train and set its properties
145144
await operationalStudiesPage.clickOnAddTrainButton();
146-
await operationalStudiesPage.setTrainStartTime('11:22:40');
147-
await rollingStockPage.selectRollingStock(improbableRollingStockName);
148-
await page.waitForTimeout(stabilityTimeout);
149145
await operationalStudiesPage.setTrainScheduleName('Train-name-e2e-test');
146+
await rollingStockPage.selectRollingStock(improbableRollingStockName);
147+
await operationalStudiesPage.setTrainStartTime('11:22:40');
148+
150149
// Perform pathfinding
151150
await operationalStudiesPage.clickOnRouteTab();
152151
await routePage.performPathfindingByTrigram('WS', 'SES', 'MWS');
@@ -155,6 +154,7 @@ test.describe('Simulation Settings Tab Verification', () => {
155154
await scrollContainer(page, '.time-stops-datasheet .dsg-container');
156155
}
157156
);
157+
158158
test.afterEach('Delete the created scenario', async () => {
159159
await deleteScenario(project.id, study.id, scenario.name);
160160
});
@@ -207,7 +207,6 @@ test.describe('Simulation Settings Tab Verification', () => {
207207
await operationalStudiesPage.clickOnSimulationSettingsTab();
208208
await opSimulationSettingsPage.deactivateElectricalProfile();
209209
await opTimetablePage.clickOnEditTrainSchedule();
210-
await page.waitForTimeout(stabilityTimeout); // Waiting for the timetable to update due to a slight latency
211210
await opTimetablePage.getTrainArrivalTime('11:52');
212211
await opTimetablePage.clickOnScenarioCollapseButton();
213212
await opOutputTablePage.verifyTimesStopsDataSheetVisibility();
@@ -273,7 +272,6 @@ test.describe('Simulation Settings Tab Verification', () => {
273272
await operationalStudiesPage.clickOnSimulationSettingsTab();
274273
await opSimulationSettingsPage.selectCodeCompoOption('__PLACEHOLDER__');
275274
await opTimetablePage.clickOnEditTrainSchedule();
276-
await page.waitForTimeout(stabilityTimeout);
277275
await opTimetablePage.getTrainArrivalTime('11:52');
278276
await opTimetablePage.clickOnScenarioCollapseButton();
279277
await opOutputTablePage.verifyTimesStopsDataSheetVisibility();
@@ -345,7 +343,6 @@ test.describe('Simulation Settings Tab Verification', () => {
345343
await operationalStudiesPage.clickOnSimulationSettingsTab();
346344
await opSimulationSettingsPage.activateMarecoMargin();
347345
await opTimetablePage.clickOnEditTrainSchedule();
348-
await page.waitForTimeout(stabilityTimeout);
349346
await opTimetablePage.getTrainArrivalTime('11:54');
350347
await opTimetablePage.clickOnScenarioCollapseButton();
351348
await opOutputTablePage.verifyTimesStopsDataSheetVisibility();

front/tests/013-stdcm-simulation-sheet.spec.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,6 @@ import { getInfra } from './utils/api-setup';
1313
import { findFirstPdf, verifySimulationContent } from './utils/simulationSheet';
1414
import type { ConsistFields, Simulation } from './utils/types';
1515

16-
test.use({
17-
launchOptions: {
18-
slowMo: 500, // Give the interface time to update between actions
19-
},
20-
});
21-
2216
test.describe('Verify stdcm simulation page', () => {
2317
test.describe.configure({ mode: 'serial' }); // Configure this block to run serially
2418
test.slow(); // Mark test as slow due to multiple steps
@@ -50,7 +44,7 @@ test.describe('Verify stdcm simulation page', () => {
5044
// Retrieve OSRD language and navigate to STDCM page
5145
stdcmPage = new STDCMPage(page);
5246
await page.goto('/stdcm');
53-
await page.waitForLoadState('domcontentloaded', { timeout: 30_000 });
47+
await page.waitForLoadState('networkidle');
5448
await stdcmPage.removeViteOverlay();
5549

5650
// Wait for infra to be in 'CACHED' state before proceeding

front/tests/assets/timeout-const.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const LOAD_PAGE_TIMEOUT = 30_000;
2+
export const SIMULATION_RESULT_TIMEOUT = 30_000;
3+
export const STDCM_SIMULATION_TIMEOUT = 30_000;
4+
export const EXPLICIT_UI_STABILITY_TIMEOUT = 1_000;

front/tests/pages/op-input-table-page-model.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class OperationalStudiesInputTablePage {
6565
.first();
6666
await rowLocator.waitFor({ state: 'attached' });
6767
const cell = rowLocator.locator('.dsg-cell').nth(columnIndex);
68-
await cell.waitFor({ state: 'visible', timeout: 5000 });
68+
await cell.waitFor();
6969
await cell.dblclick();
7070

7171
// Fill the input field based on the presence of a placeholder
@@ -111,7 +111,7 @@ class OperationalStudiesInputTablePage {
111111

112112
for (let rowIndex = 1; rowIndex < rowCount; rowIndex += 1) {
113113
const rowCells = this.tableRows.nth(rowIndex).locator('.dsg-cell .dsg-input');
114-
await rowCells.first().waitFor({ state: 'visible', timeout: 5000 });
114+
await rowCells.first().waitFor();
115115
const rowValues = await rowCells.evaluateAll((cells) =>
116116
cells.map((cell) => cell.getAttribute('value'))
117117
);

front/tests/pages/op-output-table-page-model.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class OperationalStudiesOutputTablePage extends OperationalStudiesTimetablePage
5151
// Iterate through each active row and extract data based on header mappings
5252
for (let rowIndex = 1; rowIndex < rowCount; rowIndex += 1) {
5353
const row = this.tableRows.nth(rowIndex);
54-
await row.waitFor({ state: 'visible' });
54+
await row.waitFor();
5555

5656
// Extract cells from the current row
5757
const cells = row.locator('.dsg-cell.dsg-cell-disabled');
@@ -148,11 +148,10 @@ class OperationalStudiesOutputTablePage extends OperationalStudiesTimetablePage
148148
expect(normalizedActualData).toEqual(normalizedExpectedData);
149149
}
150150

151-
// Wait for the Times and Stops simulation data sheet to be fully loaded with a specified timeout (default: 60 seconds)
152-
async verifyTimesStopsDataSheetVisibility(timeout = 60 * 1000): Promise<void> {
153-
await this.timesStopsDataSheet.waitFor({ state: 'visible', timeout });
154-
await this.page.waitForTimeout(1000); // Short delay for stabilization
155-
await this.timesStopsDataSheet.scrollIntoViewIfNeeded({ timeout });
151+
// Wait for the Times and Stops simulation data sheet to be fully loaded
152+
async verifyTimesStopsDataSheetVisibility(): Promise<void> {
153+
await this.timesStopsDataSheet.waitFor({ timeout: LOAD_PAGE_TIMEOUT });
154+
await this.timesStopsDataSheet.scrollIntoViewIfNeeded();
156155
}
157156
}
158157

front/tests/pages/op-route-page-model.ts

+12-11
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { expect, type Locator, type Page } from '@playwright/test';
33
import enTranslations from '../../public/locales/en/operationalStudies/manageTrainSchedule.json';
44
import frTranslations from '../../public/locales/fr/operationalStudies/manageTrainSchedule.json';
55
import { getTranslations } from '../utils';
6-
import { clickWithDelay } from '../utils';
76

87
class RoutePage {
98
readonly page: Page;
@@ -52,6 +51,8 @@ class RoutePage {
5251

5352
readonly missingParamMessage: Locator;
5453

54+
readonly pathfindingLoader: Locator;
55+
5556
constructor(page: Page) {
5657
this.page = page;
5758

@@ -77,6 +78,7 @@ class RoutePage {
7778
this.viaModal = page.locator('.manage-vias-modal');
7879
this.closeViaModalButton = page.getByLabel('Close');
7980
this.missingParamMessage = page.getByTestId('missing-params-info');
81+
this.pathfindingLoader = page.locator('.dots-loader');
8082
}
8183

8284
// Get the name locator of a waypoint suggestion.
@@ -196,7 +198,7 @@ class RoutePage {
196198
const expectedDestinationTrigram =
197199
await this.getDestinationLocatorByTrigram(destinationTrigram).innerText();
198200
await this.clickSearchByTrigramSubmitButton();
199-
await this.page.waitForSelector('.dots-loader', { state: 'hidden' });
201+
await this.pathfindingLoader.waitFor({ state: 'hidden' });
200202
await expect(this.searchByTrigramContainer).not.toBeVisible();
201203
await expect(this.resultPathfindingDone).toBeVisible();
202204

@@ -213,15 +215,14 @@ class RoutePage {
213215
async clickOnDeleteOPButtons(selectedLanguage: string) {
214216
// Ensure all buttons are rendered and visible before proceeding
215217
await Promise.all([
216-
this.viaDeleteButton.waitFor({ state: 'visible' }),
217-
this.originDeleteButton.waitFor({ state: 'visible' }),
218-
this.destinationDeleteButton.waitFor({ state: 'visible' }),
218+
this.viaDeleteButton.waitFor(),
219+
this.originDeleteButton.waitFor(),
220+
this.destinationDeleteButton.waitFor(),
219221
]);
220222

221-
// Click the buttons sequentially with waits to ensure UI stability
222-
await clickWithDelay(this.viaDeleteButton);
223-
await clickWithDelay(this.originDeleteButton);
224-
await clickWithDelay(this.destinationDeleteButton);
223+
await this.viaDeleteButton.click();
224+
await this.originDeleteButton.click();
225+
await this.destinationDeleteButton.click();
225226
const translations = getTranslations(selectedLanguage, {
226227
en: enTranslations,
227228
fr: frTranslations,
@@ -230,15 +231,15 @@ class RoutePage {
230231
': {{missingElements}}.',
231232
''
232233
);
233-
await this.missingParamMessage.waitFor({ state: 'visible' });
234+
await this.missingParamMessage.waitFor();
234235
const actualMessage = await this.missingParamMessage.innerText();
235236
expect(actualMessage).toContain(expectedMessage);
236237
}
237238

238239
// Click the add buttons for the specified via names.
239240
async clickOnViaAddButtons(...viaNames: string[]) {
240241
for (const viaName of viaNames) {
241-
await clickWithDelay(this.getAddButtonLocatorByViaName(viaName));
242+
await this.getAddButtonLocatorByViaName(viaName).click();
242243
await expect(this.getDeleteButtonLocatorByViaName(viaName)).toBeVisible();
243244
}
244245
}

0 commit comments

Comments
 (0)