Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions cypress/e2e/stop-registry/hybridStop.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import {
Priority,
ReusableComponentsVehicleModeEnum,
StopAreaInput,
StopRegistryGeoJsonType,
StopRegistryTransportModeType,
} from '@hsl/jore4-test-db-manager/dist/CypressSpecExports';
import { Tag } from '../../enums';
import { MapPage, StopDetailsPage, Toast } from '../../pageObjects';
import { InsertedStopRegistryIds } from '../utils';

// Test labels
const busStopLabel = 'H9901';
const busAreaCode = 'HYB01';
const tramAreaCode = 'HYB02';

// Coordinates where bus infra links are known to exist (same as createStop.cy.ts)
const testCoordinates = {
lat: 60.164074274478054,
lng: 24.93021804533524,
};

const stopAreaInput: Array<StopAreaInput> = [
{
StopArea: {
transportMode: StopRegistryTransportModeType.Bus,
name: { lang: 'fin', value: 'Hybrid bussi-alue' },
privateCode: { type: 'HSL/TEST', value: busAreaCode },
geometry: {
coordinates: [testCoordinates.lng, testCoordinates.lat],
type: StopRegistryGeoJsonType.Point,
},
},
organisations: null,
},
{
StopArea: {
transportMode: StopRegistryTransportModeType.Tram,
name: { lang: 'fin', value: 'Hybrid ratikka-alue' },
privateCode: { type: 'HSL/TEST', value: tramAreaCode },
geometry: {
coordinates: [testCoordinates.lng, testCoordinates.lat],
type: StopRegistryGeoJsonType.Point,
},
},
organisations: null,
},
];

