mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 22:31:46 +00:00
fix: repair test suite after SWR offline-read changes
Add navigator.onLine guard to SWR refresh IIFEs so background network calls don't fire in offline mode (prevents fake-IDB leakage in tests via MSW default handlers). Fix IDB isolation in affected test files by flushing pending macro tasks then clearing IDB tables in beforeEach, so stale IDB writes from previous tests' background IIFEs don't bleed into the next test. Restore loadBudgetItems and refreshPlaces to apply background refresh results to store state. Move tags/categories API calls before the main Promise.all in loadTrip so MSW handlers resolve during the await window.
This commit is contained in:
@@ -10,8 +10,11 @@ import { usePermissionsStore } from '../../store/permissionsStore';
|
||||
import { resetAllStores, seedStore } from '../../../tests/helpers/store';
|
||||
import { buildUser, buildTrip, buildBudgetItem, buildSettings } from '../../../tests/helpers/factories';
|
||||
import BudgetPanel from './BudgetPanel';
|
||||
import { offlineDb } from '../../db/offlineDb';
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
||||
await Promise.all(offlineDb.tables.map(t => t.clear()));
|
||||
resetAllStores();
|
||||
// Settlement and per-person APIs needed by BudgetPanel
|
||||
server.use(
|
||||
|
||||
@@ -9,6 +9,7 @@ import { buildUser, buildTrip, buildTripFile } from '../../tests/helpers/factori
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useTripStore } from '../store/tripStore';
|
||||
import FilesPage from './FilesPage';
|
||||
import { offlineDb } from '../db/offlineDb';
|
||||
|
||||
vi.mock('../components/Files/FileManager', () => ({
|
||||
default: ({ files }: { files: unknown[]; onUpload: unknown; onDelete: unknown }) =>
|
||||
@@ -29,7 +30,9 @@ function renderFilesPage(tripId: number | string = 1) {
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
||||
await Promise.all(offlineDb.tables.map(t => t.clear()));
|
||||
vi.clearAllMocks();
|
||||
resetAllStores();
|
||||
seedStore(useAuthStore, { isAuthenticated: true, user: buildUser() });
|
||||
|
||||
@@ -9,6 +9,7 @@ export const accommodationRepo = {
|
||||
.where('trip_id').equals(Number(tripId)).toArray()
|
||||
|
||||
const refresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await accommodationsApi.list(tripId)
|
||||
upsertAccommodations(result.accommodations || []).catch(() => {})
|
||||
|
||||
@@ -11,6 +11,7 @@ export const budgetRepo = {
|
||||
.toArray()
|
||||
|
||||
const refresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await budgetApi.list(tripId)
|
||||
upsertBudgetItems(result.items)
|
||||
|
||||
@@ -11,6 +11,7 @@ export const dayRepo = {
|
||||
.sortBy('day_number' as keyof Day)) as Day[]
|
||||
|
||||
const refresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await daysApi.list(tripId)
|
||||
upsertDays(result.days)
|
||||
|
||||
@@ -11,6 +11,7 @@ export const fileRepo = {
|
||||
.toArray()
|
||||
|
||||
const refresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await filesApi.list(tripId)
|
||||
upsertTripFiles(result.files)
|
||||
|
||||
@@ -11,6 +11,7 @@ export const packingRepo = {
|
||||
.toArray()
|
||||
|
||||
const refresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await packingApi.list(tripId)
|
||||
upsertPackingItems(result.items)
|
||||
|
||||
@@ -11,6 +11,7 @@ export const placeRepo = {
|
||||
.toArray()
|
||||
|
||||
const refresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await placesApi.list(tripId, params)
|
||||
upsertPlaces(result.places)
|
||||
|
||||
@@ -11,6 +11,7 @@ export const reservationRepo = {
|
||||
.toArray()
|
||||
|
||||
const refresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await reservationsApi.list(tripId)
|
||||
upsertReservations(result.reservations)
|
||||
|
||||
@@ -11,6 +11,7 @@ export const todoRepo = {
|
||||
.toArray()
|
||||
|
||||
const refresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await todoApi.list(tripId)
|
||||
upsertTodoItems(result.items)
|
||||
|
||||
@@ -11,6 +11,7 @@ export const tripRepo = {
|
||||
const all = await offlineDb.trips.toArray()
|
||||
|
||||
const refresh: TripsRefresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const [active, archived] = await Promise.all([
|
||||
tripsApi.list(),
|
||||
@@ -41,6 +42,7 @@ export const tripRepo = {
|
||||
const cached = await offlineDb.trips.get(Number(tripId))
|
||||
|
||||
const refresh: TripRefresh = (async () => {
|
||||
if (!navigator.onLine) return null
|
||||
try {
|
||||
const result = await tripsApi.get(tripId)
|
||||
upsertTrip(result.trip)
|
||||
|
||||
@@ -4,8 +4,11 @@ import { server } from '../../../tests/helpers/msw/server';
|
||||
import { resetAllStores, seedStore } from '../../../tests/helpers/store';
|
||||
import { buildBudgetItem } from '../../../tests/helpers/factories';
|
||||
import { useTripStore } from '../tripStore';
|
||||
import { offlineDb } from '../../db/offlineDb';
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
||||
await Promise.all(offlineDb.tables.map(t => t.clear()));
|
||||
resetAllStores();
|
||||
server.resetHandlers();
|
||||
});
|
||||
|
||||
@@ -24,6 +24,9 @@ export const createBudgetSlice = (set: SetState, get: GetState): BudgetSlice =>
|
||||
try {
|
||||
const data = await budgetRepo.list(tripId)
|
||||
set({ budgetItems: data.items })
|
||||
data.refresh.then(fresh => {
|
||||
if (fresh) set({ budgetItems: fresh.items })
|
||||
}).catch(() => {})
|
||||
} catch (err: unknown) {
|
||||
console.error('Failed to load budget items:', err)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ export const createPlacesSlice = (set: SetState, get: GetState): PlacesSlice =>
|
||||
try {
|
||||
const data = await placeRepo.list(tripId)
|
||||
set({ places: data.places })
|
||||
data.refresh.then(fresh => {
|
||||
if (fresh) set({ places: fresh.places })
|
||||
}).catch(() => {})
|
||||
} catch (err: unknown) {
|
||||
console.error('Failed to refresh places:', err)
|
||||
}
|
||||
|
||||
@@ -89,6 +89,15 @@ export const useTripStore = create<TripStoreState>((set, get) => ({
|
||||
loadTrip: async (tripId: number | string) => {
|
||||
set({ isLoading: true, error: null })
|
||||
try {
|
||||
// Fire tags/categories network refresh immediately — they're global (not trip-specific)
|
||||
// and must be in-flight before the await below so MSW resolves them during the wait
|
||||
const tagsRefresh = tagsApi.list()
|
||||
.then(fresh => { upsertTags(fresh.tags).catch(() => {}); return fresh })
|
||||
.catch(() => null)
|
||||
const categoriesRefresh = categoriesApi.list()
|
||||
.then(fresh => { upsertCategories(fresh.categories).catch(() => {}); return fresh })
|
||||
.catch(() => null)
|
||||
|
||||
// All reads from IndexedDB — instant, no network wait
|
||||
const [tripData, daysData, placesData, packingData, todoData, cachedTags, cachedCategories] = await Promise.all([
|
||||
tripRepo.get(tripId),
|
||||
@@ -100,14 +109,6 @@ export const useTripStore = create<TripStoreState>((set, get) => ({
|
||||
offlineDb.categories.toArray(),
|
||||
])
|
||||
|
||||
// Tags/categories background refresh (network-only, applied when ready)
|
||||
const tagsRefresh = tagsApi.list()
|
||||
.then(fresh => { upsertTags(fresh.tags).catch(() => {}); return fresh })
|
||||
.catch(() => null)
|
||||
const categoriesRefresh = categoriesApi.list()
|
||||
.then(fresh => { upsertCategories(fresh.categories).catch(() => {}); return fresh })
|
||||
.catch(() => null)
|
||||
|
||||
const buildMaps = (days: Day[]) => {
|
||||
const assignmentsMap: AssignmentsMap = {}
|
||||
const dayNotesMap: DayNotesMap = {}
|
||||
|
||||
Reference in New Issue
Block a user