Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: issue with caching when selecting transport filter #229

Merged
merged 13 commits into from
Feb 16, 2024
2 changes: 1 addition & 1 deletion .github/workflows/playwright-scheduled-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Run Playwright tests
run: yarn playwright test
env:
NEXT_PUBLIC_PLANNER_ORG_ID: atb
NEXT_PUBLIC_PLANNER_ORG_ID: ${{ matrix.env }}
E2E_URL: 'https://${{ matrix.env }}-staging.planner-web.mittatb.no/'

- uses: actions/upload-artifact@v4
Expand Down
79 changes: 79 additions & 0 deletions e2e-tests/assistant-view.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { test, expect } from '@playwright/test';

test.use({
geolocation: { longitude: 62.4722, latitude: 6.1495 },
permissions: ['geolocation'],
});

test('Should fetch Kristiansund - Molde and loading more after first result', async ({
page,
}) => {
await page.goto(process.env.E2E_URL ?? 'http://localhost:3000');

await page.getByRole('textbox', { name: 'From' }).click();
await page.getByRole('textbox', { name: 'From' }).fill('Kristiansund');
await page
.getByRole('option', { name: 'Kristiansund Kristiansund', exact: true })
.click();

await page.getByRole('textbox', { name: 'To' }).click();
await page.getByRole('textbox', { name: 'To' }).fill('Molde');

const initialRequest = page.waitForResponse((request) => {
return request.url().includes('trip') && request.url().includes('cursor');
});

await page.getByRole('option', { name: 'Molde Molde', exact: true }).click();

await initialRequest;

await page.getByRole('button', { name: 'More choices' }).click();
await page.getByText('Bus', { exact: true }).click();
await page.getByRole('button', { name: 'Find departures' }).click();

const tripPatternItem = page.getByTestId('tripPattern-0-0');
await tripPatternItem.waitFor();

const additionalRequest = page.waitForRequest((request) => {
return request.url().includes('trip') && request.url().includes('cursor');
});
await page.getByRole('button', { name: 'Load more results' }).click();

await additionalRequest;
});

test('should show non transit trips on walkable distance', async ({ page }) => {
await page.goto(process.env.E2E_URL ?? 'http://localhost:3000');
await page.getByRole('textbox', { name: 'From' }).click();
await page.getByRole('textbox', { name: 'From' }).fill('Fylkeshuset i Møre');
await page
.getByRole('option', { name: 'Fylkeshuset i Møre og Romsdal' })
.click();
await page.getByRole('textbox', { name: 'To' }).click();
await page.getByRole('textbox', { name: 'To' }).fill('Roseby');
await page.getByRole('option', { name: 'Roseby Molde', exact: true }).click();

await page.getByTestId('non-transit-pill-foot').click();

await expect(
page.getByRole('heading', { name: 'Fylkeshuset i Møre og Romsdal' }),
).toBeVisible();
});

test('should show non transit trips on cyclable distance', async ({ page }) => {
await page.goto(process.env.E2E_URL ?? 'http://localhost:3000');
await page.getByRole('textbox', { name: 'From' }).click();
await page.getByRole('textbox', { name: 'From' }).fill('Fylkeshuset i Møre');
await page
.getByRole('option', { name: 'Fylkeshuset i Møre og Romsdal' })
.click();
await page.getByRole('textbox', { name: 'To' }).click();
await page.getByRole('textbox', { name: 'To' }).fill('Roseby');
await page.getByRole('option', { name: 'Roseby Molde', exact: true }).click();

await page.getByTestId('non-transit-pill-bicycle').click();

await expect(
page.getByRole('heading', { name: 'Fylkeshuset i Møre og Romsdal' }),
).toBeVisible();
});
88 changes: 88 additions & 0 deletions e2e-tests/fram-specific/assistant.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { test, expect } from '@playwright/test';

test.use({
geolocation: { longitude: 62.4722, latitude: 6.1495 },
permissions: ['geolocation'],
});

