Serialize Web preparation commands strictly
This commit is contained in:
parent
f57682790d
commit
9cdd9c2313
2 changed files with 54 additions and 5 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { SessionsPage } from './SessionsPage'
|
||||
import { commandOccurrence, SessionsPage } from './SessionsPage'
|
||||
|
||||
function page(kind: string, items: unknown[]) {
|
||||
return { api_version: 1, kind, offset: 0, more: false, next_offset: items.length, items }
|
||||
|
|
@ -11,6 +11,19 @@ function response(value: unknown) {
|
|||
}
|
||||
|
||||
describe('Sessions page', () => {
|
||||
it('removes presentation-only fields from a preparation command', () => {
|
||||
const command = commandOccurrence({
|
||||
exercise_id: 'ex_one', exercise_name: 'Visible name', equipment_id: null,
|
||||
recording_mode: 'sets', tracking_mode: 'reps', load_mode: 'external', rest_seconds: 90,
|
||||
target_sets: 3, target_reps: 8, target_duration_seconds: null, target_weight_kg: 42,
|
||||
notes: null,
|
||||
})
|
||||
expect(command).not.toHaveProperty('exercise_name')
|
||||
expect(command).not.toHaveProperty('recording_mode')
|
||||
expect(command).not.toHaveProperty('tracking_mode')
|
||||
expect(command).toMatchObject({ exercise_id: 'ex_one', target_sets: 3, rest_seconds: 90 })
|
||||
})
|
||||
|
||||
it('keeps proposals distinct and opens a stable deep link', async () => {
|
||||
window.history.replaceState(null, '', '/seances')
|
||||
vi.stubGlobal('fetch', vi.fn((input: RequestInfo | URL) => {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,42 @@ function emptyInput(): PreparationInput {
|
|||
editing_state: 'draft', occurrences: [] }
|
||||
}
|
||||
|
||||
function editableOccurrence(
|
||||
value: PreparationOccurrence,
|
||||
preserveIdentity: boolean,
|
||||
): PreparationOccurrence {
|
||||
return {
|
||||
...(preserveIdentity ? { entry_id: value.entry_id } : {}),
|
||||
exercise_id: value.exercise_id,
|
||||
exercise_name: value.exercise_name,
|
||||
equipment_id: value.equipment_id,
|
||||
recording_mode: value.recording_mode,
|
||||
tracking_mode: value.tracking_mode,
|
||||
load_mode: value.load_mode,
|
||||
rest_seconds: value.rest_seconds,
|
||||
target_sets: value.target_sets,
|
||||
target_reps: value.target_reps,
|
||||
target_duration_seconds: value.target_duration_seconds,
|
||||
target_weight_kg: value.target_weight_kg,
|
||||
notes: value.notes,
|
||||
}
|
||||
}
|
||||
|
||||
export function commandOccurrence(value: PreparationOccurrence): PreparationOccurrence {
|
||||
return {
|
||||
...(value.entry_id ? { entry_id: value.entry_id } : {}),
|
||||
exercise_id: value.exercise_id,
|
||||
equipment_id: value.equipment_id,
|
||||
load_mode: value.load_mode,
|
||||
rest_seconds: value.rest_seconds,
|
||||
target_sets: value.target_sets,
|
||||
target_reps: value.target_reps,
|
||||
target_duration_seconds: value.target_duration_seconds,
|
||||
target_weight_kg: value.target_weight_kg,
|
||||
notes: value.notes,
|
||||
}
|
||||
}
|
||||
|
||||
function Editor({ initial, onSaved, onCancel }: {
|
||||
initial?: SessionDetail
|
||||
onSaved: (identity: string) => void
|
||||
|
|
@ -38,9 +74,8 @@ function Editor({ initial, onSaved, onCancel }: {
|
|||
}) {
|
||||
const [input, setInput] = useState<PreparationInput>(() => initial ? {
|
||||
title: initial.title, session_type: initial.session_type, planned_for: initial.planned_for,
|
||||
notes: initial.notes, editing_state: 'draft', occurrences: initial.occurrences.map((value) => ({
|
||||
...value, entry_id: initial.kind === 'proposal' ? undefined : value.entry_id,
|
||||
})),
|
||||
notes: initial.notes, editing_state: 'draft', occurrences: initial.occurrences.map((value) =>
|
||||
editableOccurrence(value, initial.kind === 'preparation')),
|
||||
...(initial.kind === 'proposal' ? {
|
||||
source_proposal_id: initial.identity,
|
||||
source_payload_sha256: initial.source_fingerprint ?? undefined,
|
||||
|
|
@ -78,7 +113,8 @@ function Editor({ initial, onSaved, onCancel }: {
|
|||
const submit = async (ready: boolean) => {
|
||||
setSaving(true); setError('')
|
||||
try {
|
||||
const result = await savePreparation({ ...input, editing_state: ready ? 'ready' : 'draft' },
|
||||
const result = await savePreparation({ ...input, editing_state: ready ? 'ready' : 'draft',
|
||||
occurrences: input.occurrences.map(commandOccurrence) },
|
||||
initial?.kind === 'preparation' ? initial.identity : undefined,
|
||||
initial?.kind === 'preparation' ? initial.revision_id : undefined)
|
||||
if (ready) await prepareForAndroid(result.preparation_id, result.revision_id)
|
||||
|
|
|
|||
Loading…
Reference in a new issue