diff --git a/apps/web/e2e/home.spec.ts b/apps/web/e2e/home.spec.ts index 793224e9..97e79379 100644 --- a/apps/web/e2e/home.spec.ts +++ b/apps/web/e2e/home.spec.ts @@ -1,7 +1,6 @@ -import type { Page } from '@playwright/test'; - import { home } from './demo'; import { expect, test } from './fixtures'; +import { inkMuted } from './ink'; test('the Home greets the user and lists what needs them', async ({ page }) => { await page.route('/api/home', (route) => route.fulfill({ json: home })); @@ -27,17 +26,6 @@ test('the Home leaves out This week when its answer has no week', async ({ page await expect(page.locator('main section').last()).toContainText('Your apps'); }); -// The colour --ink-muted gives text in the page's theme. -function inkMuted(page: Page) { - return page.evaluate(() => { - const probe = document.body.appendChild(document.createElement('span')); - probe.style.color = 'var(--ink-muted)'; - const { color } = getComputedStyle(probe); - probe.remove(); - return color; - }); -} - test("the Home says the hours are unavailable when home couldn't get the week", async ({ page }, testInfo) => { await page.route('/api/home', (route) => route.fulfill({ json: { ...home, week: undefined, weekState: 'unavailable' } }) diff --git a/apps/web/e2e/ink.ts b/apps/web/e2e/ink.ts new file mode 100644 index 00000000..788b5c85 --- /dev/null +++ b/apps/web/e2e/ink.ts @@ -0,0 +1,12 @@ +import type { Page } from '@playwright/test'; + +// The colour --ink-muted gives text in the page's theme. +export function inkMuted(page: Page) { + return page.evaluate(() => { + const probe = document.body.appendChild(document.createElement('span')); + probe.style.color = 'var(--ink-muted)'; + const { color } = getComputedStyle(probe); + probe.remove(); + return color; + }); +} diff --git a/apps/web/e2e/page-status.spec.ts b/apps/web/e2e/page-status.spec.ts new file mode 100644 index 00000000..b1ddcb5b --- /dev/null +++ b/apps/web/e2e/page-status.spec.ts @@ -0,0 +1,62 @@ +import type { Page } from '@playwright/test'; + +import { home, tickets } from './demo'; +import { expect, test } from './fixtures'; +import { inkMuted } from './ink'; +import { answerDismissals, answerTickets } from './jira'; +import { answerPulls, answerReviews } from './pulls'; + +// Each page's line in place of its sections, and the answers that make the page show it. +const lines: { name: string; path: string; says: string; answer: (page: Page) => Promise }[] = [ + { + name: 'PRs', + path: '/pulls', + says: "Bitbucket isn't set up.", + answer: async (page) => { + await answerPulls(page, 404); + await answerReviews(page, 404); + }, + }, + { + name: 'Time', + path: '/time', + says: "Jira isn't set up.", + answer: async (page) => { + await answerTickets(page, tickets); + await page.route('/api/time/report', (route) => route.fulfill({ status: 404 })); + }, + }, + { + name: 'Nudge', + path: '/nudge', + says: "Couldn't load the questions.", + answer: async (page) => { + await page.route('/api/nudge/state', (route) => route.fulfill({ status: 502 })); + await page.route('/api/nudge/events', (route) => + route.fulfill({ contentType: 'text/event-stream', body: 'retry: 600000\n: open\n\n' }) + ); + }, + }, + { + name: 'Todos', + path: '/todos', + says: "Couldn't load the todos.", + answer: async (page) => { + await page.route('/api/todos', (route) => route.fulfill({ status: 500, body: 'Internal Server Error\n' })); + await answerDismissals(page); + await answerTickets(page, tickets); + }, + }, +]; + +for (const { name, path, says, answer } of lines) { + test(`the ${name} page's status line is muted, with a section's space below it`, async ({ page }, testInfo) => { + await page.route('/api/home', (route) => route.fulfill({ json: home })); + await answer(page); + await page.goto(path); + + const line = page.getByText(says); + await expect(line).toHaveCSS('color', await inkMuted(page)); + await expect(line).toHaveCSS('padding-bottom', testInfo.project.name === 'phone' ? '48px' : '64px'); + }); +} diff --git a/apps/web/src/main.ts b/apps/web/src/main.ts index 4971f9a8..6809e1bb 100644 --- a/apps/web/src/main.ts +++ b/apps/web/src/main.ts @@ -9,6 +9,7 @@ import './styles/list.css'; import './styles/chip.css'; import './styles/icon-button.css'; import './styles/dialog.css'; +import './styles/page.css'; import App from './App.svelte'; import { applyChoice, loadChoice } from './shell/theme'; diff --git a/apps/web/src/nudge/NudgePage.svelte b/apps/web/src/nudge/NudgePage.svelte index d2ce143b..b1f9dadb 100644 --- a/apps/web/src/nudge/NudgePage.svelte +++ b/apps/web/src/nudge/NudgePage.svelte @@ -28,13 +28,5 @@ onMount(() => { {:else if loaded.kind === 'failed'} -

Couldn't load the questions.

+

Couldn't load the questions.

{/if} - - diff --git a/apps/web/src/pulls/PullsPage.svelte b/apps/web/src/pulls/PullsPage.svelte index d2429a5a..0499a939 100644 --- a/apps/web/src/pulls/PullsPage.svelte +++ b/apps/web/src/pulls/PullsPage.svelte @@ -22,7 +22,7 @@ onMount(() => { Pull requests {#if mine.kind === 'off' && reviews.kind === 'off'} -

Bitbucket isn't set up.

+

Bitbucket isn't set up.

{:else} { failed="Couldn't load the review requests." /> {/if} - - diff --git a/apps/web/src/styles/page.css b/apps/web/src/styles/page.css new file mode 100644 index 00000000..cace676a --- /dev/null +++ b/apps/web/src/styles/page.css @@ -0,0 +1,14 @@ +/* A page's status line in place of its sections, such as "Bitbucket isn't set up." (decision 206): muted, with the + space that a section leaves under it. */ +.page-status { + margin: 0; + padding: 0 var(--page-padding) 64px; + color: var(--ink-muted); + overflow-wrap: anywhere; +} + +@media (max-width: 700px) { + .page-status { + padding-bottom: 48px; + } +} diff --git a/apps/web/src/time/TimePage.svelte b/apps/web/src/time/TimePage.svelte index d1779199..4bbecee4 100644 --- a/apps/web/src/time/TimePage.svelte +++ b/apps/web/src/time/TimePage.svelte @@ -29,13 +29,13 @@ onMount(() => { Time {#if report.kind === 'off'} -

Jira isn't set up.

+

Jira isn't set up.

{:else if report.kind === 'failed'} -

Couldn't load the time report.

+

Couldn't load the time report.

{:else if report.kind === 'loaded'} {@const { days, week, searched, failure } = report.snapshot} {#if failure} -

+

The last search failed: {failure}. {#if days && searched} These hours are from . @@ -59,24 +59,12 @@ onMount(() => {