Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce1d27f8ca |
@@ -3038,3 +3038,57 @@ test('flere noter kan markeres, arkiveres og hentes frem igen', async ({ page })
|
||||
|
||||
expect(trouble).toEqual([]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Én note, der ikke kan tegnes, må ikke tage ruden med sig.
|
||||
*
|
||||
* Meldt ind som "jeg kan ikke skifte mellem noter — uanset hvilken jeg trykker
|
||||
* på, åbner de ikke". Editoren indlæste med `note.body.trim()` uden værn, og
|
||||
* `body` er en streng fra serveren hver eneste gang — indtil den ikke er. Så
|
||||
* kaster den inde i et `$effect`, og en effekt, der kaster, tager komponentens
|
||||
* opdateringer med sig: den forrige note bliver stående, og ingen note kan åbnes
|
||||
* bagefter. Klikket kan ikke engang lande.
|
||||
*
|
||||
* Formen fremtvinges her frem for at vente på den nyttelast, der gjorde det:
|
||||
* hvad der end fik `body` til at mangle, er svaret det samme — én note må ikke
|
||||
* kunne låse de tolv hundrede andre ude.
|
||||
*/
|
||||
test('en note uden krop må ikke låse ruden', async ({ page }) => {
|
||||
const errors = [];
|
||||
page.on('pageerror', (e) => errors.push(e.message));
|
||||
|
||||
await page.goto('/noter');
|
||||
for (const title of ['Alfa', 'Beta']) {
|
||||
await page.getByRole('button', { name: 'Ny note' }).click();
|
||||
const ed = page.getByRole('textbox', { name: 'Notens tekst' });
|
||||
await ed.click();
|
||||
await page.keyboard.type(title);
|
||||
await expect(ed.locator('h1')).toHaveText(title);
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.type('Brødtekst i ' + title);
|
||||
await expect(ed.locator('p').filter({ hasText: 'Brødtekst i ' + title })).toBeVisible();
|
||||
await page.waitForTimeout(900);
|
||||
}
|
||||
await page.reload();
|
||||
await expect(page.locator('li .row strong').filter({ hasText: 'Beta' })).toBeVisible();
|
||||
|
||||
await page.route('**/api/v1/notes/*', async (route) => {
|
||||
if (route.request().method() !== 'GET') return route.continue();
|
||||
const res = await route.fetch();
|
||||
const note = await res.json();
|
||||
delete note.body;
|
||||
await route.fulfill({ response: res, json: note });
|
||||
});
|
||||
|
||||
await page.locator('li').filter({ hasText: 'Alfa' }).first().click();
|
||||
await page.waitForTimeout(600);
|
||||
expect(errors, 'noten kastede under indlæsningen').toEqual([]);
|
||||
|
||||
await page.unroute('**/api/v1/notes/*');
|
||||
await page.locator('li').filter({ hasText: 'Beta' }).first().click();
|
||||
await expect(
|
||||
page.getByRole('textbox', { name: 'Notens tekst' }).locator('h1'),
|
||||
'ruden er låst: den næste note åbner ikke'
|
||||
).toHaveText('Beta', { timeout: 5000 });
|
||||
expect(errors, 'ruden efterlod en fejl bag sig').toEqual([]);
|
||||
});
|
||||
|
||||
@@ -35,20 +35,53 @@
|
||||
let loadedId = $state(null);
|
||||
$effect(() => {
|
||||
if (!editor || !note || note.id === loadedId) return;
|
||||
// An empty note opens on its title. That is what Apple Notes does, and it is
|
||||
// also what the list needs: the first line becomes the name of the note, so a
|
||||
// note whose first line is body text is a note called by its first sentence.
|
||||
|
||||
// Sat *før* tegningen, ikke efter.
|
||||
//
|
||||
// Tested on the text and not on the rendered result: an empty body renders as
|
||||
// one empty paragraph, not as nothing, so a falsy check here never fired.
|
||||
editor.innerHTML = note.body.trim() ? markdownToHtml(note.body) : '<h1><br></h1>';
|
||||
// Kaster noget herunder, er noten stadig den, der er forsøgt indlæst — og
|
||||
// uden det her ville effekten prøve igen ved hver eneste ændring bagefter og
|
||||
// kaste hver gang.
|
||||
loadedId = note.id;
|
||||
active = {};
|
||||
// Kildevisningen hører til den note, den blev slået til på. Skifter man note,
|
||||
// er det den rige flade, man skal møde — ellers står den næste note som kode,
|
||||
// uden at nogen har bedt om det.
|
||||
asSource = false;
|
||||
colourCode();
|
||||
|
||||
// Én note, der ikke kan tegnes, må ikke tage ruden med sig.
|
||||
//
|
||||
// Det var `note.body.trim()` uden værn, og `body` er en streng fra serveren
|
||||
// hver eneste gang — indtil den ikke er. Så kaster den her inde i et
|
||||
// `$effect`, og en effekt, der kaster, tager komponentens opdateringer med
|
||||
// sig: den forrige note bliver stående, og *ingen* note kan åbnes bagefter.
|
||||
// Klikket kan ikke engang lande. Meldt ind som "jeg kan ikke skifte mellem
|
||||
// noter, uanset hvilken jeg trykker på", hvilket er præcis, hvad det ser ud
|
||||
// som fra den anden side.
|
||||
//
|
||||
// Fejlen står i konsollen frem for i en toast: den hører til den, der kan
|
||||
// rette den, og en note, der viser sin egen tekst uden formatering, siger
|
||||
// allerede til den, der læser den, at noget er galt.
|
||||
//
|
||||
// `textContent` og ikke `innerHTML` i faldbakken — teksten er ikke tegnet, og
|
||||
// at lægge den ind som markup ville være at køre det, tegningen lige nægtede.
|
||||
// Gemningen er ikke i fare: den skriver `draft`, som kommer fra svaret og
|
||||
// ikke fra det, der står i editoren, indtil nogen taster i den.
|
||||
const body = note.body ?? '';
|
||||
try {
|
||||
// An empty note opens on its title. That is what Apple Notes does, and it
|
||||
// is also what the list needs: the first line becomes the name of the
|
||||
// note, so a note whose first line is body text is a note called by its
|
||||
// first sentence.
|
||||
//
|
||||
// Tested on the text and not on the rendered result: an empty body renders
|
||||
// as one empty paragraph, not as nothing, so a falsy check here never
|
||||
// fired.
|
||||
editor.innerHTML = body.trim() ? markdownToHtml(body) : '<h1><br></h1>';
|
||||
colourCode();
|
||||
} catch (e) {
|
||||
editor.textContent = body;
|
||||
console.error('verdande: noten kunne ikke tegnes', note.id, e);
|
||||
}
|
||||
});
|
||||
|
||||
const SYNTAX = [
|
||||
|
||||
Reference in New Issue
Block a user