Skip to content

Commit

Permalink
front: reduce and remove unnecessary timeouts for e2e tests
Browse files Browse the repository at this point in the history
Signed-off-by: maymanaf <[email protected]>
  • Loading branch information
Maymanaf committed Jan 16, 2025
1 parent dcc895a commit 69856d9
Show file tree
Hide file tree
Showing 15 changed files with 71 additions and 94 deletions.
8 changes: 4 additions & 4 deletions front/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ export default defineConfig({
testDir: './tests',

/* Maximum time one test can run for. */
timeout: process.env.CI ? 90 * 1000 : 180 * 1000, // 90 seconds in CI, otherwise 180 seconds
timeout: 60_000,
expect: {
toHaveScreenshot: { maxDiffPixelRatio: 0.02 },
/**
* Maximum time expect() should wait for the condition to be met.
*/
timeout: process.env.CI ? 10 * 1000 : 30 * 1000, // 10 seconds in CI, otherwise 30 seconds
timeout: 10_000,
},

/* Run tests in files in parallel */
Expand All @@ -28,11 +28,11 @@ export default defineConfig({
* running 50% of the available workers when in CI.
* Otherwise, run tests with a single worker.
*/
workers: process.env.CI ? '50%' : 1,
workers: process.env.CI ? '75%' : 2,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry up to 2 times on CI, and 1 time otherwise */
retries: process.env.CI ? 2 : 1,
retries: process.env.CI ? 1 : 1,
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
Expand Down
3 changes: 2 additions & 1 deletion front/tests/006-stdcm.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Infra, TowedRollingStock } from 'common/api/osrdEditoastApi';

import { electricRollingStockName, fastRollingStockName } from './assets/project-const';
import { LOAD_PAGE_TIMEOUT } from './assets/timeout-const';
import test from './logging-fixture';
import STDCMPage, { type ConsistFields } from './pages/stdcm-page-model';
import { handleAndVerifyInput, waitForInfraStateToBeCached } from './utils';
Expand Down Expand Up @@ -51,7 +52,7 @@ test.describe('Verify train schedule elements and filters', () => {
// Retrieve OSRD language and navigate to STDCM page
stdcmPage = new STDCMPage(page);
await page.goto('/stdcm');
await page.waitForLoadState('load', { timeout: 30 * 1000 });
await page.waitForLoadState('load', { timeout: LOAD_PAGE_TIMEOUT });
await stdcmPage.removeViteOverlay();

// Wait for infra to be in 'CACHED' state before proceeding
Expand Down
1 change: 0 additions & 1 deletion front/tests/011-op-times-and-stops-tab.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ test.describe('Times and Stops Tab Verification', () => {
// Setup train configuration and schedule
await operationalStudiesPage.clickOnAddTrainButton();
await operationalStudiesPage.setTrainScheduleName('Train-name-e2e-test');
await page.waitForTimeout(500); // Wait for any async actions to complete
await operationalStudiesPage.setTrainStartTime('11:22:40');
await rollingStockPage.selectRollingStock(dualModeRollingStockName);

Expand Down
8 changes: 1 addition & 7 deletions front/tests/012-op-simulation-settings-tab.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ test.describe('Simulation Settings Tab Verification', () => {
let scenario: Scenario;
let infra: Infra;
type TranslationKeys = keyof typeof enTranslations;
let stabilityTimeout: number;

// Define CellData interface for table cell data
interface CellData {
Expand All @@ -91,8 +90,7 @@ test.describe('Simulation Settings Tab Verification', () => {

test.beforeEach(
'Navigate to Times and Stops tab with rolling stock and route set',
async ({ page, browserName }) => {
stabilityTimeout = browserName === 'webkit' ? 2000 : 1000;
async ({ page }) => {
[
operationalStudiesPage,
routePage,
Expand Down Expand Up @@ -131,7 +129,6 @@ test.describe('Simulation Settings Tab Verification', () => {
// Add a new train and set its properties
await operationalStudiesPage.clickOnAddTrainButton();
await operationalStudiesPage.setTrainScheduleName('Train-name-e2e-test');
await page.waitForTimeout(stabilityTimeout);
await operationalStudiesPage.setTrainStartTime('11:22:40');
// Select electric rolling stock
await rollingStockPage.selectRollingStock(improbableRollingStockName);
Expand Down Expand Up @@ -198,7 +195,6 @@ test.describe('Simulation Settings Tab Verification', () => {
await operationalStudiesPage.clickOnSimulationSettingsTab();
await opSimulationSettingsPage.deactivateElectricalProfile();
await opTimetablePage.clickOnEditTrainSchedule();
await page.waitForTimeout(stabilityTimeout); // Waiting for the timetable to update due to a slight latency
await opTimetablePage.getTrainArrivalTime('11:52');
await opTimetablePage.clickOnScenarioCollapseButton();
await opOutputTablePage.verifyTimesStopsDataSheetVisibility();
Expand Down Expand Up @@ -266,7 +262,6 @@ test.describe('Simulation Settings Tab Verification', () => {
await operationalStudiesPage.clickOnSimulationSettingsTab();
await opSimulationSettingsPage.selectCodeCompoOption('__PLACEHOLDER__');
await opTimetablePage.clickOnEditTrainSchedule();
await page.waitForTimeout(stabilityTimeout);
await opTimetablePage.getTrainArrivalTime('11:52');
await opTimetablePage.clickOnScenarioCollapseButton();
await opOutputTablePage.verifyTimesStopsDataSheetVisibility();
Expand Down Expand Up @@ -340,7 +335,6 @@ test.describe('Simulation Settings Tab Verification', () => {
await operationalStudiesPage.clickOnSimulationSettingsTab();
await opSimulationSettingsPage.activateMarecoMargin();
await opTimetablePage.clickOnEditTrainSchedule();
await page.waitForTimeout(stabilityTimeout);
await opTimetablePage.getTrainArrivalTime('11:54');
await opTimetablePage.clickOnScenarioCollapseButton();
await opOutputTablePage.verifyTimesStopsDataSheetVisibility();
Expand Down
4 changes: 4 additions & 0 deletions front/tests/assets/timeout-const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const LOAD_PAGE_TIMEOUT = 30_000;
export const SIMULATION_RESULT_TIMEOUT = 30_000;
export const STDCM_SIMULATION_TIMEOUT = 60_000;
export const DOWNLOAD_SIMULATION_TIMEOUT = 30_000;
4 changes: 2 additions & 2 deletions front/tests/pages/op-input-table-page-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class OperationalStudiesInputTablePage {
.first();
await rowLocator.waitFor({ state: 'attached' });
const cell = rowLocator.locator('.dsg-cell').nth(columnIndex);
await cell.waitFor({ state: 'visible', timeout: 5000 });
await cell.waitFor();
await cell.dblclick();

// Fill the input field based on the presence of a placeholder
Expand Down Expand Up @@ -107,7 +107,7 @@ class OperationalStudiesInputTablePage {

for (let rowIndex = 1; rowIndex < rowCount; rowIndex += 1) {
const rowCells = this.tableRows.nth(rowIndex).locator('.dsg-cell .dsg-input');
await rowCells.first().waitFor({ state: 'visible', timeout: 5000 });
await rowCells.first().waitFor();
const rowValues = await rowCells.evaluateAll((cells) =>
cells.map((cell) => cell.getAttribute('value'))
);
Expand Down
12 changes: 6 additions & 6 deletions front/tests/pages/op-output-table-page-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type Locator, type Page, expect } from '@playwright/test';
import OperationalStudiesTimetablePage from './op-timetable-page-model';
import enTranslations from '../../public/locales/en/timesStops.json';
import frTranslations from '../../public/locales/fr/timesStops.json';
import { LOAD_PAGE_TIMEOUT } from '../assets/timeout-const';
import { normalizeData, type StationData } from '../utils/dataNormalizer';

class OperationalStudiesOutputTablePage extends OperationalStudiesTimetablePage {
Expand Down Expand Up @@ -45,7 +46,7 @@ class OperationalStudiesOutputTablePage extends OperationalStudiesTimetablePage
// Iterate through each active row and extract data based on header mappings
for (let rowIndex = 1; rowIndex < rowCount; rowIndex += 1) {
const row = this.tableRows.nth(rowIndex);
await row.waitFor({ state: 'visible' });
await row.waitFor();

// Extract cells from the current row
const cells = row.locator('.dsg-cell.dsg-cell-disabled');
Expand Down Expand Up @@ -142,11 +143,10 @@ class OperationalStudiesOutputTablePage extends OperationalStudiesTimetablePage
expect(normalizedActualData).toEqual(normalizedExpectedData);
}

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

Expand Down
22 changes: 12 additions & 10 deletions front/tests/pages/op-route-page-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { expect, type Locator, type Page } from '@playwright/test';

import enTranslations from '../../public/locales/en/operationalStudies/manageTrainSchedule.json';
import frTranslations from '../../public/locales/fr/operationalStudies/manageTrainSchedule.json';
import { clickWithDelay } from '../utils';

class RoutePage {
readonly page: Page;
Expand Down Expand Up @@ -51,6 +50,8 @@ class RoutePage {

readonly missingParamMessage: Locator;

readonly pathfindingLoader: Locator;

constructor(page: Page) {
this.page = page;

Expand All @@ -76,6 +77,7 @@ class RoutePage {
this.viaModal = page.locator('.manage-vias-modal');
this.closeViaModalButton = page.getByLabel('Close');
this.missingParamMessage = page.getByTestId('missing-params-info');
this.pathfindingLoader = page.locator('.dots-loader');
}

// Get the name locator of a waypoint suggestion.
Expand Down Expand Up @@ -192,7 +194,7 @@ class RoutePage {
const expectedDestinationTrigram =
await this.getDestinationLocatorByTrigram(destinationTrigram).innerText();
await this.clickSearchByTrigramSubmitButton();
await this.page.waitForSelector('.dots-loader', { state: 'hidden' });
await this.pathfindingLoader.waitFor({ state: 'hidden' });
await expect(this.searchByTrigramContainer).not.toBeVisible();
await expect(this.resultPathfindingDone).toBeVisible();

Expand All @@ -209,29 +211,29 @@ class RoutePage {
async clickOnDeleteOPButtons(selectedLanguage: string) {
// Ensure all buttons are rendered and visible before proceeding
await Promise.all([
this.viaDeleteButton.waitFor({ state: 'visible' }),
this.originDeleteButton.waitFor({ state: 'visible' }),
this.destinationDeleteButton.waitFor({ state: 'visible' }),
this.viaDeleteButton.waitFor(),
this.originDeleteButton.waitFor(),
this.destinationDeleteButton.waitFor(),
]);

// Click the buttons sequentially with waits to ensure UI stability
await clickWithDelay(this.viaDeleteButton);
await clickWithDelay(this.originDeleteButton);
await clickWithDelay(this.destinationDeleteButton);
await this.viaDeleteButton.click();
await this.originDeleteButton.click();
await this.destinationDeleteButton.click();
const translations = selectedLanguage === 'English' ? enTranslations : frTranslations;
const expectedMessage = translations.pathfindingMissingParams.replace(
': {{missingElements}}.',
''
);
await this.missingParamMessage.waitFor({ state: 'visible' });
await this.missingParamMessage.waitFor();
const actualMessage = await this.missingParamMessage.innerText();
expect(actualMessage).toContain(expectedMessage);
}

// Click the add buttons for the specified via names.
async clickOnViaAddButtons(...viaNames: string[]) {
for (const viaName of viaNames) {
await clickWithDelay(this.getAddButtonLocatorByViaName(viaName));
await this.getAddButtonLocatorByViaName(viaName).click();
await expect(this.getDeleteButtonLocatorByViaName(viaName)).toBeVisible();
}
}
Expand Down
43 changes: 17 additions & 26 deletions front/tests/pages/op-timetable-page-model.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { type Locator, type Page, expect } from '@playwright/test';

import CommonPage from './common-page-model';
import enTranslations from '../../public/locales/en/operationalStudies/scenario.json';
import frTranslations from '../../public/locales/fr/operationalStudies/scenario.json';
import { clickWithDelay } from '../utils';
import CommonPage from './common-page-model';
import { LOAD_PAGE_TIMEOUT, SIMULATION_RESULT_TIMEOUT } from '../assets/timeout-const';

class OperationalStudiesTimetablePage extends CommonPage {
readonly invalidTrainsMessage: Locator;
Expand Down Expand Up @@ -41,6 +41,8 @@ class OperationalStudiesTimetablePage extends CommonPage {

readonly scenarioSideMenu: Locator;

readonly simulationResult: Locator;

constructor(page: Page) {
super(page);
this.invalidTrainsMessage = page.getByTestId('invalid-trains-message');
Expand All @@ -61,24 +63,14 @@ class OperationalStudiesTimetablePage extends CommonPage {
this.scenarioCollapseButton = page.getByTestId('scenario-collapse-button');
this.timetableCollapseButton = page.getByTestId('timetable-collapse-button');
this.scenarioSideMenu = page.getByTestId('scenario-sidemenu');
}

// Function to wait for an element to be visible and then assert its visibility
static async waitForElementVisibility(locator: Locator): Promise<void> {
await locator.waitFor({ state: 'visible', timeout: 30 * 1000 });
await expect(locator).toBeVisible();
this.simulationResult = page.locator('.simulation-results');
}

// Get the button locator of a train element.
static getTrainButton(trainSelector: Locator): Locator {
return trainSelector.getByTestId('scenario-timetable-train-button');
}

// Wait for the simulation results to be in the DOM
async waitForSimulationResults(): Promise<void> {
await this.page.waitForSelector('.simulation-results', { state: 'attached' });
}

// Verify that the message "The timetable contains invalid trains" is visible
async verifyInvalidTrainsMessageVisibility(selectedLanguage: string): Promise<void> {
const translations = selectedLanguage === 'English' ? enTranslations : frTranslations;
Expand All @@ -94,10 +86,10 @@ class OperationalStudiesTimetablePage extends CommonPage {

// Verify that simulation results are displayed
async verifySimulationResultsVisibility(): Promise<void> {
await this.page.waitForLoadState('networkidle');
await this.page.waitForLoadState('load', { timeout: LOAD_PAGE_TIMEOUT });

const simulationResultsLocators = [
// TODO: remove this commented code when the design of simationBar has been changed
// TODO: remove this commented code when the design of simulation Bar has been changed
// this.simulationBar,
this.manchetteSpaceTimeChart,
this.speedSpaceChart,
Expand All @@ -106,9 +98,9 @@ class OperationalStudiesTimetablePage extends CommonPage {
this.timesStopsDataSheet,
];
await Promise.all(
simulationResultsLocators.map((simulationResultsLocator) =>
OperationalStudiesTimetablePage.waitForElementVisibility(simulationResultsLocator)
)
simulationResultsLocators.map(async (simulationResultsLocator) => {
await expect(simulationResultsLocator).toBeVisible();
})
);
}

Expand Down Expand Up @@ -151,7 +143,7 @@ class OperationalStudiesTimetablePage extends CommonPage {

// Verify that the imported train number is correct
async verifyTrainCount(trainCount: number): Promise<void> {
await this.page.waitForLoadState('networkidle');
await this.page.waitForLoadState('load', { timeout: LOAD_PAGE_TIMEOUT });
await expect(this.timetableTrains).toHaveCount(trainCount);
}

Expand Down Expand Up @@ -200,8 +192,8 @@ class OperationalStudiesTimetablePage extends CommonPage {
const trainCount = await this.timetableTrains.count();

for (let currentTrainIndex = 0; currentTrainIndex < trainCount; currentTrainIndex += 1) {
await this.page.waitForLoadState('networkidle');
await this.waitForSimulationResults();
await this.page.waitForLoadState('load', { timeout: LOAD_PAGE_TIMEOUT });
await this.simulationResult.waitFor();
const trainButton = OperationalStudiesTimetablePage.getTrainButton(
this.timetableTrains.nth(currentTrainIndex)
);
Expand All @@ -210,9 +202,8 @@ class OperationalStudiesTimetablePage extends CommonPage {
}
}

async verifyTimesStopsDataSheetVisibility(timeout = 60 * 1000): Promise<void> {
// Wait for the Times and Stops simulation dataSheet to be fully loaded with a specified timeout (default: 60 seconds)
await expect(this.timesStopsDataSheet).toBeVisible({ timeout });
async verifyTimesStopsDataSheetVisibility(): Promise<void> {
await expect(this.timesStopsDataSheet).toBeVisible({ timeout: SIMULATION_RESULT_TIMEOUT });
await this.timesStopsDataSheet.scrollIntoViewIfNeeded();
}

Expand All @@ -234,13 +225,13 @@ class OperationalStudiesTimetablePage extends CommonPage {

async clickOnScenarioCollapseButton() {
await expect(this.scenarioCollapseButton).toBeVisible();
await clickWithDelay(this.scenarioCollapseButton);
await this.scenarioCollapseButton.click();
await expect(this.scenarioSideMenu).toBeHidden();
}

async clickOnTimetableCollapseButton() {
await expect(this.timetableCollapseButton).toBeVisible();
await clickWithDelay(this.timetableCollapseButton);
await this.timetableCollapseButton.click();
await expect(this.scenarioSideMenu).toBeVisible();
}
}
Expand Down
4 changes: 2 additions & 2 deletions front/tests/pages/operational-studies-page-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class OperationalStudiesPage extends CommonPage {
async setTrainStartTime(departureTime: string) {
const currentDate = new Date().toISOString().split('T')[0];
const startTime = `${currentDate}T${departureTime}`;
await this.startTimeField.waitFor({ state: 'visible' });
await this.startTimeField.waitFor();
await this.startTimeField.fill(startTime);
await expect(this.startTimeField).toHaveValue(startTime);
}
Expand All @@ -107,7 +107,7 @@ class OperationalStudiesPage extends CommonPage {
}

async checkPathfindingDistance(distance: string | RegExp) {
await this.page.waitForSelector('[data-testid="result-pathfinding-distance"]');
await this.resultPathfindingDistance.waitFor();
await expect(this.resultPathfindingDistance).toHaveText(distance);
}

Expand Down
4 changes: 2 additions & 2 deletions front/tests/pages/project-page-model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, type Locator, type Page } from '@playwright/test';

import CommonPage from './common-page-model';
import HomePage from './home-page-model';
import { cleanText } from '../utils/dataNormalizer';

// Define the type for project details
Expand All @@ -13,7 +13,7 @@ type ProjectDetails = {
tags: string[];
};

class ProjectPage extends CommonPage {
class ProjectPage extends HomePage {
readonly projectNameLabel: Locator;

readonly updateProjectButton: Locator;
Expand Down
Loading

0 comments on commit 69856d9

Please sign in to comment.