Files
TREK/client/src/repo/tripJournalLinkRepo.test.ts
T
Maurice fe05afec19 feat(trips): trip settings with a linked journal tab
The Share button becomes the trip's Settings, a round button beside the back
pill. The dialog is rebuilt around three areas (People, Sharing, Journal) with
a side navigation, a fixed height and 960 px width on wide screens.

Journal links one of your journals from the Journey addon to the trip. Every
trip member is ticked as a reader by default, each can be raised to editor or
left out, and the people a save would let in for the first time are named
before it saves. The link can be changed or removed. With a link the trip gets
a Journal tab on desktop and phone that shows the Journey addon's own view,
live through its sync and trip:journal:changed. Nothing shows with the addon
off.

members.shareTrip and nav.share are gone with the button that used them.
2026-10-18 17:41:25 +02:00

107 lines
4.4 KiB
TypeScript

/**
* FE-TJL-REPO-001 to FE-TJL-REPO-006
*
* `tripJournalLinkRepo` is online-only on purpose (who a journal is open to is
* the server's answer, never a cached guess), so what is pinned is that it
* refuses before a request while offline, that every call reaches its route
* with the body the contract names, and that the picker only offers journals
* the person owns.
*/
import type { TripJournalLinkResponse } from '@trek/shared';
import { http, HttpResponse } from 'msw';
import { afterEach, describe, expect, it } from 'vitest';
import { server } from '../../tests/helpers/msw/server';
import { _resetNetworkMode, setForcedOffline } from '../sync/networkMode';
import { tripJournalLinkRepo, TripJournalOfflineError } from './tripJournalLinkRepo';
const NONE: TripJournalLinkResponse = { link: null, can_link: true, can_unlink: false, can_edit_members: false };
afterEach(() => _resetNetworkMode());
describe('tripJournalLinkRepo', () => {
it('FE-TJL-REPO-001: refuses every call while offline, before a request leaves', async () => {
let calls = 0;
server.use(
http.get('/api/trips/:tripId/journal', () => {
calls += 1;
return HttpResponse.json(NONE);
})
);
setForcedOffline(true);
await expect(tripJournalLinkRepo.get(4)).rejects.toBeInstanceOf(TripJournalOfflineError);
await expect(tripJournalLinkRepo.candidates(4, 1)).rejects.toBeInstanceOf(TripJournalOfflineError);
await expect(tripJournalLinkRepo.link(4, 1, [])).rejects.toBeInstanceOf(TripJournalOfflineError);
await expect(tripJournalLinkRepo.setMembers(4, [])).rejects.toBeInstanceOf(TripJournalOfflineError);
await expect(tripJournalLinkRepo.unlink(4)).rejects.toBeInstanceOf(TripJournalOfflineError);
await expect(tripJournalLinkRepo.ownJournals(1)).rejects.toBeInstanceOf(TripJournalOfflineError);
expect(calls).toBe(0);
});
it('FE-TJL-REPO-002: reads the trip link', async () => {
server.use(http.get('/api/trips/4/journal', () => HttpResponse.json(NONE)));
expect(await tripJournalLinkRepo.get(4)).toEqual(NONE);
});
it('FE-TJL-REPO-003: asks for the candidates of one journal and hands back the list', async () => {
let asked: string | null = null;
server.use(
http.get('/api/trips/4/journal/candidates', ({ request }) => {
asked = new URL(request.url).searchParams.get('journey_id');
return HttpResponse.json({
candidates: [{ user_id: 2, username: 'lena', avatar_url: null, is_trip_owner: false, access: null }],
});
})
);
const list = await tripJournalLinkRepo.candidates(4, 9);
expect(asked).toBe('9');
expect(list.map((c) => c.username)).toEqual(['lena']);
});
it('FE-TJL-REPO-004: links, sets members and unlinks with the bodies the contract names', async () => {
const bodies: unknown[] = [];
server.use(
http.put('/api/trips/4/journal', async ({ request }) => {
bodies.push(await request.json());
return HttpResponse.json(NONE);
}),
http.patch('/api/trips/4/journal/members', async ({ request }) => {
bodies.push(await request.json());
return HttpResponse.json(NONE);
}),
http.delete('/api/trips/4/journal', () => HttpResponse.json(NONE))
);
await tripJournalLinkRepo.link(4, 9, [{ user_id: 2, role: 'viewer' }]);
await tripJournalLinkRepo.setMembers(4, [{ user_id: 2, role: 'editor' }]);
expect(await tripJournalLinkRepo.unlink(4)).toEqual(NONE);
expect(bodies).toEqual([
{ journey_id: 9, members: [{ user_id: 2, role: 'viewer' }] },
{ members: [{ user_id: 2, role: 'editor' }] },
]);
});
it('FE-TJL-REPO-005: offers only the journals the person owns', async () => {
server.use(
http.get('/api/journeys', () =>
HttpResponse.json({
journeys: [
{ id: 1, user_id: 7, title: 'Mine', subtitle: 'Japan' },
{ id: 2, user_id: 8, title: 'Theirs' },
{ id: 3, user_id: 7, title: 'Also mine' },
],
})
)
);
expect(await tripJournalLinkRepo.ownJournals(7)).toEqual([
{ id: 1, title: 'Mine', subtitle: 'Japan' },
{ id: 3, title: 'Also mine', subtitle: null },
]);
});
it('FE-TJL-REPO-006: the offline error says what it needs', () => {
const err = new TripJournalOfflineError();
expect(err.name).toBe('TripJournalOfflineError');
expect(err.message).toMatch(/connection/);
});
});