Skip to content

Commit

Permalink
front: add e2e tests for paced train filter
Browse files Browse the repository at this point in the history
Signed-off-by: SharglutDev <[email protected]>
  • Loading branch information
SharglutDev committed Mar 6, 2025
1 parent 99469e8 commit 48cb1ab
Show file tree
Hide file tree
Showing 9 changed files with 422 additions and 69 deletions.
2 changes: 1 addition & 1 deletion front/public/locales/en/operationalStudies/scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"advancedFilterLabel": "Rolling stock",
"choosePath": "Use this path",
"closeFilter": "Close filter",
"compositionCodes": "Composition codes",
"copy": "copy",
"delete": "Delete",
"deleteSelection": "Delete selected trains",
Expand Down Expand Up @@ -88,6 +87,7 @@
"showInvalidTrains": "Invalid",
"showNotHonoredTrains": "Partial",
"showValidTrains": "Valid",
"speedLimitTags": "Composition codes",
"stopsCount_one": "1 stop",
"stopsCount_other": "{{ count }} stops",
"toggleFilters": "Toggle filters display",
Expand Down
2 changes: 1 addition & 1 deletion front/public/locales/fr/operationalStudies/scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"advancedFilterLabel": "Engin moteur",
"choosePath": "Utiliser ce chemin",
"closeFilter": "Fermer le filtre",
"compositionCodes": "Codes de composition",
"copy": "copie",
"delete": "Supprimer",
"deleteSelection": "Supprimer les trains sélectionnés",
Expand Down Expand Up @@ -88,6 +87,7 @@
"showInvalidTrains": "Invalides",
"showNotHonoredTrains": "Partielle",
"showValidTrains": "Valides",
"speedLimitTags": "Codes de composition",
"stopsCount_one": "1 arrêt",
"stopsCount_other": "{{count}} arrêts",
"toggleFilters": "Afficher ou masquer les filtres",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const FilterPanel = ({ toggleFilterPanel, timetableFilters }: FilterPanelProps)
<Select
getOptionLabel={(option) => option.label}
getOptionValue={(option) => option.value}
id="train-validity"
id="timetable-train-validity-filter"
label={t('timetable.validityFilter')}
narrow
small
Expand All @@ -112,7 +112,7 @@ const FilterPanel = ({ toggleFilterPanel, timetableFilters }: FilterPanelProps)
<Select
getOptionLabel={(option) => option.label}
getOptionValue={(option) => option.value}
id="train-type"
id="timetable-train-type-filter"
label={t('timetable.trainType')}
narrow
small
Expand Down Expand Up @@ -146,7 +146,7 @@ const FilterPanel = ({ toggleFilterPanel, timetableFilters }: FilterPanelProps)
<Select
getOptionLabel={(option) => option.label}
getOptionValue={(option) => option.value}
id="train-keep-timetable"
id="timetable-train-punctuality-filter"
label={t('timetable.punctuality')}
narrow
small
Expand All @@ -164,9 +164,9 @@ const FilterPanel = ({ toggleFilterPanel, timetableFilters }: FilterPanelProps)
/>
</div>
</div>
<div className="compositions-code">
<label htmlFor="composition-tag-filter">{t('timetable.compositionCodes')}</label>
<div className="composition-tag-filter" id="composition-tag-filter">
<div className="speed-limit-tag">
<label htmlFor="timetable-speed-limit-tag-filter">{t('timetable.speedLimitTags')}</label>
<div className="speed-limit-tag-filter" id="timetable-speed-limit-tag-filter">
{uniqueTags.map((tag) => {
const displayTag = tag !== 'NO CODE' ? tag : t('timetable.noSpeedLimitTagsShort');
return (
Expand Down
26 changes: 26 additions & 0 deletions front/src/modules/trainschedule/components/Timetable/Timetable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,37 @@ const Timetable = ({
{
...trainSchedulesWithDetails[0],
id: formatEditoastTrainIdToPacedTrainId(12345),
trainName: 'Paced train 1',
labels: ['Paced-Train-Tag-1'],
paced: {
duration: Duration.parse('PT2H'),
step: Duration.parse('PT30M'),
},
},
{
...trainSchedulesWithDetails[0],
id: formatEditoastTrainIdToPacedTrainId(123456),
trainName: 'Paced train 2',
invalidReason: 'rolling_stock_not_found',
isValid: false,
scheduledPointsNotHonored: false,
paced: {
duration: Duration.parse('PT1H'),
step: Duration.parse('PT10M'),
},
},
{
...trainSchedulesWithDetails[0],
id: formatEditoastTrainIdToPacedTrainId(1234567),
trainName: 'Paced train 3',
notHonoredReason: 'scheduleNotHonored',
isValid: true,
scheduledPointsNotHonored: true,
paced: {
duration: Duration.parse('PT2H'),
step: Duration.parse('PT5M'),
},
},
]
: trainSchedulesWithDetails
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@
}
}

.compositions-code {
.speed-limit-tag {
padding-top: 13px;

label {
Expand All @@ -767,7 +767,7 @@
font-weight: 600;
margin-bottom: 4px;
}
.composition-tag-filter {
.speed-limit-tag-filter {
display: flex;
flex-wrap: wrap;
gap: 12px;
Expand Down
192 changes: 172 additions & 20 deletions front/tests/008-train-schedule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,72 @@ import {
trainScheduleScenarioName,
trainScheduleStudyName,
} from './assets/constants/project-const';
import {
HONORED_ITEMS,
HONORED_TRAINS,
INVALID_AND_NOT_HONORED_TRAINS,
INVALID_ITEMS,
INVALID_TRAINS,
ITEMS_WITH_NO_SPEED_LIMIT_TAG,
LABEL_FILTERED_ITEMS,
NAME_FILTERED_ITEMS,
NOT_HONORED_ITEMS,
NOT_HONORED_PACED_TRAINS,
NOT_HONORED_TRAINS,
ROLLING_STOCK_FILTERED_ITEMS,
TOTAL_ITEMS,
TOTAL_PACED_TRAINS,
TOTAL_TRAINS,
VALID_AND_HONORED_TRAINS,
VALID_ITEMS,
VALID_PACED_TRAINS,
VALID_TRAINS,
} from './assets/constants/timetable-items-count';
import test from './logging-fixture';
import OperationalStudiesPage from './pages/operational-studies/operational-studies-page';
import ScenarioTimetableSection from './pages/operational-studies/scenario-timetable-section';
import { waitForInfraStateToBeCached } from './utils';
import { getTranslations, waitForInfraStateToBeCached } from './utils';
import { getInfra, getProject, getScenario, getStudy } from './utils/api-utils';
import readJsonFile from './utils/file-utils';
import type { CommonTranslations, TimetableFilterTranslations } from './utils/types';

const enScenarioTranslations: TimetableFilterTranslations = readJsonFile(
'public/locales/en/operationalStudies/scenario.json'
);
const frScenarioTranslations: TimetableFilterTranslations = readJsonFile(
'public/locales/fr/operationalStudies/scenario.json'
);

const enCommonTranslations: CommonTranslations = readJsonFile('public/locales/en/translation.json');
const frCommonTranslations: CommonTranslations = readJsonFile('public/locales/fr/translation.json');

test.describe('Verify train schedule elements and filters', () => {
test.slow();
test.use({ viewport: { width: 1920, height: 1080 } });

let scenarioTimetableSection: ScenarioTimetableSection;
let operationalStudiesPage: OperationalStudiesPage;

let project: Project;
let study: Study;
let scenario: Scenario;
let infra: Infra;

// Constants for expected train counts
const TOTAL_TRAINS = 21;
const VALID_TRAINS = 17;
const INVALID_TRAINS = 4;
const HONORED_TRAINS = 14;
const NOT_HONORED_TRAINS = 3;
const VALID_AND_HONORED_TRAINS = 14;
const INVALID_AND_NOT_HONORED_TRAINS = 0;
let translations: TimetableFilterTranslations & CommonTranslations;

test.beforeAll('Fetch project, study and scenario with train schedule', async () => {
project = await getProject(trainScheduleProjectName);
study = await getStudy(project.id, trainScheduleStudyName);
scenario = await getScenario(project.id, study.id, trainScheduleScenarioName);
infra = await getInfra();
translations = getTranslations({
en: { ...enScenarioTranslations, ...enCommonTranslations },
fr: { ...frScenarioTranslations, ...frCommonTranslations },
});
});

test.beforeEach('Navigate to scenario page before each test', async ({ page }) => {
scenarioTimetableSection = new ScenarioTimetableSection(page);
operationalStudiesPage = new OperationalStudiesPage(page);
await page.goto(
`/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}`
);
Expand All @@ -52,31 +84,59 @@ test.describe('Verify train schedule elements and filters', () => {
await scenarioTimetableSection.verifyTrainCount(TOTAL_TRAINS);
await scenarioTimetableSection.verifyInvalidTrainsMessageVisibility();
await scenarioTimetableSection.checkSelectedTimetableTrain();
await scenarioTimetableSection.filterValidityAndVerifyTrainCount('Valid', VALID_TRAINS);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'Valid',
VALID_TRAINS,
translations
);
await scenarioTimetableSection.verifyEachTrainSimulation();
});

// TODO Paced train - remove this test in https://github.com/OpenRailAssociation/osrd/issues/10791
/** *************** Test 2 **************** */
test('Filtering imported trains', async () => {
// Verify train count and apply different filters for validity and honored status
await scenarioTimetableSection.verifyTrainCount(TOTAL_TRAINS);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount('Invalid', INVALID_TRAINS);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount('All', TOTAL_TRAINS);
await scenarioTimetableSection.filterHonoredAndVerifyTrainCount('Honored', HONORED_TRAINS);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'Invalid',
INVALID_TRAINS,
translations
);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'All',
TOTAL_TRAINS,
translations
);
await scenarioTimetableSection.filterHonoredAndVerifyTrainCount(
'Honored',
HONORED_TRAINS,
translations
);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'Valid',
VALID_AND_HONORED_TRAINS
VALID_AND_HONORED_TRAINS,
translations
);
await scenarioTimetableSection.filterHonoredAndVerifyTrainCount(
'Not honored',
NOT_HONORED_TRAINS
NOT_HONORED_TRAINS,
translations
);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'Invalid',
INVALID_AND_NOT_HONORED_TRAINS
INVALID_AND_NOT_HONORED_TRAINS,
translations
);
await scenarioTimetableSection.filterHonoredAndVerifyTrainCount(
'All',
INVALID_TRAINS,
translations
);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'All',
TOTAL_TRAINS,
translations
);
await scenarioTimetableSection.filterHonoredAndVerifyTrainCount('All', INVALID_TRAINS);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount('All', TOTAL_TRAINS);

// Verify train composition filters with predefined filter codes and expected counts
const compositionFilters = [
Expand All @@ -87,7 +147,99 @@ test.describe('Verify train schedule elements and filters', () => {
];

for (const filter of compositionFilters) {
await scenarioTimetableSection.clickCodeCompoTrainFilterButton(filter.code, filter.count);
await scenarioTimetableSection.filterSpeedLimitTagAndVerifyTrainCount(
filter.code,
filter.count,
translations
);
}
await scenarioTimetableSection.verifyTrainCount(TOTAL_TRAINS);
});

// TODO Paced train : update this test with real data in https://github.com/OpenRailAssociation/osrd/issues/10615
/** *************** Test 3 **************** */
test('Filtering imported trains and paced trains', async () => {
await operationalStudiesPage.checkPacedTrainSwitch();

// While the back end for paced trains isn't ready, 3 paced trains are hardcoded and
// added to the list of train schedules for testing purposes.
// These 3 paced trains are copy of the first train schedule in the list (1 valid, 1 not invalid, 1 not honored).
await scenarioTimetableSection.verifyTotalItemsLabel(translations, {
totalPacedTrainCount: TOTAL_PACED_TRAINS,
totalTrainScheduleCount: TOTAL_TRAINS,
});

await scenarioTimetableSection.checkTimetableFilterVisibilityLabelDefaultValue(
translations.timetable,
{ inputDefaultValue: '', selectDefaultValue: 'both' }
);

// Name and label filter
await scenarioTimetableSection.filterNameAndVerifyTrainCount(
'Paced Train 1',
NAME_FILTERED_ITEMS
);
await scenarioTimetableSection.filterNameAndVerifyTrainCount(
'Paced-Train-Tag-1',
LABEL_FILTERED_ITEMS
);

// Rolling stock name and details filter
await scenarioTimetableSection.filterRollingStockAndVerifyTrainCount(
'slow_rolling_stock',
ROLLING_STOCK_FILTERED_ITEMS
);

// Validity filter
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'Invalid',
INVALID_ITEMS,
translations
);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'Valid',
VALID_ITEMS,
translations
);

// Punctuality filter
await scenarioTimetableSection.filterHonoredAndVerifyTrainCount(
'Honored',
HONORED_ITEMS,
translations
);
await scenarioTimetableSection.filterHonoredAndVerifyTrainCount(
'Not honored',
NOT_HONORED_ITEMS,
translations
);

// Train type filter
await scenarioTimetableSection.filterTrainTypeAndVerifyTrainCount(
'Service',
NOT_HONORED_PACED_TRAINS
);
await scenarioTimetableSection.filterHonoredAndVerifyTrainCount(
'All',
VALID_PACED_TRAINS,
translations
);
await scenarioTimetableSection.filterValidityAndVerifyTrainCount(
'All',
TOTAL_PACED_TRAINS,
translations
);

await scenarioTimetableSection.filterTrainTypeAndVerifyTrainCount('Unique train', TOTAL_TRAINS);
await scenarioTimetableSection.filterTrainTypeAndVerifyTrainCount('All', TOTAL_ITEMS);

// Verify train composition filters with predefined filter codes and expected counts
// TODO Paced train : add a paced train with a unique compo code in https://github.com/OpenRailAssociation/osrd/issues/10615
await scenarioTimetableSection.filterSpeedLimitTagAndVerifyTrainCount(
null,
ITEMS_WITH_NO_SPEED_LIMIT_TAG,
translations
);
await scenarioTimetableSection.verifyTrainCount(TOTAL_ITEMS);
});
});
Loading

0 comments on commit 48cb1ab

Please sign in to comment.