describe(
'Hybrid stop (multi-transport-mode)',
{ tags: [Tag.StopRegistry, Tag.Stops] },
() => {
beforeEach(() => {
cy.task('resetDbs');

cy.task<InsertedStopRegistryIds>('insertStopRegistryData', {
stopPlaces: stopAreaInput,
stopPointsRequired: false,
});

cy.setupTests();
cy.mockLogin();
});

it('Should create a bus stop, make it hybrid (add tram), then remove tram', () => {
// Step 1: Create a bus stop on the map
MapPage.map.visit({
zoom: 16,
lat: testCoordinates.lat,
lng: testCoordinates.lng,
});

MapPage.createStopAtLocation({
stopFormInfo: {
publicCode: busStopLabel,
stopPlace: busAreaCode,
validityStartISODate: '2024-01-01',
priority: Priority.Standard,
reasonForChange: 'E2E test',
},
clickRelativePoint: {
xPercentage: 40,
yPercentage: 55,
},
vehicleMode: ReusableComponentsVehicleModeEnum.Bus,
});

MapPage.gqlStopShouldBeCreatedSuccessfully();
MapPage.checkStopSubmitSuccessToast();

// Step 2: Navigate to stop details page
StopDetailsPage.visit(busStopLabel);
StopDetailsPage.page().shouldBeVisible();

// Step 3: Open "Make hybrid" modal via extra actions menu
StopDetailsPage.titleRow.actionsMenuButton().click();
StopDetailsPage.titleRow.actionsMenuMakeHybridButton().click();

// Step 4: Select tram transport mode
StopDetailsPage.makeHybridModal.modal().shouldBeVisible();
StopDetailsPage.makeHybridModal.transportModeDropdown().click();
cy.get('[role="option"]').contains('Raitiovaunu').click();

// Step 5: Search and select the tram stop area
StopDetailsPage.makeHybridModal.stopAreaInput().type(tramAreaCode);
StopDetailsPage.makeHybridModal.stopAreaOption(tramAreaCode).click();

// Step 6: Confirm
StopDetailsPage.makeHybridModal.confirmButton().click();

Toast.expectSuccessToast('Yhteiskäyttöpysäkki luotu onnistuneesti');

// Step 7: Verify the mirrored quay card appears
StopDetailsPage.mirroredQuayDetails.cards().should('exist');

// Step 8: Remove the hybrid relation
StopDetailsPage.mirroredQuayDetails
.cards()
.first()
.within(() => {
StopDetailsPage.mirroredQuayDetails.removeButton().click();
});

// Confirm removal in the dialog
StopDetailsPage.mirroredQuayDetails.confirmationDialog
.getConfirmButton()
.click();

// Step 9: Verify the mirrored card is gone
StopDetailsPage.mirroredQuayDetails.cards().should('not.exist');
});
},
);
6 changes: 6 additions & 0 deletions cypress/pageObjects/stop-registry/StopDetailsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
InfoSpotsSection,
LocationDetailsSection,
MaintenanceSection,
MakeHybridStopModal,
MeasurementsSection,
MirroredQuayDetails,
SheltersSection,
SignageDetailsSection,
StopHeaderSummaryRow,
Expand Down Expand Up @@ -42,6 +44,10 @@ export class StopDetailsPage {

static headerSummaryRow = StopHeaderSummaryRow;

static makeHybridModal = MakeHybridStopModal;

static mirroredQuayDetails = MirroredQuayDetails;

static visit(label: string) {
cy.visit(`/stop-registry/stops/${label}`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export class MakeHybridStopModal {
static modal() {
return cy.getByTestId('MakeHybridStopModal');
}

static transportModeDropdown() {
return cy.getByTestId('MakeHybridStopModal::transportMode::ListboxButton');
}

static stopAreaInput() {
return cy.getByTestId('MakeHybridStopModal::stopAreaInput');
}

static stopAreaOption(code: string) {
return cy.getByTestId(`MakeHybridStopModal::stopArea::${code}`);
}

static confirmButton() {
return cy.getByTestId('MakeHybridStopModal::confirm');
}

static cancelButton() {
return cy.getByTestId('MakeHybridStopModal::cancel');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ConfirmationDialog } from '../../shared-components';

export class MirroredQuayDetails {
static cards() {
return cy.getByTestId('MirroredQuayDetails::container');
}

static removeButton() {
return cy.getByTestId('MirroredQuayDetails::remove');
}

static confirmationDialog = ConfirmationDialog;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ export class StopTitleRow {
static actionsMenuCopyButton() {
return cy.getByTestId('StopTitleRow::extraActions::copy');
}

static actionsMenuMakeHybridButton() {
return cy.getByTestId('StopTitleRow::extraActions::makeHybrid');
}
}
2 changes: 2 additions & 0 deletions cypress/pageObjects/stop-registry/stop-details/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ export * from './SignageDetailsSection';
export * from './SignageDetailsViewCard';
export * from './StopHeaderSummaryRow';
export * from './StopTitleRow';
export * from './MakeHybridStopModal';
export * from './MirroredQuayDetails';
2 changes: 2 additions & 0 deletions test-db-manager/src/types/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export enum KnownValueKey {
StopOwner = 'stopOwner',
OwnerContractId = 'owner-contractId',
OwnerNote = 'owner-note',
TimingPlaceId = 'timingPlaceId',
Mirrors = 'mirrors',
}

// Represents the values of hsl_municipality in LegacyHslMunicipalityCode table.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ export const DefaultHeaderButtons: FC<InfoContainerHeaderButtonsProps> = ({
<SimpleButton
shape="slim"
onClick={() => setIsExpanded((expanded) => !expanded)}
inverted={!isExpanded}
inverted={inverted ?? !isExpanded}
testId={testIds.toggle(testIdPrefix)}
>
{isExpanded ? (
<FaChevronUp className="text-white" aria-hidden />
<FaChevronUp
className={inverted ? 'text-tweaked-brand' : 'text-white'}
aria-hidden
/>
) : (
<FaChevronDown className="text-tweaked-brand" aria-hidden />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ export function useDeleteQuay() {
(stopPlaceId: string, quayId: string) =>
deleteQuay({
variables: { stopPlaceId, quayId },
refetchQueries: ['GetMapStops', 'getStopPlaceDetails'],
refetchQueries: [
'GetMapStops',
'getStopPlaceDetails',
'GetStopDetails',
],
awaitRefetchQueries: true,
}),
[deleteQuay],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FC, useContext, useState } from 'react';
import compact from 'lodash/compact';
import { FC, useContext, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { MdWarning } from 'react-icons/md';
import { Link } from 'react-router';
Expand All @@ -22,6 +23,7 @@ import {
} from './DetailTabSelector';
import { EditStopValidityButton } from './EditStopValidityButton';
import { StopExternalLinks } from './external-links/StopExternalLinks';
import { MirroredQuayDetailsCard } from './hybrid-stop';
import { SheltersInfoSpotsSection } from './info-spots/SheltersInfoSpots';
import { LocationDetailsSection } from './location-details';
import { MaintenanceSection } from './maintenance';
Expand Down Expand Up @@ -55,11 +57,20 @@ export const StopDetailsPage: FC = () => {
const selectDetailTab = (nextTab: DetailTabType) =>
requestNavigation(() => setActiveDetailTab(nextTab));

const { stopDetails, loading, error } = useGetStopDetails();
const { stopDetails, loading, error, mirroredQuays } = useGetStopDetails();

const mirroredTransportModes = useMemo(
() => compact(mirroredQuays.map((mq) => mq.stopPlace.transportMode)),
[mirroredQuays],
);

return (
<Container testId={testIds.page}>
<StopTitleRow stopDetails={stopDetails} label={label} />
<StopTitleRow
stopDetails={stopDetails}
label={label}
mirroredTransportModes={mirroredTransportModes}
/>
<StopHeaderSummaryRow className="my-2" stopDetails={stopDetails} />
<StopDetailsVersion label={label} />
<hr className="my-4" />
Expand Down Expand Up @@ -121,7 +132,13 @@ export const StopDetailsPage: FC = () => {
aria-labelledby={detailTabs.basic.buttonId}
role="tabpanel"
>
<BasicDetailsSection stop={stopDetails} />
<BasicDetailsSection
stop={stopDetails}
isHybrid={mirroredQuays.length > 0}
/>
{mirroredQuays.map((mq) => (
<MirroredQuayDetailsCard key={mq.quay.id} details={mq} />
))}
<LocationDetailsSection stop={stopDetails} />
<SignageDetailsSection stop={stopDetails} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FC, useRef } from 'react';
import { FC, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { mapTransportModeToStopTypeName } from '../../../../../i18n/uiNameMappings';
import { StopWithDetails } from '../../../../../types';
import { showSuccessToast, submitFormByRef } from '../../../../../utils';
import { InfoContainer, useInfoContainerControls } from '../../../../common';
Expand Down Expand Up @@ -32,9 +33,13 @@ const mapStopBasicDetailsDataToFormState = (stop: StopWithDetails) => {
};
type BasicDetailsSectionProps = {
readonly stop: StopWithDetails;
readonly isHybrid: boolean;
};

export const BasicDetailsSection: FC<BasicDetailsSectionProps> = ({ stop }) => {
export const BasicDetailsSection: FC<BasicDetailsSectionProps> = ({
stop,
isHybrid,
}) => {
const { t } = useTranslation();

const { saveStopPlaceDetails, defaultErrorHandler } =
Expand All @@ -60,11 +65,21 @@ export const BasicDetailsSection: FC<BasicDetailsSectionProps> = ({ stop }) => {

const defaultValues = mapStopBasicDetailsDataToFormState(stop);

const transportMode = stop.stop_place?.transportMode;
const title = useMemo(() => {
const base = t(($) => $.stopDetails.basicDetails.title);
if (!isHybrid || !transportMode) {
return base;
}
const modeName = mapTransportModeToStopTypeName(t, transportMode);
return `${base} | ${modeName}`;
}, [t, isHybrid, transportMode]);

return (
<InfoContainer
colors={stopInfoContainerColors}
controls={infoContainerControls}
title={t(($) => $.stopDetails.basicDetails.title)}
title={title}
testIdPrefix="BasicDetailsSection"
>
{infoContainerControls.isInEditMode && !!defaultValues ? (
Expand All @@ -73,6 +88,7 @@ export const BasicDetailsSection: FC<BasicDetailsSectionProps> = ({ stop }) => {
ref={formRef}
onSubmit={onSubmit}
stop={stop}
isHybrid={isHybrid}
onCancel={() => infoContainerControls.setIsInEditMode(false)}
testIdPrefix="BasicDetailsSection"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type StopBasicDetailsFormComponentProps = {
readonly defaultValues: Partial<StopBasicDetailsFormState>;
readonly onSubmit: (state: StopBasicDetailsFormState) => void;
readonly stop: StopWithDetails;
readonly isHybrid?: boolean;
readonly onCancel: () => void;
readonly testIdPrefix: string;
};
Expand All @@ -38,7 +39,15 @@ const StopBasicDetailsFormComponent: ForwardRefRenderFunction<
HTMLFormElement,
StopBasicDetailsFormComponentProps
> = (
{ className, defaultValues, onSubmit, stop, onCancel, testIdPrefix },
{
className,
defaultValues,
onSubmit,
stop,
isHybrid,
onCancel,
testIdPrefix,
},
ref,
) => {
const dispatch = useDispatch();
Expand Down Expand Up @@ -69,6 +78,7 @@ const StopBasicDetailsFormComponent: ForwardRefRenderFunction<
<HorizontalSeparator />
<StopOtherDetailsFormRow
onClickOpenTimingSettingsModal={openTimingPlaceModal}
isTransportModeLocked={isHybrid}
/>
<StopTypesFormRow />
<HorizontalSeparator />
Expand Down
Loading
Loading