From 4e561cf01fa380a3c2789cbc398c8c1f37bd665f Mon Sep 17 00:00:00 2001 From: Alex Christoffer Rasmussen Date: Tue, 17 Jun 2025 15:59:23 +0200 Subject: [PATCH] Fix: rearrange playing event (#1640) * creat failing test * find and pass index of eventNow in case it moved * fix cache invalidate edits should also recalculate timer * use coluor as test indicator * fixed empty cue guard Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> --------- Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> --- .../src/api-data/rundown/rundown.service.ts | 7 ++--- apps/server/src/stores/runtimeState.ts | 5 ++-- .../213-arrange-while-playing.spec.ts | 27 +++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 e2e/tests/features/213-arrange-while-playing.spec.ts diff --git a/apps/server/src/api-data/rundown/rundown.service.ts b/apps/server/src/api-data/rundown/rundown.service.ts index f86a47026..af8a65ca9 100644 --- a/apps/server/src/api-data/rundown/rundown.service.ts +++ b/apps/server/src/api-data/rundown/rundown.service.ts @@ -8,6 +8,7 @@ import { isOntimeDelay, isOntimeEvent, OntimeEntry, + OntimeEvent, PatchWithId, Rundown, } from 'ontime-types'; @@ -84,7 +85,7 @@ export async function editEntry(patch: PatchWithId): Promise { } // we dont allow the user to change the cue to empty string - if (isOntimeEvent(patch) && patch?.cue === '') { + if ((patch as Partial)?.cue === '') { throw new Error('Cue value invalid'); } @@ -107,7 +108,7 @@ export async function editEntry(patch: PatchWithId): Promise { updateRuntimeOnChange(rundownMetadata); // notify timer and external services of change - notifyChanges(rundownMetadata, revision, { timer: [entry.id], external: true }); + notifyChanges(rundownMetadata, revision, { timer: didInvalidate ? true : [entry.id], external: true }); }); return entry; @@ -171,7 +172,7 @@ export async function batchEditEntries(ids: EntryId[], patch: Partial id === runtimeState.eventNow?.id); + loadNow(timedEvents, eventNowIndex >= 0 ? eventNowIndex : undefined); + loadNext(timedEvents, eventNowIndex >= 0 ? eventNowIndex : undefined); updateLoaded(runtimeState.eventNow ?? undefined); loadBlock(rundown); } diff --git a/e2e/tests/features/213-arrange-while-playing.spec.ts b/e2e/tests/features/213-arrange-while-playing.spec.ts new file mode 100644 index 000000000..1c85fa791 --- /dev/null +++ b/e2e/tests/features/213-arrange-while-playing.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/test'; + +test('Rearrange while playing', async ({ page }) => { + await page.goto('http://localhost:4001/rundown'); + + // clear rundown + await page.getByRole('button', { name: 'Clear rundown' }).click(); + await page.getByRole('button', { name: 'Delete all' }).click(); + + // create events + await page.getByRole('button', { name: 'Create Event' }).click(); + await page.getByRole('button', { name: 'Event' }).nth(4).click(); + await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click(); + + // start event 2 + await page.getByTestId('entry-2').getByRole('button', { name: 'Start event' }).click(); + await expect(page.getByTestId('entry-2').locator('#event-block')).toHaveCSS('background-color', 'rgb(8, 122, 39)'); + + // move event 2 up + await page.getByTestId('entry-2').locator('#event-block').getByText('2').click(); + await page.getByTestId('entry-2').locator('#event-block div').filter({ hasText: '2' }).press('Alt+Control+ArrowUp'); + + // event CUE1 should new be entry 2 + await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('1'); + // but entry 1 should be the one playing (it will be unlinked as it will be the first event) + await expect(page.getByTestId('entry-1').locator('#event-block')).toHaveCSS('background-color', 'rgb(8, 122, 39)'); +});