From 268b7d01fd973235526e2001b255c1e2d05497d8 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Wed, 22 Jul 2026 19:27:10 +0200 Subject: [PATCH] test: harden visual resource rendering --- src/components/PageReader.test.tsx | 7 +-- src/components/PageReader.tsx | 20 ++++++--- tests/content-model.test.ts | 69 ++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 11 deletions(-) diff --git a/src/components/PageReader.test.tsx b/src/components/PageReader.test.tsx index 4408041..63ec432 100644 --- a/src/components/PageReader.test.tsx +++ b/src/components/PageReader.test.tsx @@ -94,11 +94,8 @@ const page: OneNotePageDto = { describe('PageReader structured content', () => { beforeEach(() => { - vi.stubGlobal('URL', { - ...URL, - createObjectURL: vi.fn(() => 'blob:local-image'), - revokeObjectURL: vi.fn(), - }); + vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:local-image'); + vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined); }); it('renders semantic formatting, links, tables, and a signed image resource', async () => { diff --git a/src/components/PageReader.tsx b/src/components/PageReader.tsx index 418fdca..a7894d4 100644 --- a/src/components/PageReader.tsx +++ b/src/components/PageReader.tsx @@ -426,15 +426,20 @@ function ImageView({ function InkView({ ink }: { ink: OneNoteInkBlock }) { const box = ink.boundingBox ?? boundingBoxFromInk(ink.strokes); - const padding = Math.max( - 140, - ...ink.strokes.map((stroke) => stroke.width ?? 0) + const widestStroke = ink.strokes.reduce( + (widest, stroke) => Math.max(widest, stroke.width ?? 0), + 0 ); + const padding = Math.max(140, Math.min(1_000_000, widestStroke)); const viewBox = box ? `${box.x - padding / 2} ${box.y - padding / 2} ${Math.max(box.width + padding, 1)} ${Math.max(box.height + padding, 1)}` : '0 0 1 1'; - const width = box ? Math.max((box.width + padding) / (2540 / 96), 1) : 1; - const height = box ? Math.max((box.height + padding) / (2540 / 96), 1) : 1; + const width = box + ? Math.min(Math.max((box.width + padding) / (2540 / 96), 1), 10_000) + : 1; + const height = box + ? Math.min(Math.max((box.height + padding) / (2540 / 96), 1), 10_000) + : 1; return (
@@ -452,7 +457,10 @@ function InkView({ ink }: { ink: OneNoteInkBlock }) { d={inkPath(stroke)} fill="none" stroke={inkColor(stroke.color)} - strokeWidth={Math.max(stroke.width ?? 140, 1)} + strokeWidth={Math.min( + Math.max(stroke.width ?? 140, 1), + 1_000_000 + )} strokeOpacity={ stroke.transparency === undefined ? undefined diff --git a/tests/content-model.test.ts b/tests/content-model.test.ts index 4f34a71..a019cb0 100644 --- a/tests/content-model.test.ts +++ b/tests/content-model.test.ts @@ -121,6 +121,75 @@ describe('OneNote rich-content model', () => { ).toBeUndefined(); }); + it('retains an embedded-file node as a downloadable lazy resource', () => { + const guid = '{22222222-2222-2222-2222-222222222222}'; + const attachmentId = id(guid, 1); + const containerId = id(guid, 2); + const payload = Uint8Array.of(1, 2, 3, 4); + const attachment = object( + attachmentId, + JCID.EmbeddedFileNode, + { + ...emptyProps(), + objectIds: [compact(2)], + properties: { + entries: [ + referenceEntry(PROPERTY.EmbeddedFileContainer, 'one'), + bytesEntry( + PROPERTY.EmbeddedFileName, + Buffer.from('report.txt', 'utf16le') + ), + ], + }, + }, + guid + ); + const container = object( + containerId, + JCID.EmbeddedFileContainer, + emptyProps(), + guid + ); + container.fileData = { + referenceKind: 'embedded', + dataReference: '{22222222-2222-2222-2222-222222222222}', + extension: '.txt', + blob: { source: payload, offset: 0, size: payload.length }, + }; + const resources = new Map(); + const space: ObjectSpace = { + id: id(guid, 0), + roots: new Map(), + objects: new Map([ + [exGuidKey(attachment.id), attachment], + [exGuidKey(container.id), container], + ]), + }; + + const blocks = parseContentObjects([attachmentId], { + space, + limits: { ...DEFAULT_ONENOTE_PARSER_LIMITS }, + diagnostics: [], + resources, + }); + + expect(blocks).toEqual([ + expect.objectContaining({ + kind: 'attachment', + filename: 'report.txt', + size: 4, + resourceId: exGuidKey(containerId), + }), + ]); + expect(resources.get(exGuidKey(containerId))).toMatchObject({ + kind: 'attachment', + extension: 'txt', + mediaType: 'text/plain', + availability: 'available', + size: 4, + }); + }); + it('bounds aggregate ink point expansion', () => { const model = parseOneNoteSectionContent(fixture, { limits: { maxInkPoints: 100 },