mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 06:11:45 +00:00
Derive client domain types from the shared schema contracts
Add entity/response Zod schemas to @trek/shared (place, trip, assignment, day, budget, packing, reservation), each matched against the producing server service, and re-export them from client types.ts instead of the hand-written duplicates that had drifted (name/title, amount/total_price, owner_id/user_id, cover_url/cover_image, ...). Updates the call sites and test fixtures the corrected types surfaced; type-only, no runtime behaviour change.
This commit is contained in:
@@ -33,14 +33,15 @@ import type { Trip, Day, Place, PackingItem, TodoItem, BudgetItem, Reservation,
|
||||
|
||||
const makeTrip = (id = 1): Trip => ({
|
||||
id,
|
||||
name: `Trip ${id}`,
|
||||
user_id: 42,
|
||||
title: `Trip ${id}`,
|
||||
description: null,
|
||||
start_date: '2026-07-01',
|
||||
end_date: '2026-07-05',
|
||||
cover_url: null,
|
||||
is_archived: false,
|
||||
currency: 'EUR',
|
||||
cover_image: null,
|
||||
is_archived: 0,
|
||||
reminder_days: 3,
|
||||
owner_id: 42,
|
||||
created_at: '2026-01-01T00:00:00Z',
|
||||
updated_at: '2026-01-01T00:00:00Z',
|
||||
});
|
||||
@@ -65,7 +66,6 @@ const makePlace = (id: number, tripId = 1): Place => ({
|
||||
lng: 2.3522,
|
||||
address: null,
|
||||
category_id: null,
|
||||
icon: null,
|
||||
price: null,
|
||||
currency: null,
|
||||
image_url: null,
|
||||
@@ -102,14 +102,14 @@ describe('offlineDb — trips', () => {
|
||||
await upsertTrip(trip);
|
||||
const stored = await offlineDb.trips.get(10);
|
||||
expect(stored).toBeDefined();
|
||||
expect(stored!.name).toBe('Trip 10');
|
||||
expect(stored!.title).toBe('Trip 10');
|
||||
});
|
||||
|
||||
it('upsertTrip overwrites an existing trip (put semantics)', async () => {
|
||||
await upsertTrip(makeTrip(1));
|
||||
await upsertTrip({ ...makeTrip(1), name: 'Updated' });
|
||||
await upsertTrip({ ...makeTrip(1), title: 'Updated' });
|
||||
const stored = await offlineDb.trips.get(1);
|
||||
expect(stored!.name).toBe('Updated');
|
||||
expect(stored!.title).toBe('Updated');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -133,7 +133,7 @@ describe('offlineDb — places', () => {
|
||||
|
||||
describe('offlineDb — packing / todo / budget / reservations / files', () => {
|
||||
it('upserts packing items', async () => {
|
||||
const item: PackingItem = { id: 1, trip_id: 1, name: 'Passport', category: null, checked: 0, quantity: 1 };
|
||||
const item: PackingItem = { id: 1, trip_id: 1, name: 'Passport', category: null, checked: 0, sort_order: 0, quantity: 1 };
|
||||
await upsertPackingItems([item]);
|
||||
expect(await offlineDb.packingItems.count()).toBe(1);
|
||||
});
|
||||
@@ -149,8 +149,8 @@ describe('offlineDb — packing / todo / budget / reservations / files', () => {
|
||||
|
||||
it('upserts budget items', async () => {
|
||||
const item: BudgetItem = {
|
||||
id: 1, trip_id: 1, name: 'Flight', amount: 500, currency: 'EUR',
|
||||
category: 'Transport', paid_by: null, persons: 1, members: [], expense_date: null,
|
||||
id: 1, trip_id: 1, name: 'Flight', total_price: 500,
|
||||
category: 'Transport', persons: 1, members: [], expense_date: null, sort_order: 0,
|
||||
};
|
||||
await upsertBudgetItems([item]);
|
||||
expect(await offlineDb.budgetItems.count()).toBe(1);
|
||||
@@ -158,8 +158,8 @@ describe('offlineDb — packing / todo / budget / reservations / files', () => {
|
||||
|
||||
it('upserts reservations', async () => {
|
||||
const item: Reservation = {
|
||||
id: 1, trip_id: 1, name: 'Hotel', type: 'hotel', status: 'confirmed',
|
||||
date: null, time: null, confirmation_number: null, notes: null, url: null, created_at: '2026-01-01T00:00:00Z',
|
||||
id: 1, trip_id: 1, title: 'Hotel', type: 'hotel', status: 'confirmed',
|
||||
reservation_time: null, confirmation_number: null, notes: null, created_at: '2026-01-01T00:00:00Z',
|
||||
};
|
||||
await upsertReservations([item]);
|
||||
expect(await offlineDb.reservations.count()).toBe(1);
|
||||
@@ -168,7 +168,7 @@ describe('offlineDb — packing / todo / budget / reservations / files', () => {
|
||||
it('upserts trip files', async () => {
|
||||
const file: TripFile = {
|
||||
id: 1, trip_id: 1, filename: 'ticket.pdf', original_name: 'Ticket.pdf',
|
||||
mime_type: 'application/pdf', created_at: '2026-01-01T00:00:00Z',
|
||||
mime_type: 'application/pdf', url: '/api/trips/1/files/1/download', created_at: '2026-01-01T00:00:00Z',
|
||||
};
|
||||
await upsertTripFiles([file]);
|
||||
expect(await offlineDb.tripFiles.count()).toBe(1);
|
||||
@@ -238,7 +238,7 @@ describe('offlineDb — clearTripData', () => {
|
||||
await upsertTrip(makeTrip(1));
|
||||
await upsertDays([makeDay(1, 1), makeDay(2, 1)]);
|
||||
await upsertPlaces([makePlace(10, 1)]);
|
||||
const item: PackingItem = { id: 5, trip_id: 1, name: 'Towel', category: null, checked: 0, quantity: 1 };
|
||||
const item: PackingItem = { id: 5, trip_id: 1, name: 'Towel', category: null, checked: 0, sort_order: 0, quantity: 1 };
|
||||
await upsertPackingItems([item]);
|
||||
|
||||
// Also add data for a different trip — should NOT be removed
|
||||
|
||||
@@ -2,15 +2,15 @@ import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useTripStore } from '../../../src/store/tripStore';
|
||||
import { resetAllStores } from '../../helpers/store';
|
||||
import { buildBudgetItem } from '../../helpers/factories';
|
||||
import type { BudgetMember } from '../../../src/types';
|
||||
import type { BudgetItemMember } from '../../../src/types';
|
||||
|
||||
beforeEach(() => {
|
||||
resetAllStores();
|
||||
});
|
||||
|
||||
describe('remoteEventHandler > budget', () => {
|
||||
const member1: BudgetMember = { user_id: 5, paid: false };
|
||||
const member2: BudgetMember = { user_id: 6, paid: true };
|
||||
const member1: BudgetItemMember = { user_id: 5, paid: 0, username: 'eve' };
|
||||
const member2: BudgetItemMember = { user_id: 6, paid: 1, username: 'frank' };
|
||||
|
||||
const seedData = () => {
|
||||
useTripStore.setState({
|
||||
@@ -40,12 +40,12 @@ describe('remoteEventHandler > budget', () => {
|
||||
|
||||
it('FE-WSEVT-BUDGET-003: budget:updated replaces item in array', () => {
|
||||
seedData();
|
||||
const updated = buildBudgetItem({ id: 1, name: 'Updated Hotel', amount: 500 });
|
||||
const updated = buildBudgetItem({ id: 1, name: 'Updated Hotel', total_price: 500 });
|
||||
useTripStore.getState().handleRemoteEvent({ type: 'budget:updated', item: updated });
|
||||
const { budgetItems } = useTripStore.getState();
|
||||
const item = budgetItems.find(i => i.id === 1);
|
||||
expect(item?.name).toBe('Updated Hotel');
|
||||
expect(item?.amount).toBe(500);
|
||||
expect(item?.total_price).toBe(500);
|
||||
});
|
||||
|
||||
it('FE-WSEVT-BUDGET-004: budget:deleted removes item by ID', () => {
|
||||
@@ -58,7 +58,7 @@ describe('remoteEventHandler > budget', () => {
|
||||
|
||||
it('FE-WSEVT-BUDGET-005: budget:members-updated replaces entire members array and persons count', () => {
|
||||
seedData();
|
||||
const newMembers: BudgetMember[] = [{ user_id: 7, paid: true }, { user_id: 8, paid: false }];
|
||||
const newMembers: BudgetItemMember[] = [{ user_id: 7, paid: 1, username: 'grace' }, { user_id: 8, paid: 0, username: 'heidi' }];
|
||||
useTripStore.getState().handleRemoteEvent({
|
||||
type: 'budget:members-updated',
|
||||
itemId: 1,
|
||||
@@ -86,8 +86,8 @@ describe('remoteEventHandler > budget', () => {
|
||||
const item = budgetItems.find(i => i.id === 1);
|
||||
const m = item?.members?.find(m => m.user_id === 5);
|
||||
expect(m?.paid).toBe(true);
|
||||
// Other item members unchanged
|
||||
// Other item members unchanged (member2 keeps its seeded paid value)
|
||||
const item2 = budgetItems.find(i => i.id === 2);
|
||||
expect(item2?.members?.[0].paid).toBe(true);
|
||||
expect(item2?.members?.[0].paid).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,13 +10,13 @@ beforeEach(() => {
|
||||
describe('remoteEventHandler > reservations', () => {
|
||||
const seedData = () => {
|
||||
useTripStore.setState({
|
||||
reservations: [buildReservation({ id: 1, name: 'Hotel Paris' })],
|
||||
reservations: [buildReservation({ id: 1, title: 'Hotel Paris' })],
|
||||
});
|
||||
};
|
||||
|
||||
it('FE-WSEVT-RESERV-001: reservation:created prepends new reservation to array', () => {
|
||||
seedData();
|
||||
const newRes = buildReservation({ id: 99, name: 'Flight' });
|
||||
const newRes = buildReservation({ id: 99, title: 'Flight' });
|
||||
useTripStore.getState().handleRemoteEvent({ type: 'reservation:created', reservation: newRes });
|
||||
const { reservations } = useTripStore.getState();
|
||||
expect(reservations).toHaveLength(2);
|
||||
@@ -25,19 +25,19 @@ describe('remoteEventHandler > reservations', () => {
|
||||
|
||||
it('FE-WSEVT-RESERV-002: reservation:created is idempotent — no duplicate if same ID', () => {
|
||||
seedData();
|
||||
const duplicate = buildReservation({ id: 1, name: 'Hotel Paris Dup' });
|
||||
const duplicate = buildReservation({ id: 1, title: 'Hotel Paris Dup' });
|
||||
useTripStore.getState().handleRemoteEvent({ type: 'reservation:created', reservation: duplicate });
|
||||
const { reservations } = useTripStore.getState();
|
||||
expect(reservations).toHaveLength(1);
|
||||
expect(reservations[0].name).toBe('Hotel Paris');
|
||||
expect(reservations[0].title).toBe('Hotel Paris');
|
||||
});
|
||||
|
||||
it('FE-WSEVT-RESERV-003: reservation:updated replaces reservation in array', () => {
|
||||
seedData();
|
||||
const updated = buildReservation({ id: 1, name: 'Hotel Lyon' });
|
||||
const updated = buildReservation({ id: 1, title: 'Hotel Lyon' });
|
||||
useTripStore.getState().handleRemoteEvent({ type: 'reservation:updated', reservation: updated });
|
||||
const { reservations } = useTripStore.getState();
|
||||
expect(reservations[0].name).toBe('Hotel Lyon');
|
||||
expect(reservations[0].title).toBe('Hotel Lyon');
|
||||
});
|
||||
|
||||
it('FE-WSEVT-RESERV-004: reservation:deleted removes reservation by ID', () => {
|
||||
@@ -49,8 +49,8 @@ describe('remoteEventHandler > reservations', () => {
|
||||
|
||||
it('FE-WSEVT-RESERV-005: reservation:created ordering — newest is first', () => {
|
||||
seedData();
|
||||
const r2 = buildReservation({ id: 2, name: 'Second' });
|
||||
const r3 = buildReservation({ id: 3, name: 'Third' });
|
||||
const r2 = buildReservation({ id: 2, title: 'Second' });
|
||||
const r3 = buildReservation({ id: 3, title: 'Third' });
|
||||
useTripStore.getState().handleRemoteEvent({ type: 'reservation:created', reservation: r2 });
|
||||
useTripStore.getState().handleRemoteEvent({ type: 'reservation:created', reservation: r3 });
|
||||
const { reservations } = useTripStore.getState();
|
||||
|
||||
@@ -9,21 +9,21 @@ beforeEach(() => {
|
||||
|
||||
describe('remoteEventHandler > trip', () => {
|
||||
it('FE-WSEVT-TRIP-001: trip:updated replaces trip in state', () => {
|
||||
const originalTrip = buildTrip({ id: 1, name: 'Paris Trip' });
|
||||
const originalTrip = buildTrip({ id: 1, title: 'Paris Trip' });
|
||||
useTripStore.setState({ trip: originalTrip });
|
||||
const updatedTrip = buildTrip({ id: 1, name: 'Paris & Lyon Trip' });
|
||||
const updatedTrip = buildTrip({ id: 1, title: 'Paris & Lyon Trip' });
|
||||
useTripStore.getState().handleRemoteEvent({ type: 'trip:updated', trip: updatedTrip });
|
||||
const { trip } = useTripStore.getState();
|
||||
expect(trip?.name).toBe('Paris & Lyon Trip');
|
||||
expect(trip?.title).toBe('Paris & Lyon Trip');
|
||||
});
|
||||
|
||||
it('FE-WSEVT-TRIP-002: trip:updated does not affect other state fields', () => {
|
||||
const existingPlace = buildPlace({ id: 55, name: 'Eiffel Tower' });
|
||||
useTripStore.setState({
|
||||
trip: buildTrip({ id: 1, name: 'Original' }),
|
||||
trip: buildTrip({ id: 1, title: 'Original' }),
|
||||
places: [existingPlace],
|
||||
});
|
||||
const updatedTrip = buildTrip({ id: 1, name: 'Updated' });
|
||||
const updatedTrip = buildTrip({ id: 1, title: 'Updated' });
|
||||
useTripStore.getState().handleRemoteEvent({ type: 'trip:updated', trip: updatedTrip });
|
||||
const { places } = useTripStore.getState();
|
||||
expect(places).toHaveLength(1);
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('budgetSlice', () => {
|
||||
const existing = buildBudgetItem({ trip_id: 1 });
|
||||
seedStore(useTripStore, { budgetItems: [existing] });
|
||||
|
||||
const result = await useTripStore.getState().addBudgetItem(1, { name: 'Hotel', amount: 200 });
|
||||
const result = await useTripStore.getState().addBudgetItem(1, { name: 'Hotel', total_price: 200 });
|
||||
|
||||
expect(result.name).toBe('Hotel');
|
||||
expect(useTripStore.getState().budgetItems).toHaveLength(2);
|
||||
@@ -64,7 +64,7 @@ describe('budgetSlice', () => {
|
||||
|
||||
describe('updateBudgetItem', () => {
|
||||
it('FE-BUDGET-004: updateBudgetItem replaces item in array', async () => {
|
||||
const item = buildBudgetItem({ id: 10, trip_id: 1, name: 'Old', amount: 100 });
|
||||
const item = buildBudgetItem({ id: 10, trip_id: 1, name: 'Old', total_price: 100 });
|
||||
seedStore(useTripStore, { budgetItems: [item] });
|
||||
|
||||
server.use(
|
||||
@@ -74,16 +74,16 @@ describe('budgetSlice', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await useTripStore.getState().updateBudgetItem(1, 10, { name: 'Updated', amount: 150 });
|
||||
const result = await useTripStore.getState().updateBudgetItem(1, 10, { name: 'Updated', total_price: 150 });
|
||||
|
||||
expect(result.name).toBe('Updated');
|
||||
expect(useTripStore.getState().budgetItems[0].name).toBe('Updated');
|
||||
});
|
||||
|
||||
it('FE-BUDGET-005: updateBudgetItem with total_price triggers loadReservations when reservation_id present', async () => {
|
||||
const item = buildBudgetItem({ id: 10, trip_id: 1, amount: 100 });
|
||||
const item = buildBudgetItem({ id: 10, trip_id: 1, total_price: 100 });
|
||||
const initialReservation = buildReservation({ trip_id: 1 });
|
||||
const newReservation = buildReservation({ trip_id: 1, name: 'Refreshed Reservation' });
|
||||
const newReservation = buildReservation({ trip_id: 1, title: 'Refreshed Reservation' });
|
||||
seedStore(useTripStore, {
|
||||
budgetItems: [item],
|
||||
reservations: [initialReservation],
|
||||
@@ -106,7 +106,7 @@ describe('budgetSlice', () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
expect(useTripStore.getState().reservations).toHaveLength(1);
|
||||
expect(useTripStore.getState().reservations[0].name).toBe('Refreshed Reservation');
|
||||
expect(useTripStore.getState().reservations[0].title).toBe('Refreshed Reservation');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,7 +162,7 @@ describe('budgetSlice', () => {
|
||||
|
||||
describe('toggleBudgetMemberPaid', () => {
|
||||
it('FE-BUDGET-008: toggleBudgetMemberPaid updates paid status after API success', async () => {
|
||||
const member = { user_id: 5, paid: false };
|
||||
const member = { user_id: 5, paid: 0, username: 'dave' };
|
||||
const item = buildBudgetItem({ id: 10, trip_id: 1, members: [member] });
|
||||
seedStore(useTripStore, { budgetItems: [item] });
|
||||
|
||||
|
||||
@@ -42,20 +42,20 @@ describe('reservationsSlice', () => {
|
||||
|
||||
describe('addReservation', () => {
|
||||
it('FE-RESERV-002: addReservation prepends to reservations array', async () => {
|
||||
const existing = buildReservation({ trip_id: 1, name: 'Existing' });
|
||||
const existing = buildReservation({ trip_id: 1, title: 'Existing' });
|
||||
seedStore(useTripStore, { reservations: [existing] });
|
||||
|
||||
const result = await useTripStore.getState().addReservation(1, {
|
||||
name: 'New Hotel',
|
||||
title: 'New Hotel',
|
||||
type: 'hotel',
|
||||
status: 'pending',
|
||||
});
|
||||
|
||||
expect(result.name).toBe('New Hotel');
|
||||
expect(result.title).toBe('New Hotel');
|
||||
const reservations = useTripStore.getState().reservations;
|
||||
expect(reservations).toHaveLength(2);
|
||||
// addReservation prepends
|
||||
expect(reservations[0].name).toBe('New Hotel');
|
||||
expect(reservations[0].title).toBe('New Hotel');
|
||||
});
|
||||
|
||||
it('FE-RESERV-003: addReservation on failure throws', async () => {
|
||||
@@ -66,14 +66,14 @@ describe('reservationsSlice', () => {
|
||||
);
|
||||
|
||||
await expect(
|
||||
useTripStore.getState().addReservation(1, { name: 'Fail' })
|
||||
useTripStore.getState().addReservation(1, { title: 'Fail' })
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateReservation', () => {
|
||||
it('FE-RESERV-004: updateReservation replaces item in array by id', async () => {
|
||||
const reservation = buildReservation({ id: 10, trip_id: 1, name: 'Old', status: 'pending' });
|
||||
const reservation = buildReservation({ id: 10, trip_id: 1, title: 'Old', status: 'pending' });
|
||||
seedStore(useTripStore, { reservations: [reservation] });
|
||||
|
||||
server.use(
|
||||
@@ -83,10 +83,10 @@ describe('reservationsSlice', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await useTripStore.getState().updateReservation(1, 10, { name: 'Updated Hotel' });
|
||||
const result = await useTripStore.getState().updateReservation(1, 10, { title: 'Updated Hotel' });
|
||||
|
||||
expect(result.name).toBe('Updated Hotel');
|
||||
expect(useTripStore.getState().reservations[0].name).toBe('Updated Hotel');
|
||||
expect(result.title).toBe('Updated Hotel');
|
||||
expect(useTripStore.getState().reservations[0].title).toBe('Updated Hotel');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -201,14 +201,14 @@ describe('tripStore', () => {
|
||||
|
||||
describe('updateTrip', () => {
|
||||
it('FE-TRIP-008: updateTrip persists and refreshes trip + days', async () => {
|
||||
const updatedTrip = buildTrip({ id: 1, name: 'Updated Trip' });
|
||||
const updatedTrip = buildTrip({ id: 1, title: 'Updated Trip' });
|
||||
|
||||
server.use(
|
||||
http.put('/api/trips/1', () => HttpResponse.json({ trip: updatedTrip })),
|
||||
http.get('/api/trips/1/days', () => HttpResponse.json({ days: [] })),
|
||||
);
|
||||
|
||||
const result = await useTripStore.getState().updateTrip(1, { name: 'Updated Trip' });
|
||||
const result = await useTripStore.getState().updateTrip(1, { title: 'Updated Trip' });
|
||||
|
||||
expect(result).toEqual(updatedTrip);
|
||||
expect(useTripStore.getState().trip).toEqual(updatedTrip);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { formatDate, formatTime, dayTotalCost, currencyDecimals } from '../../../src/utils/formatters';
|
||||
import type { AssignmentsMap } from '../../../src/types';
|
||||
|
||||
// dayTotalCost intentionally exercises edge-case price inputs (string / non-numeric),
|
||||
// which are looser than the canonical AssignmentsMap shape — hence the casts below.
|
||||
const asMap = (m: unknown): AssignmentsMap => m as AssignmentsMap;
|
||||
|
||||
describe('currencyDecimals', () => {
|
||||
it('returns 0 for zero-decimal currencies', () => {
|
||||
@@ -68,7 +73,7 @@ describe('dayTotalCost', () => {
|
||||
{ id: 1, day_id: 1, order_index: 0, notes: null, place: { id: 1, trip_id: 1, name: 'P', lat: null, lng: null, description: null, address: null, category_id: null, icon: null, price: null, image_url: null, google_place_id: null, osm_id: null, route_geometry: null, place_time: null, end_time: null, created_at: '' } },
|
||||
],
|
||||
};
|
||||
expect(dayTotalCost(1, assignments, 'EUR')).toBeNull();
|
||||
expect(dayTotalCost(1, asMap(assignments), 'EUR')).toBeNull();
|
||||
});
|
||||
|
||||
it('sums prices across assignments', () => {
|
||||
@@ -78,7 +83,7 @@ describe('dayTotalCost', () => {
|
||||
{ id: 2, day_id: 1, order_index: 1, notes: null, place: { id: 2, trip_id: 1, name: 'B', lat: null, lng: null, description: null, address: null, category_id: null, icon: null, price: '30', image_url: null, google_place_id: null, osm_id: null, route_geometry: null, place_time: null, end_time: null, created_at: '' } },
|
||||
],
|
||||
};
|
||||
expect(dayTotalCost(1, assignments, 'EUR')).toBe('50 EUR');
|
||||
expect(dayTotalCost(1, asMap(assignments), 'EUR')).toBe('50 EUR');
|
||||
});
|
||||
|
||||
it('ignores non-numeric price strings', () => {
|
||||
@@ -87,7 +92,7 @@ describe('dayTotalCost', () => {
|
||||
{ id: 1, day_id: 1, order_index: 0, notes: null, place: { id: 1, trip_id: 1, name: 'A', lat: null, lng: null, description: null, address: null, category_id: null, icon: null, price: 'free', image_url: null, google_place_id: null, osm_id: null, route_geometry: null, place_time: null, end_time: null, created_at: '' } },
|
||||
],
|
||||
};
|
||||
expect(dayTotalCost(1, assignments, 'EUR')).toBeNull();
|
||||
expect(dayTotalCost(1, asMap(assignments), 'EUR')).toBeNull();
|
||||
});
|
||||
|
||||
it('uses the dayId key to look up assignments', () => {
|
||||
@@ -96,7 +101,7 @@ describe('dayTotalCost', () => {
|
||||
{ id: 3, day_id: 2, order_index: 0, notes: null, place: { id: 3, trip_id: 1, name: 'C', lat: null, lng: null, description: null, address: null, category_id: null, icon: null, price: '10', image_url: null, google_place_id: null, osm_id: null, route_geometry: null, place_time: null, end_time: null, created_at: '' } },
|
||||
],
|
||||
};
|
||||
expect(dayTotalCost(1, assignments, 'USD')).toBeNull();
|
||||
expect(dayTotalCost(2, assignments, 'USD')).toBe('10 USD');
|
||||
expect(dayTotalCost(1, asMap(assignments), 'USD')).toBeNull();
|
||||
expect(dayTotalCost(2, asMap(assignments), 'USD')).toBe('10 USD');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user