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 { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import { SessionsPage } from './SessionsPage'
|
import { commandOccurrence, SessionsPage } from './SessionsPage'
|
||||||
|
|
||||||
function page(kind: string, items: unknown[]) {
|
function page(kind: string, items: unknown[]) {
|
||||||
return { api_version: 1, kind, offset: 0, more: false, next_offset: items.length, items }
|
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', () => {
|
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 () => {
|
it('keeps proposals distinct and opens a stable deep link', async () => {
|
||||||
window.history.replaceState(null, '', '/seances')
|
window.history.replaceState(null, '', '/seances')
|
||||||
vi.stubGlobal('fetch', vi.fn((input: RequestInfo | URL) => {
|
vi.stubGlobal('fetch', vi.fn((input: RequestInfo | URL) => {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,42 @@ function emptyInput(): PreparationInput {
|
||||||
editing_state: 'draft', occurrences: [] }
|
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 }: {
|
function Editor({ initial, onSaved, onCancel }: {
|
||||||
initial?: SessionDetail
|
initial?: SessionDetail
|
||||||
onSaved: (identity: string) => void
|
onSaved: (identity: string) => void
|
||||||
|
|
@ -38,9 +74,8 @@ function Editor({ initial, onSaved, onCancel }: {
|
||||||
}) {
|
}) {
|
||||||
const [input, setInput] = useState<PreparationInput>(() => initial ? {
|
const [input, setInput] = useState<PreparationInput>(() => initial ? {
|
||||||
title: initial.title, session_type: initial.session_type, planned_for: initial.planned_for,
|
title: initial.title, session_type: initial.session_type, planned_for: initial.planned_for,
|
||||||
notes: initial.notes, editing_state: 'draft', occurrences: initial.occurrences.map((value) => ({
|
notes: initial.notes, editing_state: 'draft', occurrences: initial.occurrences.map((value) =>
|
||||||
...value, entry_id: initial.kind === 'proposal' ? undefined : value.entry_id,
|
editableOccurrence(value, initial.kind === 'preparation')),
|
||||||
})),
|
|
||||||
...(initial.kind === 'proposal' ? {
|
...(initial.kind === 'proposal' ? {
|
||||||
source_proposal_id: initial.identity,
|
source_proposal_id: initial.identity,
|
||||||
source_payload_sha256: initial.source_fingerprint ?? undefined,
|
source_payload_sha256: initial.source_fingerprint ?? undefined,
|
||||||
|
|
@ -78,7 +113,8 @@ function Editor({ initial, onSaved, onCancel }: {
|
||||||
const submit = async (ready: boolean) => {
|
const submit = async (ready: boolean) => {
|
||||||
setSaving(true); setError('')
|
setSaving(true); setError('')
|
||||||
try {
|
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.identity : undefined,
|
||||||
initial?.kind === 'preparation' ? initial.revision_id : undefined)
|
initial?.kind === 'preparation' ? initial.revision_id : undefined)
|
||||||
if (ready) await prepareForAndroid(result.preparation_id, result.revision_id)
|
if (ready) await prepareForAndroid(result.preparation_id, result.revision_id)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue