From 9686991c82f8ed85614e0b35b9348f2e967fdbd4 Mon Sep 17 00:00:00 2001 From: "python-templates-copier-update[bot]" Date: Sun, 30 Aug 2026 10:32:15 +0000 Subject: [PATCH] Update from Copier (2026-08-30T10-32-08Z) Signed-off-by: python-templates-copier-update[bot] --- .copier-answers.yaml | 3 ++- README.md | 3 +++ playwright.config.ts | 12 ++++++++---- src/app.css | 28 ++++++++++++++++++++++++--- src/app.html | 20 +++++++++++++++++++ src/routes/+error.svelte | 18 +++++++++++++++++ src/routes/+layout.svelte | 32 +++++++++++++++++++++++++++++++ src/routes/+layout.ts | 1 + src/routes/sitemap.xml/+server.ts | 18 +++++++++++++++++ static/favicon.svg | 13 +++++++++++++ tests/a11y/audit.spec.ts | 23 ++++++++++++++++------ tests/behavior/theme.spec.ts | 15 +++++++++++++++ tests/visual/pages.spec.ts | 13 +++++++++++++ 13 files changed, 185 insertions(+), 14 deletions(-) create mode 100644 src/routes/+error.svelte create mode 100644 src/routes/sitemap.xml/+server.ts create mode 100644 static/favicon.svg create mode 100644 tests/behavior/theme.spec.ts create mode 100644 tests/visual/pages.spec.ts diff --git a/.copier-answers.yaml b/.copier-answers.yaml index 2d2834a..958898a 100644 --- a/.copier-answers.yaml +++ b/.copier-answers.yaml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 7522e24 +_commit: 195919b _src_path: https://github.com/python-project-templates/base.git add_extension: site-sveltekit brand_name: Example @@ -8,3 +8,4 @@ project_description: A fast, accessible SvelteKit website. project_name: javascript-template-site-sveltekit site_adapter: static site_url: https://example.com + diff --git a/README.md b/README.md index 8f0a2a8..2ac9cad 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,7 @@ pnpm test pnpm build ``` +Visual regression snapshots live beside `tests/visual/` and are skipped when `CI` is set. Record or +refresh them locally with `pnpm test:e2e:update`. + This site uses SvelteKit's `adapter-static` deployment adapter. diff --git a/playwright.config.ts b/playwright.config.ts index 8165435..d3dfc15 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,10 +2,16 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: 'tests', + testMatch: ['**/*.spec.ts'], + testIgnore: process.env.CI ? ['**/visual/**'] : [], + snapshotPathTemplate: '{testDir}/{testFilePath}-snapshots/{arg}-{projectName}{ext}', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, reporter: process.env.CI ? 'github' : 'list', + expect: { + toHaveScreenshot: { maxDiffPixelRatio: 0.01, threshold: 0.2, animations: 'disabled', caret: 'hide' } + }, use: { baseURL: 'http://127.0.0.1:4177', trace: 'retain-on-failure', @@ -18,9 +24,7 @@ export default defineConfig({ timeout: 120_000 }, projects: [ - { - name: 'desktop', - use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 800 } } - } + { name: 'desktop', use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 800 } } }, + { name: 'mobile', use: { ...devices['Desktop Chrome'], viewport: { width: 390, height: 844 } } } ] }); diff --git a/src/app.css b/src/app.css index 3169048..751a8af 100644 --- a/src/app.css +++ b/src/app.css @@ -1,13 +1,35 @@ @import 'tailwindcss'; :root { - color: #171717; - background: #ffffff; - font-family: system-ui, sans-serif; + --paper: #ffffff; + --ink: #171717; + --muted: #5c5c5c; + --line: #e4e4e4; + + color-scheme: light; +} + +:root[data-theme='dark'] { + --paper: #0b0b0b; + --ink: #f2f2f2; + --muted: #a1a1a1; + --line: #262626; + + color-scheme: dark; +} + +@theme inline { + --color-paper: var(--paper); + --color-ink: var(--ink); + --color-muted: var(--muted); + --color-line: var(--line); } body { margin: 0; + color: var(--ink); + background: var(--paper); + font-family: system-ui, sans-serif; } a { diff --git a/src/app.html b/src/app.html index a15015b..0e31c8a 100644 --- a/src/app.html +++ b/src/app.html @@ -4,6 +4,26 @@ + + %sveltekit.head% diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 0000000..bc24881 --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,18 @@ + + + + {page.status} — Example + + +
+
+

