mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 14:39:06 +00:00
bb221685ea
The three-variant result union differed only in whether an entry had a cue, an event index and a parent, and the UI narrowed it with runtime property checks anyway, so the discrimination bought nothing while costing three near-identical branches to build a result. One flat type removes those branches and the checks around them. Skipping non-searchable entries asked whether an entry was a group or a milestone, then asked again when building the result. It now asks once whether it is a delay, which is what the guard actually meant. The search functions move out of the memo closure to the module, where they are ordinary pure functions. Whether to show the matched field compared against its display label, so a custom field labelled "Title" would have had its match hidden. It now compares the field key. Drops the review document, which had served its purpose, and reduces the finder tests to a single one covering the path a user takes: open from a focused field, find an entry by its note, scope with a badge, and reveal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DzALEq9gGWwFmwTdgAiFcY
288 lines
14 KiB
TypeScript
288 lines
14 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
test('Copy-paste', async ({ page }) => {
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
|
|
// create event
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await page.getByTestId('entry-1').click();
|
|
await page.getByLabel('Cue', { exact: true }).click();
|
|
await page.getByLabel('Cue', { exact: true }).fill('4');
|
|
await page.getByLabel('Cue', { exact: true }).press('Enter');
|
|
await page.getByTestId('entry-1').click();
|
|
await page.getByTestId('entry__title').click();
|
|
await page.getByTestId('entry__title').fill('test');
|
|
await page.getByTestId('entry__title').press('Enter');
|
|
|
|
// copy paste below
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '4' }).click();
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '4' }).press('ControlOrMeta+c');
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '4' }).press('ControlOrMeta+v');
|
|
|
|
// assert
|
|
await expect(page.getByTestId('entry-2')).toBeVisible();
|
|
await expect(page.getByTestId('entry-2').getByTestId('entry__title')).toHaveValue('test');
|
|
await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('4');
|
|
});
|
|
|
|
test('Cut-paste', async ({ page }) => {
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
|
|
// create events
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').click();
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').fill('first');
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').press('Enter');
|
|
|
|
await page.getByRole('button', { name: 'Event' }).nth(4).click();
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').click();
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').fill('second');
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').press('Enter');
|
|
|
|
// cut first event, paste below second
|
|
await page.getByTestId('entry-1').getByTestId('rundown-event').getByText('1').click();
|
|
await page.getByTestId('entry-1').getByTestId('rundown-event').filter({ hasText: '1' }).press('ControlOrMeta+x');
|
|
await page.getByTestId('entry-2').getByTestId('rundown-event').getByText('2').click();
|
|
await page.getByTestId('entry-2').getByTestId('rundown-event').filter({ hasText: '2' }).press('ControlOrMeta+v');
|
|
|
|
// we can verify that the entries have swapped places
|
|
const events = await page.getByTestId('entry__title').all();
|
|
await expect(events[0]).toHaveValue('second');
|
|
await expect(events[1]).toHaveValue('first');
|
|
});
|
|
|
|
test('Move', async ({ page }) => {
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(0);
|
|
|
|
// create events
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await page.getByRole('button', { name: 'Event' }).nth(4).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(2);
|
|
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(3);
|
|
|
|
// copy move down
|
|
await page.getByTestId('entry-1').getByTestId('rundown-event').getByText('1').click();
|
|
await page
|
|
.getByTestId('entry-1')
|
|
.getByTestId('rundown-event')
|
|
.filter({ hasText: '1' })
|
|
.press('Alt+Control+ArrowDown');
|
|
await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('1');
|
|
|
|
// move entry three up twice, waiting for each reorder before targeting its new row
|
|
await page.getByTestId('entry-3').getByTestId('rundown-event').getByText('3').click();
|
|
await page
|
|
.getByTestId('entry-3')
|
|
.getByTestId('rundown-event')
|
|
.filter({ hasText: '3' })
|
|
.press('Alt+ControlOrMeta+ArrowUp');
|
|
await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('3');
|
|
await page
|
|
.getByTestId('entry-2')
|
|
.getByTestId('rundown-event')
|
|
.filter({ hasText: '3' })
|
|
.press('Alt+ControlOrMeta+ArrowUp');
|
|
await expect(page.getByTestId('entry-1').getByTestId('rundown-event')).toContainText('3');
|
|
});
|
|
|
|
test('Add group', async ({ page }) => {
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(0);
|
|
await expect(page.getByTestId('rundown-group')).toHaveCount(0);
|
|
|
|
// create events
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await expect(page.getByTestId('rundown-group')).toHaveCount(0);
|
|
await page.getByPlaceholder(/event title/i).fill('test');
|
|
await page.getByTestId('entry-1').click();
|
|
|
|
// add group below
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+G');
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await expect(page.getByTestId('rundown-group')).toHaveCount(1);
|
|
await page.getByTestId('rundown-group').getByTestId('entry__title').fill('group below');
|
|
|
|
// add group above
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+Shift+G');
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await expect(page.getByTestId('rundown-group')).toHaveCount(2);
|
|
await page.getByTestId('entry__title').first().fill('group above');
|
|
|
|
await expect(page.getByTestId(/entry__title/i).first()).toHaveValue('group above');
|
|
await expect(page.getByTestId(/entry__title/i).nth(2)).toHaveValue('group below');
|
|
await expect(page.getByTestId('entry-1').getByTestId(/entry__title/)).toHaveValue('test');
|
|
});
|
|
|
|
test('Add delay', async ({ page }) => {
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(0);
|
|
await expect(page.getByTestId('rundown-delay')).toHaveCount(0);
|
|
|
|
// create events
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await expect(page.getByTestId('rundown-delay')).toHaveCount(0);
|
|
await page.getByTestId('entry-1').click();
|
|
await page.getByTestId('entry__title').press('Escape');
|
|
|
|
// add delay below
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+D');
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await expect(page.getByTestId('rundown-delay')).toHaveCount(1);
|
|
await expect(page.getByTestId('delay-input')).toBeVisible();
|
|
|
|
// add delay above
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+Shift+D');
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await expect(page.getByTestId('rundown-delay')).toHaveCount(2);
|
|
await expect(page.getByTestId('entry-0').getByTestId('delay-input')).toBeVisible();
|
|
});
|
|
|
|
test('Add event', async ({ page }) => {
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(0);
|
|
|
|
// create events
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await page.getByTestId('entry-1').click();
|
|
await page.getByTestId('entry__title').press('Escape');
|
|
|
|
// add event below
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+E');
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(2);
|
|
await expect(page.getByTestId('entry-2').getByTestId('rundown-event').getByText('2')).toBeVisible();
|
|
|
|
// add event above
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+Shift+E');
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(3);
|
|
await expect(page.getByTestId('entry-1').getByTestId('rundown-event')).toContainText('1');
|
|
});
|
|
|
|
test('Delete event', async ({ page }) => {
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(0);
|
|
|
|
// create event
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
|
|
// delete event
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).click();
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+Backspace');
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(0);
|
|
await expect(page.getByRole('button', { name: 'Create Event' })).toBeVisible();
|
|
await expect(page.getByRole('button', { name: 'Create Group' })).toBeVisible();
|
|
});
|
|
|
|
test('Finder searches the rundown and reveals a result', async ({ page }) => {
|
|
await page.goto('/rundown');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(0);
|
|
|
|
// two events, where the one we are looking for is identified only by its note
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(1);
|
|
await page.getByTestId('entry-1').click();
|
|
await page.getByTestId('entry__title').press('Escape');
|
|
await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+E');
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(2);
|
|
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').fill('opening');
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').press('Enter');
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').fill('closing');
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').press('Enter');
|
|
|
|
await page.getByTestId('entry-2').click();
|
|
await page.getByLabel('Note', { exact: true }).fill('remember the zebrafish');
|
|
await page.getByLabel('Note', { exact: true }).press('Tab');
|
|
|
|
// the shortcut has to work from a focused field, which is where it is usually reached for
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').click();
|
|
await page.keyboard.press('ControlOrMeta+f');
|
|
await expect(page.getByPlaceholder('Search...')).toBeFocused();
|
|
|
|
// a bare query reaches the note, and the result names the field it matched
|
|
await page.getByPlaceholder('Search...').fill('zebrafish');
|
|
await expect(page.getByTestId('finder-result')).toHaveCount(1);
|
|
await expect(page.getByTestId('finder-result-match')).toContainText('Note');
|
|
|
|
// a badge scopes the search to one field, without putting syntax in the input
|
|
const titleFilter = page.getByTestId('finder-filters').getByRole('button', { name: 'Title', exact: true });
|
|
await titleFilter.click();
|
|
await expect(page.getByPlaceholder('Search...')).toHaveValue('zebrafish');
|
|
await expect(page.getByTestId('finder-result')).toHaveCount(0);
|
|
|
|
// pressing it again searches every field once more
|
|
await titleFilter.click();
|
|
await expect(page.getByTestId('finder-result')).toHaveCount(1);
|
|
|
|
// choosing a result closes the finder and selects the entry in the rundown
|
|
await page.getByPlaceholder('Search...').press('Enter');
|
|
await expect(page.getByPlaceholder('Search...')).toBeHidden();
|
|
await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toHaveAttribute('data-selected', 'true');
|
|
});
|
|
|
|
test('Open settings', async ({ page }) => {
|
|
await page.goto('/editor');
|
|
await expect(page.getByTestId('editor-container')).toBeVisible();
|
|
|
|
await page.keyboard.press('ControlOrMeta+,');
|
|
await expect(page.getByRole('button', { name: 'Close settings' })).toBeVisible();
|
|
|
|
await page.keyboard.press('Escape');
|
|
await expect(page.getByRole('button', { name: 'Close settings' })).toBeHidden();
|
|
});
|