Spaces:
Sleeping
Sleeping
assafvayner HF Staff
fix(photos,forms): own-property content types, dimension bounds, VP8L signature, trimmed inputs
fa363c5 Download tests/unit/forms/expense-form.test.ts from assafvayner/splitwise: direct link, hf CLI and curl.
- Browser
- Download file 4.98 kB
-
https://huggingface.co/spaces/assafvayner/splitwise/resolve/main/tests/unit/forms/expense-form.test.ts
- Command line
-
hf download hf://spaces/assafvayner/splitwise/tests/unit/forms/expense-form.test.ts
-
curl -L -o expense-form.test.ts https://huggingface.co/spaces/assafvayner/splitwise/resolve/main/tests/unit/forms/expense-form.test.ts
4.98 kB
| import { describe, expect, it } from 'vitest'; | |
| import { buildExpenseBody, emptyExpenseForm, expenseToFormValues, readExpenseForm } from '../../../src/lib/server/forms/expense-form'; | |
| import type { Expense } from '../../../src/lib/domain/types'; | |
| const members = ['u_a', 'u_b', 'u_c']; | |
| const fd = (entries: [string, string][]) => { | |
| const f = new FormData(); | |
| for (const [k, v] of entries) f.append(k, v); | |
| return f; | |
| }; | |
| describe('readExpenseForm', () => { | |
| it('reads all fields, keeping only known participants', () => { | |
| const values = readExpenseForm( | |
| fd([['description', ' Pizza '], ['amount', '30'], ['date', '2026-08-01'], ['paidBy', 'u_a'], ['mode', 'percent'], | |
| ['participants', 'u_a'], ['participants', 'u_b'], ['participants', 'u_zzz'], ['pct_u_a', '25'], ['pct_u_b', '75'], ['exact_u_a', '1'], ['notes', 'n'], ['removePhoto', 'on']]), | |
| members | |
| ); | |
| expect(values).toEqual({ | |
| description: 'Pizza', amount: '30', date: '2026-08-01', paidBy: 'u_a', mode: 'percent', participants: ['u_a', 'u_b'], | |
| pct: { u_a: '25', u_b: '75' }, exact: { u_a: '1' }, notes: 'n', removePhoto: true | |
| }); | |
| }); | |
| it('defaults unknown mode to equal', () => { | |
| expect(readExpenseForm(fd([['mode', 'weird']]), members).mode).toBe('equal'); | |
| }); | |
| }); | |
| describe('buildExpenseBody', () => { | |
| const base = { ...emptyExpenseForm('u_a', members, '2026-08-01'), description: 'Pizza', amount: '30' }; | |
| const opts = { activeMemberIds: members, currency: 'USD' }; | |
| it('builds an equal split', () => { | |
| const r = buildExpenseBody({ ...base, participants: ['u_a', 'u_b'] }, opts); | |
| expect(r).toEqual({ | |
| ok: true, | |
| body: { description: 'Pizza', amountCents: 3000, currency: 'USD', date: '2026-08-01', paidBy: 'u_a', split: { mode: 'equal', participants: ['u_a', 'u_b'] }, shares: { u_a: 1500, u_b: 1500 } } | |
| }); | |
| }); | |
| it('builds percent and exact splits from the participant inputs', () => { | |
| const pct = buildExpenseBody({ ...base, mode: 'percent', participants: ['u_a', 'u_b'], pct: { u_a: '25', u_b: '75', u_c: '100' } }, opts); | |
| expect(pct.ok && pct.body.split).toEqual({ mode: 'percent', bp: { u_a: 2500, u_b: 7500 } }); | |
| expect(pct.ok && pct.body.shares).toEqual({ u_a: 750, u_b: 2250 }); | |
| const exact = buildExpenseBody({ ...base, mode: 'exact', participants: ['u_a', 'u_b'], exact: { u_a: '10', u_b: '20' } }, opts); | |
| expect(exact.ok && exact.body.shares).toEqual({ u_a: 1000, u_b: 2000 }); | |
| }); | |
| it('includes notes only when non-empty', () => { | |
| const r = buildExpenseBody({ ...base, notes: ' ' }, opts); | |
| expect(r.ok && 'notes' in r.body).toBe(false); | |
| const r2 = buildExpenseBody({ ...base, notes: 'hi' }, opts); | |
| expect(r2.ok && r2.body.notes).toBe('hi'); | |
| }); | |
| it('trims the description before storing it', () => { | |
| const r = buildExpenseBody({ ...base, description: ' Pizza ' }, opts); | |
| expect(r.ok && r.body.description).toBe('Pizza'); | |
| }); | |
| it('reports field errors', () => { | |
| const errs = (v: Partial<typeof base>) => { | |
| const r = buildExpenseBody({ ...base, ...v }, opts); | |
| return r.ok ? {} : r.errors; | |
| }; | |
| expect(errs({ description: '' })).toHaveProperty('description'); | |
| expect(errs({ amount: 'abc' })).toHaveProperty('amount'); | |
| expect(errs({ amount: '0' })).toHaveProperty('amount'); | |
| expect(errs({ date: '2026-02-30' })).toHaveProperty('date'); | |
| expect(errs({ paidBy: 'u_admin' })).toHaveProperty('paidBy'); | |
| expect(errs({ participants: [] })).toHaveProperty('participants'); | |
| expect(errs({ participants: ['u_a', 'u_ghost'] })).toHaveProperty('participants'); | |
| expect(errs({ mode: 'percent', pct: { u_a: '50' } })).toHaveProperty('shares'); | |
| expect(errs({ mode: 'percent', pct: { u_a: '50', u_b: '30', u_c: '30' } })).toHaveProperty('shares'); | |
| expect(errs({ mode: 'exact', exact: { u_a: '10', u_b: '10', u_c: '5' } })).toHaveProperty('shares'); | |
| expect(errs({ mode: 'exact', exact: { u_a: 'x', u_b: '10', u_c: '10' } })).toHaveProperty('shares'); | |
| }); | |
| it('rejects a payer who is not an active member (photo/notes untouched)', () => { | |
| const r = buildExpenseBody({ ...base, paidBy: 'u_c' }, { ...opts, activeMemberIds: ['u_a', 'u_b'] }); | |
| expect(r.ok).toBe(false); | |
| }); | |
| }); | |
| describe('expenseToFormValues', () => { | |
| it('maps every split mode back to inputs', () => { | |
| const audit = { createdBy: 'u_a', createdAt: 'x', updatedBy: 'u_a', updatedAt: 'x' }; | |
| const e: Expense = { id: 'e_1', version: 1, description: 'D', amountCents: 1234, currency: 'USD', date: '2026-08-01', paidBy: 'u_b', split: { mode: 'percent', bp: { u_a: 3333, u_b: 6667 } }, shares: { u_a: 411, u_b: 823 }, notes: 'n', ...audit }; | |
| expect(expenseToFormValues(e)).toEqual({ description: 'D', amount: '12.34', date: '2026-08-01', paidBy: 'u_b', mode: 'percent', participants: ['u_a', 'u_b'], pct: { u_a: '33.33', u_b: '66.67' }, exact: {}, notes: 'n', removePhoto: false }); | |
| const ex: Expense = { ...e, split: { mode: 'exact', cents: { u_a: 34, u_b: 1200 } } }; | |
| expect(expenseToFormValues(ex).exact).toEqual({ u_a: '0.34', u_b: '12.00' }); | |
| }); | |
| }); | |