mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-15 12:23:51 +00:00
69aeb4aabb
Emptying a field which holds different values across the selection does not submit, otherwise focusing it and tabbing away would wipe every entry. That left no way to clear such a field, so text fields now offer an explicit Clear action, shown only while the entries disagree. Once they agree the field can be emptied by hand and the action is withdrawn. Both of the fixes which multi edit depends on were unguarded, so the logic moves into pure helpers alongside the existing hook helper convention: - shouldSubmitValue covers an unknown initial value being submitted as an empty string - applyPatchToEvents covers the optimistic batch update, whose signature now makes the original mistake of spreading the request body unrepresentable, and pins the custom field merge Both tests were confirmed to fail against the original behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PDETGBrhwgAqgTHjmrmJGJ
88 lines
4.5 KiB
TypeScript
88 lines
4.5 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
test('Editing multiple events', async ({ page }) => {
|
|
await page.goto('/editor');
|
|
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 two events with distinct titles
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(2);
|
|
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').fill('first');
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').press('Enter');
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').fill('second');
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').press('Enter');
|
|
|
|
const editor = page.getByTestId('editor-container');
|
|
|
|
// a single selection shows the full schedule and the event id
|
|
await page.getByTestId('entry-1').getByTestId('rundown-event').click();
|
|
await expect(editor.getByTestId('time-input-timeStart')).toBeVisible();
|
|
await expect(editor.getByLabel('Title', { exact: true })).toHaveValue('first');
|
|
|
|
// selecting both events shows a merged view
|
|
await page
|
|
.getByTestId('entry-2')
|
|
.getByTestId('rundown-event')
|
|
.click({ modifiers: ['Shift'] });
|
|
await expect(editor.locator('#eventId')).toHaveValue('2 events selected');
|
|
await expect(editor.getByText('Automations are not available when editing multiple events')).toBeVisible();
|
|
|
|
// start and end times are unique to an event, only the duration can be batched
|
|
await expect(editor.getByTestId('time-input-timeStart')).toBeHidden();
|
|
await expect(editor.getByTestId('time-input-timeEnd')).toBeHidden();
|
|
await expect(editor.getByTestId('time-input-duration')).toBeVisible();
|
|
|
|
// fields which do not agree are shown as mixed
|
|
const title = editor.getByLabel('Title', { exact: true });
|
|
await expect(title).toHaveValue('');
|
|
await expect(title).toHaveAttribute('placeholder', 'Mixed');
|
|
|
|
// leaving a mixed field without editing it does not overwrite the events
|
|
await title.click();
|
|
await page.keyboard.press('Tab');
|
|
await expect(page.getByTestId('entry-1').getByTestId('entry__title')).toHaveValue('first');
|
|
await expect(page.getByTestId('entry-2').getByTestId('entry__title')).toHaveValue('second');
|
|
|
|
// a mixed field cannot be cleared by emptying it, so an explicit action is offered
|
|
const clearTitle = editor.getByRole('button', { name: 'Clear', exact: true }).first();
|
|
await expect(clearTitle).toBeVisible();
|
|
await clearTitle.click();
|
|
await expect(page.getByTestId('entry-1').getByTestId('entry__title')).toHaveValue('');
|
|
await expect(page.getByTestId('entry-2').getByTestId('entry__title')).toHaveValue('');
|
|
|
|
// the events now agree, so the field can be cleared by hand and the action is withdrawn
|
|
await expect(editor.getByLabel('Title', { exact: true })).not.toHaveAttribute('placeholder', 'Mixed');
|
|
|
|
// editing a field applies it to the whole selection
|
|
await title.fill('shared title');
|
|
await title.press('Enter');
|
|
await expect(page.getByTestId('entry-1').getByTestId('entry__title')).toHaveValue('shared title');
|
|
await expect(page.getByTestId('entry-2').getByTestId('entry__title')).toHaveValue('shared title');
|
|
|
|
// the value is no longer mixed
|
|
await expect(editor.getByLabel('Title', { exact: true })).toHaveValue('shared title');
|
|
|
|
// a batched duration is applied to every event and the rundown is recalculated once
|
|
const duration = editor.getByTestId('time-input-duration');
|
|
await duration.click();
|
|
await duration.fill('5m');
|
|
await duration.press('Enter');
|
|
await expect(page.getByTestId('entry-1').getByTestId('time-input-duration')).toHaveValue('00:05:00');
|
|
await expect(page.getByTestId('entry-2').getByTestId('time-input-duration')).toHaveValue('00:05:00');
|
|
// the second event is linked, so its start follows the new end of the first
|
|
await expect(page.getByTestId('entry-2').getByTestId('time-input-timeStart')).toHaveValue('00:05:00');
|
|
await expect(page.getByTestId('entry-2').getByTestId('time-input-timeEnd')).toHaveValue('00:10:00');
|
|
|
|
// going back to a single selection restores the full editor
|
|
await page.getByTestId('entry-1').getByTestId('rundown-event').click();
|
|
await expect(editor.getByTestId('time-input-timeStart')).toBeVisible();
|
|
await expect(editor.locator('#eventId')).not.toHaveValue('2 events selected');
|
|
});
|