feat(links): vælg hvilken browser et link åbner i
Ønsket var en liste over de installerede browsere. Den findes ikke: en webside kan hverken se, hvilke browsere der er på maskinen — listen er lukket land, fordi den er et fingeraftryk, der ville følge folk rundt på nettet — eller bede systemet om at bruge en bestemt af dem. Det eneste håndtag er de URL-skemaer, browserne selv registrerer: firefox://open-url, googlechrome://, microsoft-edge:. Dem kan man prøve, og man kan ikke spørge, om de findes. Så indstillingen er skrevet som det, den er — et forsøg med et faldback — frem for som et løfte, der ville holde på telefonen og ikke på en Mac. Valget står i localStorage, ikke på kontoen. Samme grund som sidebjælkens bredde: det er en egenskab ved maskinen, man sidder ved. firefox:// virker på telefonen og gør ingenting på Mac'en, så et valg, der fulgte kontoen, ville være forkert det ene af de to steder hver gang. Ét sted i koden frem for ét pr. komponent. Links står i opgavetitler, i beskrivelser, i noter og i indstillingerne, og en regel, der skulle gentages hvert af de steder, ville mangle det ene sted, ingen tænkte på. Kun udadgående links; programmets egne ruter går gennem SvelteKit som før. To ting, målingen rettede undervejs: Det skjulte iframe — den kendte opskrift — bliver blokeret af appens egen Content-Security-Policy, som siger default-src 'self'. Uden e2e-prøven, der fangede CSP-fejlen i konsollen, ville skemaet aldrig være blevet forsøgt i produktion, og indstillingen ville have set ud til at virke, fordi faldbacket åbnede linket alligevel. Nu sættes location i stedet: et ukendt skema afbryder navigationen frem for at forlade siden. Og faldbacket troede sig blokeret hver eneste gang. window.open med noopener returnerer altid null — det står i specifikationen — så kontrollen af, om fanen blev åbnet, faldt altid ud til "blokeret", og den aktuelle fane blev navigeret væk i stedet. Håndtaget er det eneste, der kan kende en blokeret fane fra en åbnet, så det bruges nu, og opener ryddes bagefter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e931ddc313
commit
5eb689e4c2
@@ -4272,3 +4272,56 @@ test('en gentagelse, serveren kun kan på dansk, siges alligevel i fladen', asyn
|
||||
|
||||
expect(trouble).toEqual([]);
|
||||
});
|
||||
|
||||
test('links kan åbnes i en anden browser, og åbner alligevel når skemaet ikke findes', async ({
|
||||
page
|
||||
}) => {
|
||||
const trouble = watchForTrouble(page);
|
||||
|
||||
await page.goto('/indstillinger');
|
||||
await expect(page.getByRole('heading', { name: 'Links' })).toBeVisible();
|
||||
|
||||
// Som udgangspunkt gør indstillingen ingenting, og der er intet skemafelt at
|
||||
// skrive i: et felt til en adresse, der ikke bliver brugt, er en invitation
|
||||
// til at udfylde den og undre sig.
|
||||
await expect(page.getByLabel('Skema')).toBeHidden();
|
||||
|
||||
// Et skema, der med sikkerhed ikke findes på nogen maskine. Pointen er ikke,
|
||||
// at det virker — pointen er, hvad der sker, når det ikke gør.
|
||||
await page.getByLabel('Åbn links i').selectOption('custom');
|
||||
await page.getByLabel('Skema').fill('verdande-findes-ikke://{url}');
|
||||
await page.getByLabel('Skema').blur();
|
||||
|
||||
// Valget hører til maskinen, ikke kontoen, så det står i localStorage.
|
||||
const stored = await page.evaluate(() => localStorage.getItem('verdande:link-opener'));
|
||||
expect(stored, 'valget blev ikke gemt på apparatet').toContain('verdande-findes-ikke');
|
||||
|
||||
// Om browseren rent faktisk tegner en fane, er browserens sag og ikke appens —
|
||||
// en headless Chromium åbner den uden at fortælle nogen om det. Det, der er
|
||||
// vores, er beslutningen: prøv skemaet, og åbn linket alligevel, når det ikke
|
||||
// findes. Så det er dét, der bliver målt.
|
||||
await page.evaluate(() => {
|
||||
window.__opened = [];
|
||||
window.open = (url) => {
|
||||
window.__opened.push(url);
|
||||
return { closed: false, close() {} };
|
||||
};
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Prøv det' }).click();
|
||||
await expect
|
||||
.poll(() => page.evaluate(() => window.__opened), { timeout: 5000 })
|
||||
.toEqual(['https://example.dk']);
|
||||
|
||||
// Og appen blev, hvor den var. Faldbacket må ikke kapre den fane, man står i:
|
||||
// et link, der smider en ud af programmet, er værre end et, der åbner et sted,
|
||||
// man ikke havde valgt.
|
||||
await expect(page).toHaveURL(/\/indstillinger$/);
|
||||
|
||||
// Tilbage til systemets standard, så resten af prøverne klikker på links som
|
||||
// alle andre.
|
||||
await page.getByLabel('Åbn links i').selectOption('');
|
||||
await expect(page.getByLabel('Skema')).toBeHidden();
|
||||
|
||||
expect(trouble).toEqual([]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user