feat(notes): et billede kan indsættes og trækkes ind i en note
Rapporteret fra brug: man kunne ikke indsætte billeder eller trække filer ind i noter. Det passede — importen kunne tage billeder med, og intet andet kunne. Indsætningen tog kun `text/plain` og smed resten væk, og der var slet ingen håndtering af et træk. At indsætte et skærmbillede er det mest almindelige, der findes i et noteprogram, og det gjorde ingenting overhovedet. Der manglede også et sted at lægge filen. Vedhæftninger på en note fandtes i databasen, siden migration 0021, men kun importen kunne skrive dem; der var ingen rute. Nu er der en, med editor-rettigheder på noten og samme svar for "findes ikke" og "ikke din", så et id ikke kan afprøves. Skrevet ind som `` og ikke som en data-URL: en note er Markdown i en database, og et fotografi fra en telefon som base64 midt i teksten ville gøre noten til fire megabyte tekst, der skal læses hver gang listen tegnes. `execCommand` frem for at bygge noden selv, fordi det er dét, der lægger handlingen i browserens egen fortryd-historik — ⌘Z tager billedet ud igen, ligesom det tager et ord ud. `preventDefault` på både dragover og drop. Uden den første kommer der intet drop; uden den anden åbner browseren filen i stedet, og man mister det, man var i gang med at skrive, til en fane med et billede i. Kun filer: et træk inde fra appen selv bærer vores egen nyttelast og skal falde igennem til den, der lyttede. Prøven går begge veje ind, ser at billederne bliver hentet og tegnet, og læser filen bagefter for at sikre, at der står to bilagsadresser og ingen data-URL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
241c5ed37d
commit
3991ec9445
@@ -2434,3 +2434,68 @@ test('en note kan ikke smugle et script ind gennem et billedes alt-tekst', async
|
||||
// andet skal stadig være stille.
|
||||
expect(trouble.filter((t) => !t.includes('/api/v1/attachments/'))).toEqual([]);
|
||||
});
|
||||
|
||||
test('et billede kan indsættes og trækkes ind i en note', async ({ page }) => {
|
||||
const trouble = watchForTrouble(page);
|
||||
await page.goto('/noter');
|
||||
|
||||
const id = await page.evaluate(async () => {
|
||||
const r = await fetch('/api/v1/notes', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json', 'Sec-Fetch-Site': 'same-origin' },
|
||||
body: JSON.stringify({ body: '# Med billeder\n\nEn linje.\n' })
|
||||
});
|
||||
return (await r.json()).id;
|
||||
});
|
||||
await page.goto('/noter?note=' + id);
|
||||
const ed = page.getByRole('textbox', { name: 'Notens tekst' });
|
||||
await expect(ed).toBeVisible();
|
||||
|
||||
// Et rigtigt lille PNG, bygget i browseren, så prøven ikke har brug for en fil
|
||||
// på disken — og lagt i både en paste og et drop, som er de to veje ind.
|
||||
const png =
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
|
||||
|
||||
for (const kind of ['paste', 'drop']) {
|
||||
await ed.click();
|
||||
await page.evaluate(
|
||||
async ({ kind, png }) => {
|
||||
const bytes = Uint8Array.from(atob(png), (c) => c.charCodeAt(0));
|
||||
const file = new File([bytes], `${kind}.png`, { type: 'image/png' });
|
||||
const dt = new DataTransfer();
|
||||
dt.items.add(file);
|
||||
const ed = document.querySelector('.page[contenteditable]');
|
||||
ed.focus();
|
||||
ed.dispatchEvent(
|
||||
kind === 'paste'
|
||||
? new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true })
|
||||
: new DragEvent('drop', { dataTransfer: dt, bubbles: true, cancelable: true })
|
||||
);
|
||||
},
|
||||
{ kind, png }
|
||||
);
|
||||
await page.waitForTimeout(1200);
|
||||
}
|
||||
|
||||
// To billeder, begge hentet og tegnet — ikke to stykker tekst med ).toHaveCount(2);
|
||||
const loaded = await ed.locator('img').evaluateAll((els) =>
|
||||
els.filter((e) => e.complete && e.naturalWidth > 0).length
|
||||
);
|
||||
expect(loaded).toBe(2);
|
||||
|
||||
// Og de overlever turen til Markdown: filen skal pege på et bilag, ikke på en
|
||||
// data-URL, som ville lægge fotografiet i noteteksten.
|
||||
const body = await page.evaluate(async (noteId) => {
|
||||
const r = await fetch('/api/v1/notes/' + noteId, {
|
||||
credentials: 'include',
|
||||
headers: { 'Sec-Fetch-Site': 'same-origin' }
|
||||
});
|
||||
return (await r.json()).body;
|
||||
}, id);
|
||||
expect(body.match(/!\[\]\(\/api\/v1\/attachments\/[0-9a-f-]+\)/g)).toHaveLength(2);
|
||||
expect(body).not.toContain('data:image');
|
||||
|
||||
expect(trouble).toEqual([]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user