The React PreferenceSwitch island is replaced by PreferenceSwitch.astro: static markup, an inline script that presses the stored choice before first paint, and a page script for clicks and lw:pref. Used on the start page and the work bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
2.8 KiB
TypeScript
71 lines
2.8 KiB
TypeScript
import type { Page } from '@playwright/test';
|
|
import config from '../playwright.config.ts';
|
|
|
|
export const PREF_KEY = 'lw:projects-made';
|
|
|
|
// Fixture ids (see fixtures/content): levels by-hand / some-ai / ai-made / unknown.
|
|
export const P = {
|
|
byHand: 'example-compute-portal', // featured 1 (not in the list)
|
|
byHand2: 'demo-course-planner',
|
|
someAi: 'fixture-dashboard', // featured 2
|
|
aiMade: 'sample-garden-bot',
|
|
unknown: 'placeholder-notes',
|
|
draft: 'draft-unfinished-thing',
|
|
};
|
|
export const I = {
|
|
soundtrackFilm: 'fixture-soundtrack-film', // [film-tv, music], spotify media url
|
|
song: 'example-song', // youtube link, no ai (a "share")
|
|
byHand: 'own-drawing',
|
|
unknown: 'fixture-life',
|
|
someAi: 'synthetic-life-note',
|
|
};
|
|
|
|
// Keeps tests offline: only the local server is reachable. Returns the blocked URLs.
|
|
const servers = Array.isArray(config.webServer) ? config.webServer : config.webServer ? [config.webServer] : [];
|
|
const LOCAL = new Set(servers.map((s) => ('url' in s && s.url ? new URL(s.url).origin : '')));
|
|
|
|
export async function blockThirdParty(page: Page): Promise<string[]> {
|
|
const blocked: string[] = [];
|
|
await page.route(/^https?:\/\//, (r) => {
|
|
const url = r.request().url();
|
|
if (LOCAL.has(new URL(url).origin)) return r.continue();
|
|
// The analytics script is the one allowed third-party request; it is aborted but not counted.
|
|
if (!url.startsWith('https://analytics.lucaswinther.info/')) blocked.push(url);
|
|
return r.abort();
|
|
});
|
|
return blocked;
|
|
}
|
|
|
|
export async function setPref(page: Page, value: string) {
|
|
await page.addInitScript(([k, v]) => localStorage.setItem(k!, v!), [PREF_KEY, value]);
|
|
}
|
|
|
|
// Waits until the named islands have hydrated (Astro drops the `ssr` attribute), scrolling
|
|
// client:visible ones into view first.
|
|
export async function hydrated(page: Page, ...names: string[]) {
|
|
for (const n of names) {
|
|
await page.evaluate((name) => {
|
|
const el = document.querySelector(`astro-island[component-url*="/${name}."]`);
|
|
(el?.firstElementChild ?? el?.parentElement)?.scrollIntoView({ block: 'center' });
|
|
}, n);
|
|
await page.waitForFunction((name) => {
|
|
const el = document.querySelector(`astro-island[component-url*="/${name}."]`);
|
|
return !!el && !el.hasAttribute('ssr');
|
|
}, n);
|
|
}
|
|
}
|
|
|
|
// Open /work/ with the sheet, the list and the preference switch live. The switch is a plain module
|
|
// script (spec 006), which has run by the time goto resolves on `load`.
|
|
export async function openWork(page: Page) {
|
|
await page.goto('/work/');
|
|
await hydrated(page, 'ProjectSheet', 'ProjectList');
|
|
await page.evaluate(() => scrollTo(0, 0));
|
|
}
|
|
|
|
// Open /off-the-clock/ with the chips live.
|
|
export async function openPersonal(page: Page) {
|
|
await page.goto('/off-the-clock/');
|
|
await hydrated(page, 'Chips');
|
|
}
|