diff --git a/web/e2e/smoke.spec.js b/web/e2e/smoke.spec.js index 9855b8e..8aace00 100644 --- a/web/e2e/smoke.spec.js +++ b/web/e2e/smoke.spec.js @@ -82,6 +82,12 @@ test('hvert link i sidebjælken fører et sted hen', async ({ page }) => { for (const href of hrefs) { await sidebar.locator(`a[href="${href}"]`).first().click(); + // Wait for the navigation before looking at the page. Without this the + // assertion can be satisfied by the heading of the page you just left — + // which is not a hypothetical: a change that removed a project page's + // heading entirely passed here on a fast machine and failed on CI. + await page.waitForURL(`**${href}`); + // Something with a heading rendered. A route written outside the routes // tree lands on the SPA shell and renders nothing — which is exactly how // the label route broke, and it answered 200 the whole time. @@ -196,9 +202,11 @@ test('et projekt kan omdøbes, slettes og hentes tilbage', async ({ page }) => { await sidebar.getByLabel('Projektnavn').fill('Ferie'); await sidebar.getByLabel('Projektnavn').press('Enter'); - const title = page.getByLabel('Projektets navn'); - await expect(title).toHaveValue('Ferie'); + await expect(page.getByRole('heading', { name: 'Ferie' })).toBeVisible(); + // The heading is the rename affordance: click it, and it becomes a field. + await page.getByRole('button', { name: 'Ferie' }).click(); + const title = page.getByLabel('Projektets navn'); await title.fill('Sommerferie'); await title.press('Enter'); await expect(sidebar.getByRole('link', { name: 'Sommerferie' })).toBeVisible(); diff --git a/web/src/routes/projekt/[id]/+page.svelte b/web/src/routes/projekt/[id]/+page.svelte index 64c7606..7943362 100644 --- a/web/src/routes/projekt/[id]/+page.svelte +++ b/web/src/routes/projekt/[id]/+page.svelte @@ -66,19 +66,19 @@ let open = $derived(app.tasks.filter((t) => !t.completed && !t.parent_id)); let unsectioned = $derived(open.filter((t) => !t.section_id)); + let editing = $state(false); + /** Renames on blur. A no-op when nothing changed, so tabbing through is free. */ async function rename(input) { const name = input.value.trim(); - if (!name || name === project.name) { - input.value = project.name; - return; - } + editing = false; + if (!name || name === project.name) return; + try { project = await api.updateProject(project.id, { name }); // The sidebar reads its own copy, so it has to be told. await app.refreshProjects(); } catch (e) { - input.value = project.name; app.toast(humanMessage(e)); } } @@ -123,11 +123,16 @@