test.describe('fram only', () => {
test.skip(
() => process.env.NEXT_PUBLIC_PLANNER_ORG_ID !== 'fram',
'Only FRAM!',
);

test('Should filter on line 701', async ({ page }) => {
await page.goto(process.env.E2E_URL ?? 'http://localhost:3000');

await page.getByRole('textbox', { name: 'From' }).click();
await page.getByRole('textbox', { name: 'From' }).fill('Kvam');
await page.getByRole('option', { name: 'Kvam skole Molde' }).click();

await page.getByRole('textbox', { name: 'To' }).click();
await page.getByRole('textbox', { name: 'To' }).fill('Fylkeshusa');

const tripResponse = page.waitForResponse((request) => {
return request.url().includes('assistant/trip');
});
await page.getByRole('option', { name: 'Fylkeshusa Molde' }).click();

await page.getByRole('button', { name: 'More choices' }).click();

await tripResponse;

await page.getByPlaceholder('line number').click();
await page.getByPlaceholder('line number').fill('701');

const tripResponse2 = page.waitForResponse((request) => {
return request.url().includes('assistant/trip');
});
await page.getByRole('button', { name: 'Find departures' }).click();
await tripResponse2;

const tripPatternItem2 = page.getByTestId('tripPattern-0-0');
await tripPatternItem2.waitFor();

await expect(tripPatternItem2).toBeVisible();
expect(await tripPatternItem2.getAttribute('aria-label')).toContain('701');
});

test('should show boats and message on Correspondance', async ({ page }) => {
await page.goto(process.env.E2E_URL ?? 'http://localhost:3000');

await page.getByRole('textbox', { name: 'From' }).click();
await page.getByRole('textbox', { name: 'From' }).fill('Moa trafikktermin');
await page.getByRole('option', { name: 'Moa trafikkterminal' }).click();

await page.getByRole('textbox', { name: 'To' }).click();
await page.getByRole('textbox', { name: 'To' }).fill('Ulsteinvik');

const additionalRequest = page.waitForResponse((request) => {
return request.url().includes('trip');
});

await page
.getByRole('option', { name: 'Ulsteinvik Ulstein', exact: true })
.click();

await additionalRequest;

await page.getByTestId('tripPattern-0-0').waitFor();

await page.getByRole('button', { name: 'More choices' }).click();

const additionalRequest2 = page.waitForResponse((request) => {
return (
request.url().includes('trip') && request.url().includes('expressboat')
);
});
await page.locator('label').filter({ hasText: 'Express boat' }).click();
await page.getByText('Bus', { exact: true }).click();

await page.getByText('Loading travel suggestions...').waitFor();

await additionalRequest2;

await page.getByText('1145').first().click();
await expect(page.getByText('Correspondance between 1145')).toBeVisible();
});
});
29 changes: 0 additions & 29 deletions e2e-tests/search-test.spec.ts

This file was deleted.

2 changes: 2 additions & 0 deletions src/page-modules/assistant/__tests__/assistant.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ describe('assistant page', function () {
transportModeFilter: null,
cursor: null,
lineFilter: null,
via: null,
};
const fromToTripQuery2: FromToTripQuery = {
from: fromFeature,
Expand Down Expand Up @@ -286,6 +287,7 @@ describe('assistant page', function () {
transportModeFilter: null,
cursor: null,
lineFilter: null,
via: null,
};

addAssistantTripToCache(cachedFromToTripQuery, tripResult);
Expand Down
1 change: 1 addition & 0 deletions src/page-modules/assistant/non-transit-pill/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function NonTransitTrip({ nonTransit }: NonTransitTripProps) {
size="pill"
href={`/assistant/${nonTransit.compressedQuery}`}
title={`${modeText} ${durationShort}`}
testID={`non-transit-pill-${mode}`}
icon={{
left: <TransportMonoIcon mode={{ transportMode: mode }} />,
right: <MonoIcon icon="navigation/ArrowRight" />,
Expand Down
20 changes: 14 additions & 6 deletions src/page-modules/assistant/server/trip-cache.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import TTLCache from '@isaacs/ttlcache';
import { FromToTripQuery, TripData } from '../types';
import { createTripQuery, tripQueryToQueryString } from '../utils';

let tripCache: TTLCache<string, TripData> | null = null;

Expand All @@ -15,16 +14,25 @@ function getTripCacheInstance(): TTLCache<string, TripData> {
export function getAssistantTripIfCached(
query: FromToTripQuery,
): TripData | undefined {
const queryString = tripQueryToQueryString(createTripQuery(query));
if (tripCache?.has(`/api/assistant/trip?${queryString}`)) {
return tripCache.get(`/api/assistant/trip?${queryString}`);
const cacheKey = createCacheKey(query);
if (tripCache?.has(cacheKey)) {
return tripCache.get(cacheKey);
}
}

export function addAssistantTripToCache(
query: FromToTripQuery,
tripData: TripData,
) {
const queryString = tripQueryToQueryString(createTripQuery(query));
getTripCacheInstance().set(`/api/assistant/trip?${queryString}`, tripData);
getTripCacheInstance().set(createCacheKey(query), tripData);
}

function createCacheKey(valuesToCreateCacheKey: FromToTripQuery) {
const keys = Object.keys(valuesToCreateCacheKey).sort();
const values = keys.map(
(key) => valuesToCreateCacheKey[key as keyof FromToTripQuery],
);
const cacheKey = JSON.stringify(values);

return 'assistant-trip-' + Buffer.from(cacheKey).toString('base64');
}
1 change: 1 addition & 0 deletions src/page-modules/assistant/trip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export default function Trip({ tripQuery, fallback }: TripProps) {
tripPattern={tripPattern}
delay={i * 0.1}
index={i}
testId={`tripPattern-${tripIndex}-${i}`}
/>
</div>
)),
Expand Down
4 changes: 3 additions & 1 deletion src/page-modules/assistant/trip/trip-pattern/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ type TripPatternProps = {
tripPattern: TripPatternType;
delay: number;
index: number;
testId?: string;
};

export default function TripPattern({
tripPattern,
delay,
index,
testId,
}: TripPatternProps) {
const { t, language } = useTranslation();

Expand Down Expand Up @@ -70,7 +72,7 @@ export default function TripPattern({
<motion.a
href={`/assistant/${tripPattern.compressedQuery}`}
className={className}
data-testid={`trip-pattern-${index}`}
data-testid={testId}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: maxOpacity, x: 0 }}
exit={{ opacity: 0, x: -10 }}
Expand Down
Loading