diff --git a/e2e-tests/assistant-search.spec.ts b/e2e-tests/assistant-search.spec.ts new file mode 100644 index 00000000..2be490c3 --- /dev/null +++ b/e2e-tests/assistant-search.spec.ts @@ -0,0 +1,145 @@ +import { test, expect } from '@playwright/test'; + +const JST = 'Asia/Tokyo'; +const EET = 'Europe/Helsinki'; +const CTU = 'Europe/Oslo'; +const UTC = 'Europe/London'; +const PST = 'America/Los_Angeles'; + +const fromTextbox = 'Kristiansund'; +const fromOption = 'Kristiansund trafikkterminal'; +const toTextbox = 'Molde'; +const toOption = 'Molde trafikkterminal'; +const searchTime = '15:30'; +const expectedDeparture = '16:00 -'; + +test.describe('Trip search from different timezones - detailed view.', () => { + test.beforeEach(async ({ page }) => { + await page.goto(process.env.E2E_URL ?? 'http://localhost:3000'); + }); + + test.use({ + timezoneId: JST, + }); + test(JST + ' - (UTC +0900)', async ({ page }) => { + await page.getByTestId('searchTimeSelector-departBy').click(); + await page.getByTestId('searchTimeSelector-time').click(); + await page.getByTestId('searchTimeSelector-time').fill(searchTime); + await page.getByRole('textbox', { name: 'From' }).click(); + await page.getByRole('textbox', { name: 'From' }).fill(fromTextbox); + await page.getByRole('option', { name: fromOption }).click(); + await page.getByRole('textbox', { name: 'To' }).click(); + await page.getByRole('textbox', { name: 'To' }).fill(toTextbox); + await page + .getByRole('option', { + name: toOption, + }) + .click(); + await page.getByTestId('tripPattern-0-0').click(); + const elementText = await page + .getByTestId('detailsHeader-duration') + .textContent(); + + expect(elementText?.includes(expectedDeparture)); + }); + + test.use({ + timezoneId: EET, + }); + test(EET + ' - (UTC +0200)', async ({ page }) => { + await page.getByTestId('searchTimeSelector-departBy').click(); + await page.getByTestId('searchTimeSelector-time').click(); + await page.getByTestId('searchTimeSelector-time').fill(searchTime); + await page.getByRole('textbox', { name: 'From' }).click(); + await page.getByRole('textbox', { name: 'From' }).fill(fromTextbox); + await page.getByRole('option', { name: fromOption }).click(); + await page.getByRole('textbox', { name: 'To' }).click(); + await page.getByRole('textbox', { name: 'To' }).fill(toTextbox); + await page + .getByRole('option', { + name: toOption, + }) + .click(); + await page.getByTestId('tripPattern-0-0').click(); + const elementText = await page + .getByTestId('detailsHeader-duration') + .textContent(); + + expect(elementText?.includes(expectedDeparture)); + }); + + test.use({ + timezoneId: CTU, + }); + test(CTU + ' - (UTC +0100)', async ({ page }) => { + await page.getByTestId('searchTimeSelector-departBy').click(); + await page.getByTestId('searchTimeSelector-time').click(); + await page.getByTestId('searchTimeSelector-time').fill(searchTime); + await page.getByRole('textbox', { name: 'From' }).click(); + await page.getByRole('textbox', { name: 'From' }).fill(fromTextbox); + await page.getByRole('option', { name: fromOption }).click(); + await page.getByRole('textbox', { name: 'To' }).click(); + await page.getByRole('textbox', { name: 'To' }).fill(toTextbox); + await page + .getByRole('option', { + name: toOption, + }) + .click(); + await page.getByTestId('tripPattern-0-0').click(); + const elementText = await page + .getByTestId('detailsHeader-duration') + .textContent(); + + expect(elementText?.includes(expectedDeparture)); + }); + + test.use({ + timezoneId: UTC, + }); + test(UTC + ' - (UTC +0000)', async ({ page }) => { + await page.getByTestId('searchTimeSelector-departBy').click(); + await page.getByTestId('searchTimeSelector-time').click(); + await page.getByTestId('searchTimeSelector-time').fill(searchTime); + await page.getByRole('textbox', { name: 'From' }).click(); + await page.getByRole('textbox', { name: 'From' }).fill(fromTextbox); + await page.getByRole('option', { name: fromOption }).click(); + await page.getByRole('textbox', { name: 'To' }).click(); + await page.getByRole('textbox', { name: 'To' }).fill(toTextbox); + await page + .getByRole('option', { + name: toOption, + }) + .click(); + await page.getByTestId('tripPattern-0-0').click(); + const elementText = await page + .getByTestId('detailsHeader-duration') + .textContent(); + + expect(elementText?.includes(expectedDeparture)); + }); + + test.use({ + timezoneId: PST, + }); + test(PST + ' - (UTC -0800)', async ({ page }) => { + await page.getByTestId('searchTimeSelector-departBy').click(); + await page.getByTestId('searchTimeSelector-time').click(); + await page.getByTestId('searchTimeSelector-time').fill(searchTime); + await page.getByRole('textbox', { name: 'From' }).click(); + await page.getByRole('textbox', { name: 'From' }).fill(fromTextbox); + await page.getByRole('option', { name: fromOption }).click(); + await page.getByRole('textbox', { name: 'To' }).click(); + await page.getByRole('textbox', { name: 'To' }).fill(toTextbox); + await page + .getByRole('option', { + name: toOption, + }) + .click(); + await page.getByTestId('tripPattern-0-0').click(); + const elementText = await page + .getByTestId('detailsHeader-duration') + .textContent(); + + expect(elementText?.includes(expectedDeparture)); + }); +}); diff --git a/src/modules/search-time/selector/index.tsx b/src/modules/search-time/selector/index.tsx index 36d670b6..a2ed936e 100644 --- a/src/modules/search-time/selector/index.tsx +++ b/src/modules/search-time/selector/index.tsx @@ -8,6 +8,7 @@ import { } from '@atb/translations'; import { SEARCH_MODES, SearchMode, SearchTime } from '../types'; import style from './selector.module.css'; +import { formatLocalTimeToCET, setTimezone } from '@atb/utils/date'; type SearchTimeSelectorProps = { onChange: (state: SearchTime) => void; @@ -22,8 +23,12 @@ export default function SearchTimeSelector({ }: SearchTimeSelectorProps) { const { t } = useTranslation(); const [selectedMode, setSelectedMode] = useState(initialState); - const initialDate = - 'dateTime' in initialState ? new Date(initialState.dateTime) : new Date(); + const initialDate = setTimezone( + 'dateTime' in initialState + ? new Date(formatLocalTimeToCET(initialState.dateTime)) + : new Date(), + ) as Date; + const [selectedDate, setSelectedDate] = useState(initialDate); const [selectedTime, setSelectedTime] = useState(() => format(initialDate, 'HH:mm'), @@ -47,11 +52,23 @@ export default function SearchTimeSelector({ }; const resetToCurrentTime = () => { - setSelectedTime(() => format(new Date(), 'HH:mm')); + const newState = { + mode: selectedMode.mode, + dateTime: setTimezone(new Date()).getTime(), + }; + setSelectedTime(() => format(newState.dateTime, 'HH:mm')); + setSelectedMode(newState); + onChange(newState); }; const resetToCurrentDate = () => { - setSelectedDate(new Date()); + const newState = { + mode: selectedMode.mode, + dateTime: setTimezone(new Date()).getTime(), + }; + setSelectedDate(new Date(newState.dateTime)); + setSelectedMode(newState); + onChange(newState); }; const isPastDate = (selectedDate: string) => { @@ -102,7 +119,11 @@ export default function SearchTimeSelector({ style={{ '--number-of-options': options.length } as CSSProperties} > {options.map((state) => ( -