Error / {page.status}

+

This path went quiet.

+

+ {page.error?.message ?? 'The page could not be found.'} +

+ Return home → +
+
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 373a21f..e8d4647 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,7 +1,29 @@ @@ -13,4 +35,14 @@ + + {@render children()} diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 189f71e..ba58d86 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -1 +1,2 @@ export const prerender = true; +export const trailingSlash = 'always'; diff --git a/src/routes/sitemap.xml/+server.ts b/src/routes/sitemap.xml/+server.ts new file mode 100644 index 0000000..bbf35ed --- /dev/null +++ b/src/routes/sitemap.xml/+server.ts @@ -0,0 +1,18 @@ +import type { RequestHandler } from './$types'; + +export const prerender = true; + +const pages = ['']; + +export const GET: RequestHandler = () => { + const urls = pages.map((page) => `https://example.com/${page}`).join(''); + + return new Response( + `${urls}`, + { + headers: { + 'Content-Type': 'application/xml' + } + } + ); +}; diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..2a7bdfb --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1,13 @@ + + + E + diff --git a/tests/a11y/audit.spec.ts b/tests/a11y/audit.spec.ts index 040bf0f..f443181 100644 --- a/tests/a11y/audit.spec.ts +++ b/tests/a11y/audit.spec.ts @@ -1,10 +1,21 @@ import AxeBuilder from '@axe-core/playwright'; import { expect, test } from '@playwright/test'; -test('home page has no automatically detectable accessibility violations', async ({ page }) => { - await page.goto('/'); - await expect(page).toHaveTitle('Example'); +const routes = ['/']; - const results = await new AxeBuilder({ page }).analyze(); - expect(results.violations).toEqual([]); -}); +for (const route of routes) { + for (const theme of ['light', 'dark'] as const) { + test(`${route} has no serious or critical accessibility violations in ${theme} mode`, async ({ page }) => { + await page.emulateMedia({ reducedMotion: 'reduce' }); + await page.addInitScript((value) => localStorage.setItem('theme', value), theme); + await page.goto(route); + + const results = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']).analyze(); + const violations = results.violations.filter((violation) => + ['serious', 'critical'].includes(violation.impact ?? '') + ); + + expect(violations).toEqual([]); + }); + } +} diff --git a/tests/behavior/theme.spec.ts b/tests/behavior/theme.spec.ts new file mode 100644 index 0000000..fcf1a8f --- /dev/null +++ b/tests/behavior/theme.spec.ts @@ -0,0 +1,15 @@ +import { expect, test } from '@playwright/test'; + +test('theme selection persists across navigation', async ({ page }) => { + await page.goto('/'); + await page.evaluate(() => localStorage.removeItem('theme')); + await page.reload(); + + const initial = await page.locator('html').getAttribute('data-theme'); + await page.getByTestId('theme-toggle').click(); + const expected = initial === 'dark' ? 'light' : 'dark'; + + await expect(page.locator('html')).toHaveAttribute('data-theme', expected); + await page.reload(); + await expect(page.locator('html')).toHaveAttribute('data-theme', expected); +}); diff --git a/tests/visual/pages.spec.ts b/tests/visual/pages.spec.ts new file mode 100644 index 0000000..1d0224f --- /dev/null +++ b/tests/visual/pages.spec.ts @@ -0,0 +1,13 @@ +import { expect, test } from '@playwright/test'; + +const routes = [{ name: 'home', path: '/' }]; + +for (const route of routes) { + for (const theme of ['light', 'dark'] as const) { + test(`${route.name} in ${theme} mode`, async ({ page }) => { + await page.addInitScript((value) => localStorage.setItem('theme', value), theme); + await page.goto(route.path); + await expect(page).toHaveScreenshot(`${route.name}-${theme}.png`, { fullPage: true }); + }); + } +}