mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 07:40:59 +00:00
122 lines
5.9 KiB
TypeScript
122 lines
5.9 KiB
TypeScript
// FE-COMP-BAGWEIGHT-001 to FE-COMP-BAGWEIGHT-006
|
|
|
|
/**
|
|
* What the bag panels actually print (#2191).
|
|
*
|
|
* The numbers used to be summed in the browser over the privacy-filtered item
|
|
* list, so a bag shared with someone whose personal items you cannot see read
|
|
* lighter than it was — and that figure is what gets measured against an
|
|
* airline's weight limit. The server now sends the real total; these tests pin
|
|
* that the panels show it, that the "no bag" pile and the grand total follow
|
|
* the same rule, and that offline they fall back to the honest partial sum
|
|
* rather than a frozen absolute one.
|
|
*/
|
|
import { describe, it, expect, vi } from 'vitest'
|
|
import { render, screen, within } from '../../../tests/helpers/render'
|
|
import { BagSidebar } from './PackingListPanelBagSidebar'
|
|
import { BagModal } from './PackingListPanelBagModal'
|
|
import type { PackingState } from './usePackingListPanel'
|
|
|
|
/** My own 300 g item; the buddy's 700 g personal item never reaches this client. */
|
|
const ITEMS = [
|
|
{ id: 1, name: 'Stove', weight_grams: 300, quantity: 1, bag_id: 10, is_private: 0, owner_id: 1, checked: 0, category: 'Kitchen' },
|
|
{ id: 2, name: 'Sandwich', weight_grams: 150, quantity: 1, bag_id: null, is_private: 0, owner_id: 1, checked: 0, category: 'Food' },
|
|
]
|
|
|
|
function buildState(over: Partial<PackingState> = {}): PackingState {
|
|
return {
|
|
t: (k: string) => k,
|
|
bags: [{ id: 10, trip_id: 1, name: 'Duffel', color: '#6366f1', sort_order: 0, weight_limit_grams: null, members: [], total_weight_grams: 1000 }],
|
|
items: ITEMS,
|
|
tripId: 1,
|
|
tripMembers: [],
|
|
canEdit: false,
|
|
currentUserId: 1,
|
|
unassignedWeightGrams: 500,
|
|
serverWeightsFresh: true,
|
|
handleDeleteBag: vi.fn(),
|
|
handleUpdateBag: vi.fn(),
|
|
handleSetBagMembers: vi.fn(),
|
|
handleCreateBag: vi.fn(),
|
|
setShowBagModal: vi.fn(),
|
|
showAddBag: false,
|
|
setShowAddBag: vi.fn(),
|
|
newBagName: '',
|
|
setNewBagName: vi.fn(),
|
|
...over,
|
|
} as unknown as PackingState
|
|
}
|
|
|
|
describe('bag weights on the packing panels (#2191)', () => {
|
|
it('FE-COMP-BAGWEIGHT-001: the sidebar shows the server total, not the sum of what you can see', () => {
|
|
// Locally visible: 300 g. Actually in the bag: 1000 g.
|
|
render(<BagSidebar {...buildState()} />)
|
|
expect(screen.getByText('1.0 kg')).toBeInTheDocument()
|
|
expect(screen.queryByText('300 g')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('FE-COMP-BAGWEIGHT-002: the grand total adds the bags and the unassigned pile', () => {
|
|
// 1000 g in the bag + 500 g loose.
|
|
const { container } = render(<BagSidebar {...buildState()} />)
|
|
const total = within(container).getByText('packing.totalWeight').parentElement as HTMLElement
|
|
expect(total).toHaveTextContent('1.5 kg')
|
|
})
|
|
|
|
it('FE-COMP-BAGWEIGHT-003: the modal follows the same rule as the sidebar', () => {
|
|
render(<BagModal {...buildState()} />)
|
|
expect(screen.getByText('1.0 kg')).toBeInTheDocument()
|
|
})
|
|
|
|
it('FE-COMP-BAGWEIGHT-004: offline it falls back to summing what is visible', () => {
|
|
// The server totals are frozen at the last online read and blind to the
|
|
// mutation queue, so an honest partial number beats a stale absolute one.
|
|
render(<BagSidebar {...buildState({ serverWeightsFresh: false })} />)
|
|
expect(screen.getByText('300 g')).toBeInTheDocument()
|
|
expect(screen.queryByText('1.0 kg')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('FE-COMP-BAGWEIGHT-005: a bag from before the field falls back to the local sum', () => {
|
|
const bags = [{ id: 10, trip_id: 1, name: 'Duffel', color: '#6366f1', sort_order: 0, weight_limit_grams: null, members: [] }]
|
|
render(<BagSidebar {...buildState({ bags: bags as never, unassignedWeightGrams: null })} />)
|
|
expect(screen.getByText('300 g')).toBeInTheDocument()
|
|
})
|
|
|
|
it('FE-COMP-BAGWEIGHT-006: the unassigned row appears for weight with no visible items', () => {
|
|
// Someone else's personal item is loose in the trip: the grand total counts
|
|
// it, so a row has to account for it or the total adds up to nothing.
|
|
render(<BagSidebar {...buildState({ items: [ITEMS[0]] as never, unassignedWeightGrams: 400 })} />)
|
|
expect(screen.getByText('packing.noBag')).toBeInTheDocument()
|
|
expect(screen.getByText('400 g')).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('packed weight and per-person loads (#1131)', () => {
|
|
it('FE-COMP-BAGWEIGHT-1131-1: the foot shows what of the total is already packed', () => {
|
|
const items = [{ ...ITEMS[0], checked: 1 }, { ...ITEMS[1], quantity: 2, packed_quantity: 1 }]
|
|
const { container } = render(<BagSidebar {...buildState({ items: items as never })} />)
|
|
// 300 g ticked + one of two 150 g sandwiches.
|
|
const packed = within(container).getByText('packing.packedWeight').parentElement as HTMLElement
|
|
expect(packed).toHaveTextContent('450 g')
|
|
expect(screen.getByRole('progressbar')).toHaveAttribute('aria-valuenow', '30')
|
|
})
|
|
|
|
it('FE-COMP-BAGWEIGHT-1131-2: each bag member carries an even share of the bag', () => {
|
|
const bags = [
|
|
{ id: 10, trip_id: 1, name: 'Duffel', color: '#6366f1', sort_order: 0, members: [{ user_id: 1, username: 'Ana' }, { user_id: 2, username: 'Ben' }], total_weight_grams: 1000 },
|
|
{ id: 11, trip_id: 1, name: 'Daypack', color: '#10b981', sort_order: 1, members: [{ user_id: 2, username: 'Ben' }], total_weight_grams: 800 },
|
|
]
|
|
render(<BagModal {...buildState({ bags: bags as never })} />)
|
|
expect(screen.getByText('packing.perPerson')).toBeInTheDocument()
|
|
// Ben: half the duffel plus the daypack. Ana: the other half of the duffel.
|
|
const row = (name: string) => screen.getAllByText(name).map(el => el.parentElement?.textContent ?? '').find(text => text.endsWith(' g') || text.endsWith(' kg'))
|
|
expect(row('Ben')).toBe('BBen1.3 kg')
|
|
expect(row('Ana')).toBe('AAna500 g')
|
|
})
|
|
|
|
it('FE-COMP-BAGWEIGHT-1131-3: no per-person block while no bag has anyone on it', () => {
|
|
render(<BagSidebar {...buildState()} />)
|
|
expect(screen.queryByText('packing.perPerson')).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
|