fix(bug-report): Bildschirmfoto scheitert nicht mehr an einem fremden Bild

html-to-image laedt jedes <img> per fetch nach; ein einziges Bild ohne CORS
(z. B. ein direkt von der Website geholtes Favoriten-Symbol) liess die ganze
Aufnahme scheitern, im Dialog blieb das Haekchen "Bildschirmfoto beifuegen"
gesperrt. Nicht ladbare Bilder werden jetzt zum transparenten Pixel; scheitert
die Aufnahme trotzdem, folgt ein zweiter Versuch ohne Bilder und Rahmen.
Im Linux-Client 1.3.1 nachgestellt und nach dem Fix gegengeprueft.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 15:52:02 +02:00
parent b03ffb5d10
commit bf4384ad73
2 changed files with 73 additions and 4 deletions
+43 -1
View File
@@ -1,5 +1,8 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { sendBugReport } from './bug-report-api';
import { captureScreenshot, sendBugReport } from './bug-report-api';
const { mockToPng } = vi.hoisted(() => ({ mockToPng: vi.fn() }));
vi.mock('html-to-image', () => ({ toPng: mockToPng }));
/**
* bug-report-api.test — NEU (quick-260918-gza). Bisher pruefte nur der
@@ -85,3 +88,42 @@ describe('sendBugReport (quick-260918-gza)', () => {
).resolves.toEqual({ ok: false, status: 429 });
});
});
describe('captureScreenshot (Bild von fremder Adresse)', () => {
beforeEach(() => {
mockToPng.mockReset();
});
it('ersetzt nicht ladbare Bilder statt die Aufnahme abzubrechen', async () => {
mockToPng.mockResolvedValue('data:image/png;base64,AAA');
await expect(captureScreenshot()).resolves.toBe('data:image/png;base64,AAA');
const opts = mockToPng.mock.calls[0][1];
expect(opts.imagePlaceholder).toMatch(/^data:image\/png;base64,/);
expect(opts.onImageErrorHandler()).toBeUndefined();
});
it('versucht es ein zweites Mal ohne Bilder und Rahmen, wenn der erste Versuch scheitert', async () => {
mockToPng
.mockRejectedValueOnce(new Error('tainted'))
.mockResolvedValueOnce('data:image/png;base64,BBB');
await expect(captureScreenshot()).resolves.toBe('data:image/png;base64,BBB');
expect(mockToPng).toHaveBeenCalledTimes(2);
const filter = mockToPng.mock.calls[1][1].filter as (n: Node) => boolean;
expect(filter(document.createElement('img'))).toBe(false);
expect(filter(document.createElement('iframe'))).toBe(false);
expect(filter(document.createElement('div'))).toBe(true);
const ignored = document.createElement('div');
ignored.dataset.bugReportIgnore = 'true';
expect(filter(ignored)).toBe(false);
});
it('liefert null, wenn auch der zweite Versuch scheitert', async () => {
mockToPng.mockRejectedValue(new Error('kaputt'));
await expect(captureScreenshot()).resolves.toBeNull();
});
});