mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
Tours: bring the pinned counts and test doubles up to the new tables and switch
- tours locale key count, entity count and NOT NULL field count follow the wanderer tables and strings - the shared MSW admin handlers answer /api/admin/tours-wanderer - the mobile tour detail test checks the dialog, not the dropped heading - the refused-origin e2e sends one request per case instead of four at once
This commit is contained in:
@@ -98,13 +98,14 @@ describe('MSelectedTourDetail', () => {
|
|||||||
it('renders the selected tour through the shared detail dialog and elevation profile', () => {
|
it('renders the selected tour through the shared detail dialog and elevation profile', () => {
|
||||||
render(<MSelectedTourDetail planner={planner()} />)
|
render(<MSelectedTourDetail planner={planner()} />)
|
||||||
|
|
||||||
expect(screen.getAllByText('Track Stats')).toHaveLength(1)
|
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||||
expect(document.querySelector('#tour-elevation-42')).toBeInTheDocument()
|
expect(document.querySelector('#tour-elevation-42')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders nothing for an ordinary selected place', () => {
|
it('renders nothing for an ordinary selected place', () => {
|
||||||
render(<MSelectedTourDetail planner={planner({ selectedTour: null })} />)
|
render(<MSelectedTourDetail planner={planner({ selectedTour: null })} />)
|
||||||
|
|
||||||
expect(screen.queryByText('Track Stats')).not.toBeInTheDocument()
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||||
|
expect(document.querySelector('#tour-elevation-42')).not.toBeInTheDocument()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -76,6 +76,15 @@ export const adminHandlers = [
|
|||||||
return HttpResponse.json({ enabled: body.enabled });
|
return HttpResponse.json({ enabled: body.enabled });
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
http.get('/api/admin/tours-wanderer', () => {
|
||||||
|
return HttpResponse.json({ enabled: false });
|
||||||
|
}),
|
||||||
|
|
||||||
|
http.put('/api/admin/tours-wanderer', async ({ request }) => {
|
||||||
|
const body = await request.json() as Record<string, unknown>;
|
||||||
|
return HttpResponse.json({ enabled: body.enabled });
|
||||||
|
}),
|
||||||
|
|
||||||
http.get('/api/admin/addons', () => {
|
http.get('/api/admin/addons', () => {
|
||||||
return HttpResponse.json({ addons: [] });
|
return HttpResponse.json({ addons: [] });
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -536,21 +536,21 @@ describe('Tours e2e (real guards + temp SQLite)', () => {
|
|||||||
expect(await listed(derived)).toMatchObject({ derived_from_place_id: null });
|
expect(await listed(derived)).toMatchObject({ derived_from_place_id: null });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('400 for an origin that is a planned tour, a tour of another trip or no tour at all', async () => {
|
const refusedOrigins: Record<string, () => Promise<number>> = {
|
||||||
const planned = await createViaApi();
|
'a planned tour': () => createViaApi(),
|
||||||
const foreign = await importedTour(6);
|
'a tour of another trip': () => importedTour(6),
|
||||||
const plain = await plainPlace(5, 'Cafe');
|
'no tour at all': () => plainPlace(5, 'Cafe'),
|
||||||
// One request per origin at once; each answers on its own.
|
'a place that does not exist': async () => 999_999,
|
||||||
const answers = await Promise.all(
|
};
|
||||||
[planned, foreign, plain, 999_999].map((id) =>
|
it.each(Object.keys(refusedOrigins))('400 for an origin that is %s, and nothing is written', async (kind) => {
|
||||||
request(server)
|
const origin = await refusedOrigins[kind]();
|
||||||
.post('/api/trips/5/tours')
|
const before = await countRows(orm, Tours, {});
|
||||||
.set('Cookie', sessionCookie(OWNER))
|
const res = await request(server)
|
||||||
.send(tourBody({ derived_from_place_id: id })),
|
.post('/api/trips/5/tours')
|
||||||
),
|
.set('Cookie', sessionCookie(OWNER))
|
||||||
);
|
.send(tourBody({ derived_from_place_id: origin }));
|
||||||
expect(answers.map((res) => res.status)).toEqual([400, 400, 400, 400]);
|
expect(res.status).toBe(400);
|
||||||
expect(await countRows(orm, Tours, { place: { $nin: [planned, foreign] } })).toBe(0);
|
expect(await countRows(orm, Tours, {})).toBe(before);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('planning an import over in place keeps its recorded track, and restoring brings it back', async () => {
|
it('planning an import over in place keeps its recorded track, and restoring brings it back', async () => {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ describe('ALL_ENTITIES', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plan 2 Task 3: every one of the 133 entities the migrated schema
|
* Plan 2 Task 3: every one of the 135 entities the migrated schema
|
||||||
* describes (120 rewritten + the 5 document-sync tables added this task,
|
* describes (120 rewritten + the 5 document-sync tables added this task,
|
||||||
* + push_subscriptions and google_api_usage from legacy steps 245 and 254,
|
* + push_subscriptions and google_api_usage from legacy steps 245 and 254,
|
||||||
* + tour_types, tours and tour_waypoints from the Tours migration,
|
* + tour_types, tours and tour_waypoints from the Tours migration,
|
||||||
@@ -42,8 +42,8 @@ describe('ALL_ENTITIES', () => {
|
|||||||
* `entity-schema-parity.test.ts` can't see — it iterates the metadata map,
|
* `entity-schema-parity.test.ts` can't see — it iterates the metadata map,
|
||||||
* which de-duplicates by class name).
|
* which de-duplicates by class name).
|
||||||
*/
|
*/
|
||||||
it('ENT-003: exactly 133 entities, every class name unique', () => {
|
it('ENT-003: exactly 135 entities, every class name unique', () => {
|
||||||
expect(ALL_ENTITIES.length).toBe(133);
|
expect(ALL_ENTITIES.length).toBe(135);
|
||||||
const names = ALL_ENTITIES.map((s) => s.meta.className);
|
const names = ALL_ENTITIES.map((s) => s.meta.className);
|
||||||
expect(new Set(names).size).toBe(names.length);
|
expect(new Set(names).size).toBe(names.length);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
|||||||
// + scheduler_leases: name, owner, expires_at (the scheduler always writes all three).
|
// + scheduler_leases: name, owner, expires_at (the scheduler always writes all three).
|
||||||
// + user_sessions: id, user_id, expires_at.
|
// + user_sessions: id, user_id, expires_at.
|
||||||
// + ferrata_tiles: tile_key, payload, fetched_at (a fetched tile always carries all three).
|
// + ferrata_tiles: tile_key, payload, fetched_at (a fetched tile always carries all three).
|
||||||
const KNOWN_UNINITIALISED_NOT_NULL_SCALARS = 449;
|
const KNOWN_UNINITIALISED_NOT_NULL_SCALARS = 455;
|
||||||
|
|
||||||
describe('entity class fields never carry a NaN initialiser (task-4-review-shape.md, Important 1)', () => {
|
describe('entity class fields never carry a NaN initialiser (task-4-review-shape.md, Important 1)', () => {
|
||||||
it('CLASSFIELD-001: new X() has no own property whose value is NaN, across all 133 entities', () => {
|
it('CLASSFIELD-001: new X() has no own property whose value is NaN, across all 133 entities', () => {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const tokens = (value: string) => [...value.matchAll(/\{[^{}]+\}/g)].map((match)
|
|||||||
// placeholder spec; here only the general keys are.
|
// placeholder spec; here only the general keys are.
|
||||||
const groups = pluralGroups(Object.keys(english));
|
const groups = pluralGroups(Object.keys(english));
|
||||||
const generalKeys = (keys: string[]) => keys.filter((key) => !pluralFormOf(key, groups));
|
const generalKeys = (keys: string[]) => keys.filter((key) => !pluralFormOf(key, groups));
|
||||||
const GENERAL_KEY_COUNT = 154;
|
const GENERAL_KEY_COUNT = 196;
|
||||||
|
|
||||||
function literalKeys(source: string): string[] {
|
function literalKeys(source: string): string[] {
|
||||||
const tree = ts.createSourceFile('locale.ts', source, ts.ScriptTarget.Latest, true);
|
const tree = ts.createSourceFile('locale.ts', source, ts.ScriptTarget.Latest, true);
|
||||||
@@ -34,7 +34,7 @@ function literalKeys(source: string): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('Tours locale contracts', () => {
|
describe('Tours locale contracts', () => {
|
||||||
it('covers all 27 supported locales with 154 general source keys', () => {
|
it('covers all 27 supported locales with 196 general source keys', () => {
|
||||||
expect(Object.keys(domains)).toHaveLength(27);
|
expect(Object.keys(domains)).toHaveLength(27);
|
||||||
expect(Object.keys(domains).sort()).toEqual(SUPPORTED_LANGUAGE_CODES.map((code) => `./${code}/tours.ts`).sort());
|
expect(Object.keys(domains).sort()).toEqual(SUPPORTED_LANGUAGE_CODES.map((code) => `./${code}/tours.ts`).sort());
|
||||||
expect(generalKeys(Object.keys(english))).toHaveLength(GENERAL_KEY_COUNT);
|
expect(generalKeys(Object.keys(english))).toHaveLength(GENERAL_KEY_COUNT);
|
||||||
|
|||||||
Reference in New Issue
Block a user