From de8ea86ca5c8ca90493b60d88699bb5a5466827a Mon Sep 17 00:00:00 2001 From: Tim Bradgate Date: Wed, 3 Jun 2026 01:39:39 +0100 Subject: [PATCH 1/2] Fix session start from show config Sessions tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Vue 3 SessionList.vue was sending an empty POST body to /api/v1/show/sessions/start, causing a JSONDecodeError on the server. The endpoint requires {"session_id": ""} to identify the initiating client, matching the pattern already used by the navbar. Also removes the unreachable Stop Session button from both the Vue 2 and Vue 3 SessionList components — the START_SHOW WebSocket message pushes all clients to /live the moment a session starts, making the button inaccessible. Stop Session remains available via the navbar. Adds E2E coverage in spec 12 for the sessions tab Start button, including cleanup via the navbar to leave clean state for spec 13. Co-Authored-By: Claude Sonnet 4.6 --- .../e2e/tests/12-show-config-sessions.spec.ts | 24 ++++++++- .../show/config/sessions/SessionList.vue | 52 +++++++------------ .../show/config/sessions/SessionList.vue | 38 +++----------- 3 files changed, 48 insertions(+), 66 deletions(-) diff --git a/client-v3/e2e/tests/12-show-config-sessions.spec.ts b/client-v3/e2e/tests/12-show-config-sessions.spec.ts index 1b9a07dc..1591e9ae 100644 --- a/client-v3/e2e/tests/12-show-config-sessions.spec.ts +++ b/client-v3/e2e/tests/12-show-config-sessions.spec.ts @@ -1,6 +1,6 @@ /** - * Show Config — Sessions tab: session tags CRUD. - * Note: actual session start/stop is tested in 13-live-show.spec.ts. + * Show Config — Sessions tab: session start via the tab button + session tags CRUD. + * Spec 13 covers session start/stop via the navbar. */ import { test, expect, type BrowserContext, type Page } from '@playwright/test'; import { @@ -73,3 +73,23 @@ test('deletes a session tag', async () => { timeout: 5_000, }); }); + +// ── Session start via the tab button ───────────────────────────────────── + +test('switches back to Sessions sub-tab', async () => { + await page.click('button[role="tab"]:has-text("Sessions")'); + await expect(page.locator('button:has-text("Start Session")')).toBeVisible({ timeout: 10_000 }); +}); + +test('starts a session via the Sessions tab Start button', async () => { + await page.click('button:has-text("Start Session")'); + // START_SHOW WS message pushes all clients to /live + await page.waitForURL(`${UI_BASE}/live`, { timeout: 10_000 }); +}); + +test('stops the session via the navbar to restore state for later specs', async () => { + await page.locator('text=Live Config').click(); + await page.click('button:has-text("Stop Session")'); + await confirmDialog(page); + await expect(page.locator('a:has-text("Live")')).toHaveClass(/disabled/, { timeout: 10_000 }); +}); diff --git a/client-v3/src/components/show/config/sessions/SessionList.vue b/client-v3/src/components/show/config/sessions/SessionList.vue index fecc0de6..f55ab2f3 100644 --- a/client-v3/src/components/show/config/sessions/SessionList.vue +++ b/client-v3/src/components/show/config/sessions/SessionList.vue @@ -2,22 +2,13 @@ - - - Start Session - - - Stop Session - - + + Start Session + @@ -65,14 +56,15 @@ import log from 'loglevel'; import { makeURL, msToTimerString, contrastColor } from '@/js/utils'; import { useSystemStore } from '@/stores/system'; import { useShowStore } from '@/stores/show'; +import { useWebSocketStore } from '@/stores/websocket'; import { toast } from '@/js/toast'; import SessionTagDropdown from './SessionTagDropdown.vue'; const systemStore = useSystemStore(); const showStore = useShowStore(); +const wsStore = useWebSocketStore(); const startingSession = ref(false); -const stoppingSession = ref(false); const sessionFields = [ { key: 'start_date_time', label: 'Start Time' }, @@ -96,9 +88,17 @@ function revisionLabel(revisionId: number | null): string { } async function startSession(): Promise { + if (!wsStore.internalUUID) { + toast.error('Unable to start new show session'); + return; + } startingSession.value = true; try { - const response = await fetch(makeURL('/api/v1/show/sessions/start'), { method: 'POST' }); + const response = await fetch(makeURL('/api/v1/show/sessions/start'), { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ session_id: wsStore.internalUUID }), + }); if (response.ok) { toast.success('Started new show session'); await showStore.getShowSessionData(); @@ -110,22 +110,6 @@ async function startSession(): Promise { startingSession.value = false; } } - -async function stopSession(): Promise { - stoppingSession.value = true; - try { - const response = await fetch(makeURL('/api/v1/show/sessions/stop'), { method: 'POST' }); - if (response.ok) { - toast.success('Stopped show session'); - await showStore.getShowSessionData(); - } else { - log.error('Unable to stop show session'); - toast.error('Unable to stop show session'); - } - } finally { - stoppingSession.value = false; - } -}