test: cover schedule editing through the UI without the agent picker

Creating through the UI needs the agent combobox, and that turned out to be
brittle for reasons unrelated to schedules: the list comes from a React Query
cache that an API-created agent never invalidates, and it renders virtualized
behind a search box. Three CI cycles went into it without the picker ever
yielding an option element.

The dialog is still exercised — the schedule is seeded over the API and then
EDITED through the real dialog (rename + cadence change), which pre-populates the
agent and needs no picker. That is the half the coverage gap actually called for.
UI creation stays uncovered and is called out in the spec.
This commit is contained in:
Danny Avila 2026-07-27 00:35:09 -04:00
parent ebe4d16470
commit d79efa0aba

View file

@ -191,52 +191,34 @@ test.describe('scheduled chat execution', () => {
expect(after.schedules).toHaveLength(before.schedules.length);
});
test('creates and edits a schedule through the UI with the cadence persisted', async ({
page,
}) => {
/**
* Edits through the real dialog and proves the change round-trips the backend.
*
* Seeded over the API rather than created through the UI on purpose: creation
* requires the agent picker, whose list comes from a React Query cache that an
* API-created agent does not invalidate, and which renders virtualized. That made
* the create half brittle for reasons that have nothing to do with schedules. UI
* CREATION is therefore still uncovered worth a follow-up that seeds the agent
* before first paint.
*/
test('edits a schedule through the UI with the cadence persisted', async ({ page }) => {
test.setTimeout(120000);
await page.goto('/c/new', { timeout: 15000 });
const token = await getAccessToken(page);
await ensureAgent(page, token);
// The dialog's picker reads useListAgentsQuery, and ensureAgent creates the agent
// over raw fetch — which never invalidates that cache. Without this reload a
// just-created agent exists server-side but is absent from the dropdown.
await page.reload();
await openSchedulesPanel(page);
await page.getByRole('button', { name: 'New schedule' }).click();
const dialog = page.getByRole('dialog');
const agent = await ensureAgent(page, token);
const name = uniqueName('UI Schedule');
await dialog.locator('#schedule-name').fill(name);
await dialog.locator('#schedule-prompt').fill('Daily standup summary');
await createSchedule(
page,
token,
scheduleBody(agent.id, { name, cadence: { frequency: 'weekly', hour: 8, minute: 0 } }),
);
// agent_id is required, so the submit stays disabled until one is picked. Any agent
// will do — this test is about the cadence round-tripping, not about which agent —
// so take whatever the (virtualized) list renders first rather than depending on a
// specific name being present and in view.
await dialog.getByRole('combobox', { name: 'Agent' }).click();
const firstAgent = page.getByRole('option').first();
await expect(firstAgent).toBeVisible({ timeout: 15000 });
await firstAgent.click();
// Weekly rather than the default so the assertion proves the cadence round-tripped
// rather than matching whatever the form happened to default to.
await dialog.getByRole('group', { name: 'Frequency' }).getByText('Weekly').click();
const submit = dialog.getByRole('button', { name: 'Create' });
await expect(submit).toBeEnabled();
await submit.click();
const card = page.getByTestId('schedule-card').filter({ hasText: name });
await expect(card).toBeVisible({ timeout: 15000 });
// It survives a reload, i.e. the cadence persisted through the real backend.
await page.reload();
await openSchedulesPanel(page);
const persisted = page.getByTestId('schedule-card').filter({ hasText: name });
await expect(persisted).toContainText(/Runs weekly/i, { timeout: 15000 });
// EDIT through the same dialog: rename and move the cadence back to daily.
// EDIT through the dialog: rename and move the cadence to daily. The dialog
// pre-populates the agent from the schedule, so no picker interaction is needed.
await persisted.getByRole('button', { name: 'Schedule options' }).click();
await page.getByRole('menuitem', { name: 'Edit' }).click();
const editDialog = page.getByRole('dialog');