mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
e0efa0314a
A tourRepo replaces the direct toursApi calls: reads fall back to a new Dexie tours table, and a tour saved or edited offline is written there and queued like a place, under one temporary id for the place and its facet. The queue moves both to the real id on replay and drops both when the server refuses the create. Trip sync caches each trip's tours while the addon is on, so a tour no longer reads as a plain place offline. The tour metrics move to shared so the offline copy shows the server's numbers, and useTourPlaceIds keeps one Set per change as intended.
102 lines
3.9 KiB
TypeScript
102 lines
3.9 KiB
TypeScript
/**
|
|
* mutationQueue replay of offline tour writes: a tour is a place plus its
|
|
* facet, so a create moves both from the temporary id to the real one, points
|
|
* queued edits at it, and a refused create takes both back out.
|
|
*/
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
import 'fake-indexeddb/auto'
|
|
import { http, HttpResponse } from 'msw'
|
|
import type { TourCreateRequest, TourListItem } from '@trek/shared'
|
|
import { server } from '../../helpers/msw/server'
|
|
import { setAuthed } from '../../../src/sync/authGate'
|
|
import { mutationQueue } from '../../../src/sync/mutationQueue'
|
|
import { offlineDb, clearAll } from '../../../src/db/offlineDb'
|
|
import { tourRepo } from '../../../src/repo/tourRepo'
|
|
import { placeRepo } from '../../../src/repo/placeRepo'
|
|
|
|
function setOnline(v: boolean): void {
|
|
Object.defineProperty(navigator, 'onLine', { value: v, writable: true, configurable: true })
|
|
}
|
|
|
|
const body: TourCreateRequest = {
|
|
name: 'Lake loop',
|
|
tour_type: 'hike',
|
|
route_geometry: [[47, 11, 1000], [47.02, 11, 1050]],
|
|
waypoints: [
|
|
{ lat: 47, lng: 11, role: 'start', sequence: 0 },
|
|
{ lat: 47.02, lng: 11, role: 'end', sequence: 1 },
|
|
],
|
|
max_hiking_difficulty: 2,
|
|
duration_seconds: null,
|
|
}
|
|
|
|
function savedTour(placeId: number, name = 'Lake loop'): TourListItem {
|
|
return {
|
|
place_id: placeId, name, tour_type: 'hike', distance: 2.2, elevation_gain: 50, elevation_loss: 0, duration: null,
|
|
difficulty: null, wanderer_ref: null, match_confidence: 1, max_hiking_difficulty: 2, planned: false, caution: false,
|
|
has_waypoints: true,
|
|
}
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
await clearAll()
|
|
mutationQueue._resetFlushing()
|
|
setAuthed(true)
|
|
setOnline(false)
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks()
|
|
setAuthed(false)
|
|
setOnline(true)
|
|
})
|
|
|
|
describe('mutationQueue.flush — tours', () => {
|
|
it('FE-SYNC-TOUR-001: a replayed create moves place and facet to the real id and aims the queued edit at it', async () => {
|
|
const { tour } = await tourRepo.create(3, body)
|
|
const tempId = tour.place_id
|
|
await tourRepo.update(3, tempId, { ...body, name: 'Renamed' })
|
|
const puts: string[] = []
|
|
server.use(
|
|
http.post('/api/trips/3/tours', () => HttpResponse.json({ tour: savedTour(501), waypoints: body.waypoints })),
|
|
http.put('/api/trips/3/tours/:id', ({ params }) => {
|
|
puts.push(String(params.id))
|
|
return HttpResponse.json({ tour: savedTour(501, 'Renamed'), waypoints: body.waypoints })
|
|
}),
|
|
)
|
|
setOnline(true)
|
|
|
|
await mutationQueue.flush()
|
|
|
|
expect(puts).toEqual(['501'])
|
|
expect(await offlineDb.mutationQueue.count()).toBe(0)
|
|
expect(await offlineDb.tours.get(tempId)).toBeUndefined()
|
|
expect(await offlineDb.places.get(tempId)).toBeUndefined()
|
|
expect(await offlineDb.tours.get(501)).toMatchObject({ trip_id: 3, name: 'Renamed', waypoints: body.waypoints })
|
|
// The server built its place from the same route; it stays on the map until the next list.
|
|
expect(await offlineDb.places.get(501)).toMatchObject({ trip_id: 3, name: 'Renamed' })
|
|
})
|
|
|
|
it('FE-SYNC-TOUR-002: a create the server refuses takes its place and facet back out', async () => {
|
|
const { tour } = await tourRepo.create(3, body)
|
|
server.use(http.post('/api/trips/3/tours', () => HttpResponse.json({ error: 'Tour type is not available' }, { status: 400 })))
|
|
setOnline(true)
|
|
|
|
await mutationQueue.flush()
|
|
|
|
expect(await offlineDb.tours.get(tour.place_id)).toBeUndefined()
|
|
expect(await offlineDb.places.get(tour.place_id)).toBeUndefined()
|
|
expect((await offlineDb.mutationQueue.toArray())[0]).toMatchObject({ status: 'failed' })
|
|
})
|
|
|
|
it('FE-SYNC-TOUR-003: deleting a tour offline drops its facet with the place', async () => {
|
|
await offlineDb.tours.put({ ...savedTour(40), trip_id: 3 })
|
|
|
|
await placeRepo.delete(3, 40)
|
|
await offlineDb.tours.put({ ...savedTour(41), trip_id: 3 })
|
|
await placeRepo.deleteMany(3, [41])
|
|
|
|
expect(await offlineDb.tours.count()).toBe(0)
|
|
})
|
|
})
|