9f02fcc190
Der alte Test 1 fand den Fehler nur durch Zufall: er las den DOM einmal, nachdem findByRole den Dialog gemeldet hatte, und traf damit mal den falschen ersten, meist den richtigen zweiten Commit. Ein Fehlschlag auf rund siebzehn volle Laeufe. Die beiden neuen Tests beobachten stattdessen per MutationObserver JEDEN Commit waehrend des Oeffnens und dulden keinen einzigen, in dem das Vorschaubild sichtbar ist und das Haekchen aus. Test 14 deckt das erste Oeffnen ab, Test 15 das erneute Oeffnen nach einem Versand mit abgewaehltem Haekchen und getipptem Text - dort darf weder der alte Dankestext noch der alte Text noch ein ausgeschaltetes Haekchen in irgendeinem Commit auftauchen. Beide sind gegen den Stand vor dem Fix in fuenf von fuenf Laeufen rot, danach gruen. Kein retry, kein hoeheres Zeitlimit: die Ursache war nie blosse Zeit, sondern ein falscher Zustand, den es jetzt nicht mehr gibt. Test 1 bleibt unveraendert in der Sache und prueft weiterhin dieselbe Zusicherung. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
439 lines
19 KiB
TypeScript
439 lines
19 KiB
TypeScript
import { cleanup, render, screen, waitFor } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import de from '@/messages/de.json';
|
|
import { clearErrorBuffer, recordError } from '@/lib/error-buffer';
|
|
import { computeCaptureSize } from '@/lib/bug-report-api';
|
|
|
|
/**
|
|
* bug-report-button.test — NEU (quick-260914-m97, Fehler-melden-Knopf).
|
|
*
|
|
* Dreizehn Tests. Die Kernanforderung (Test 1): das Bild wird VOR dem
|
|
* Dialog aufgenommen — der `toPng`-Mock prueft waehrend seines Aufrufs,
|
|
* dass noch KEIN `role="dialog"` im DOM steht. `html-to-image` selbst
|
|
* laeuft in jsdom nicht (gemessen zur Planungszeit:
|
|
* `HTMLVideoElement is not defined`, kein Canvas-Backend) — deshalb der
|
|
* Mock; der Bildbeweis kommt aus dem Browser.
|
|
*
|
|
* Der next-intl-Mock liest die Texte aus der ECHTEN `de.json` (Muster
|
|
* `tessera-logo.test.tsx`), Erwartungen zitieren `de.bugReport.<key>`.
|
|
*
|
|
* Test 12/13 (quick-260918-gza): die vier Herkunftsfelder in der Nutzlast,
|
|
* abhaengig vom Cookie `tessera_desktop_client` — jsdom hat ohne gesetztes
|
|
* Cookie keinen Desktop-Client, darum bleibt Test 2 beim Browser-Fall.
|
|
*/
|
|
const { mockToPng, mockFetch, mockUser } = vi.hoisted(() => ({
|
|
mockToPng: vi.fn(),
|
|
mockFetch: vi.fn(),
|
|
mockUser: { id: 'u1', username: 'anna', displayName: 'Anna', role: 'USER', tenantId: 't1' } as {
|
|
id: string;
|
|
username: string;
|
|
displayName: string | null;
|
|
role: string;
|
|
tenantId: string;
|
|
},
|
|
}));
|
|
|
|
vi.mock('html-to-image', () => ({
|
|
toPng: (...args: unknown[]) => mockToPng(...args),
|
|
}));
|
|
|
|
vi.mock('next-intl', async () => {
|
|
const messages = (await import('@/messages/de.json')).default as Record<string, unknown>;
|
|
const lookup = (path: string): string | undefined =>
|
|
path.split('.').reduce<unknown>((o, k) => (o && typeof o === 'object' ? (o as any)[k] : undefined), messages) as
|
|
| string
|
|
| undefined;
|
|
return {
|
|
useTranslations: (ns?: string) => (key: string) => lookup(ns ? `${ns}.${key}` : key) ?? key,
|
|
};
|
|
});
|
|
|
|
vi.mock('next/link', () => ({
|
|
default: ({ children, href, className }: { children: React.ReactNode; href: string; className?: string }) => (
|
|
<a href={href} className={className}>
|
|
{children}
|
|
</a>
|
|
),
|
|
}));
|
|
|
|
vi.mock('@/lib/stores/auth-store', () => ({
|
|
useAuthStore: (sel?: (s: { user: typeof mockUser }) => unknown) =>
|
|
sel ? sel({ user: mockUser }) : { user: mockUser },
|
|
}));
|
|
|
|
vi.mock('@/lib/app-version', () => ({
|
|
appVersion: { version: 'v1.2.3', channel: 'beta', commit: 'abc1234' },
|
|
}));
|
|
|
|
const T = de.bugReport;
|
|
const DATA_URL = 'data:image/png;base64,iVBORw0KGgo=';
|
|
|
|
async function renderButton() {
|
|
const { BugReportButton } = await import('./bug-report-button');
|
|
render(<BugReportButton />);
|
|
}
|
|
|
|
async function openDialog(user: ReturnType<typeof userEvent.setup>) {
|
|
await user.click(screen.getByRole('button', { name: T.button }));
|
|
return screen.findByRole('dialog');
|
|
}
|
|
|
|
function jsonResponse(status: number, body: unknown = { statusCode: status }) {
|
|
return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' } });
|
|
}
|
|
|
|
beforeEach(() => {
|
|
mockUser.role = 'USER';
|
|
mockToPng.mockReset();
|
|
mockToPng.mockResolvedValue(DATA_URL);
|
|
mockFetch.mockReset();
|
|
vi.stubGlobal('fetch', mockFetch);
|
|
});
|
|
|
|
/**
|
|
* Groesse von `document.body` vortaeuschen. WICHTIG: `Object.defineProperty`
|
|
* legt eine eigene Eigenschaft auf dem Element an, die den Getter von
|
|
* `Element.prototype` verdeckt — `document.body` ueberlebt `cleanup()` und
|
|
* damit die ganze Datei. Ohne Ruecknahme saehen alle folgenden Tests 3200x1000.
|
|
* Deshalb merken und in `afterEach` wieder loeschen.
|
|
*/
|
|
let bodyGroesseVorgetaeuscht = false;
|
|
function stubBodyGroesse(scrollWidth: number, scrollHeight: number) {
|
|
Object.defineProperty(document.body, 'scrollWidth', { value: scrollWidth, configurable: true });
|
|
Object.defineProperty(document.body, 'scrollHeight', { value: scrollHeight, configurable: true });
|
|
bodyGroesseVorgetaeuscht = true;
|
|
}
|
|
|
|
function restoreBodyGroesse() {
|
|
if (!bodyGroesseVorgetaeuscht) return;
|
|
Reflect.deleteProperty(document.body, 'scrollWidth');
|
|
Reflect.deleteProperty(document.body, 'scrollHeight');
|
|
bodyGroesseVorgetaeuscht = false;
|
|
}
|
|
|
|
function clearDesktopCookies() {
|
|
document.cookie = 'tessera_desktop=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
|
|
document.cookie = 'tessera_desktop_client=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
|
|
}
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
clearErrorBuffer();
|
|
clearDesktopCookies();
|
|
restoreBodyGroesse();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe('BugReportButton (quick-260914-m97)', () => {
|
|
it('Test 1: Bild VOR dem Dialog — toPng laeuft ohne offenen Dialog, mit document.body, pixelRatio 1, skipFonts, 1600-px-Kante; danach Dialog mit Vorschau, Haekchen an, Textfeld leer', async () => {
|
|
stubBodyGroesse(3200, 1000);
|
|
// Kein `expect` INNERHALB der Attrappe: ein Fehler daraus landet mitten im
|
|
// `await` der Komponente und wird von `captureScreenshot` verschluckt
|
|
// (`catch` liefert `null`) — der Test waere dann an ganz anderer Stelle
|
|
// mit irrefuehrender Meldung durchgefallen. Also nur festhalten, unten
|
|
// pruefen (quick-260921-ldf).
|
|
let dialogWaehrendDerAufnahme: unknown = 'toPng wurde nicht aufgerufen';
|
|
mockToPng.mockImplementation(async () => {
|
|
dialogWaehrendDerAufnahme = screen.queryByRole('dialog');
|
|
return DATA_URL;
|
|
});
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
|
|
const dialog = await openDialog(user);
|
|
|
|
expect(mockToPng).toHaveBeenCalledTimes(1);
|
|
expect(dialogWaehrendDerAufnahme).toBeNull();
|
|
const [node, opts] = mockToPng.mock.calls[0] as [unknown, Record<string, unknown>];
|
|
expect(node).toBe(document.body);
|
|
expect(opts).toMatchObject({ pixelRatio: 1, skipFonts: true, canvasWidth: 1600, canvasHeight: 500 });
|
|
expect(dialog).toBeInTheDocument();
|
|
const img = screen.getByAltText(T.screenshotAlt) as HTMLImageElement;
|
|
expect(img.getAttribute('src')).toBe(DATA_URL);
|
|
expect(screen.getByRole('checkbox', { name: T.attachScreenshot })).toBeChecked();
|
|
expect(screen.getByLabelText(T.descriptionLabel)).toHaveValue('');
|
|
});
|
|
|
|
it('Test 2: Senden mit Bild — multipart POST /bug-reports mit credentials, allen Feldern, Fehlerliste, PNG-Blob, ohne Content-Type-Header; danach Dankestext und Schliessen-Knopf', async () => {
|
|
recordError('fetch', 'GET /modules -> 500');
|
|
mockFetch.mockResolvedValue(jsonResponse(200, { sent: true }));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.type(screen.getByLabelText(T.descriptionLabel), 'Knopf tut nichts');
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
|
|
await screen.findByText(T.sent);
|
|
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
const [url, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
expect(String(url).endsWith('/bug-reports')).toBe(true);
|
|
expect(init.method).toBe('POST');
|
|
expect(init.credentials).toBe('include');
|
|
expect(init.body).toBeInstanceOf(FormData);
|
|
const body = init.body as FormData;
|
|
expect(body.get('description')).toBe('Knopf tut nichts');
|
|
expect(body.get('webVersion')).toBe('v1.2.3');
|
|
expect(body.get('webChannel')).toBe('beta');
|
|
expect(body.get('webCommit')).toBe('abc1234');
|
|
expect(String(body.get('page')).startsWith('/')).toBe(true);
|
|
expect(body.get('userAgent')).toBeTruthy();
|
|
expect(body.get('viewport')).toMatch(/^\d+x\d+$/);
|
|
expect(body.get('clientTime')).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
expect(body.get('clientKind')).toBe('browser');
|
|
expect(body.get('clientOs')).toBe('');
|
|
expect(body.get('clientVersion')).toBe('');
|
|
expect(body.get('clientCommit')).toBe('');
|
|
expect(body.getAll('errors').some((e) => String(e).includes('GET /modules -> 500'))).toBe(true);
|
|
const shot = body.get('screenshot');
|
|
expect(shot).toBeInstanceOf(Blob);
|
|
expect((shot as Blob).type).toBe('image/png');
|
|
expect((shot as Blob).size).toBeGreaterThan(0);
|
|
const headers = (init.headers ?? {}) as Record<string, string>;
|
|
expect(Object.keys(headers).map((k) => k.toLowerCase())).not.toContain('content-type');
|
|
expect(screen.getByRole('button', { name: T.close })).toBeInTheDocument();
|
|
});
|
|
|
|
it('Test 3: Haekchen aus -> kein Feld screenshot im Rumpf', async () => {
|
|
mockFetch.mockResolvedValue(jsonResponse(200, { sent: true }));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.click(screen.getByRole('checkbox', { name: T.attachScreenshot }));
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
|
|
await screen.findByText(T.sent);
|
|
const body = (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as FormData;
|
|
expect(body.has('screenshot')).toBe(false);
|
|
});
|
|
|
|
it('Test 4: 409 — Meldung "kein Postfach"; als ADMIN zusaetzlich Hinweis mit Link auf /admin/smtp, als USER kein Link', async () => {
|
|
mockUser.role = 'ADMIN';
|
|
mockFetch.mockResolvedValue(jsonResponse(409, { statusCode: 409, message: 'kein Postfach' }));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
|
|
await screen.findByText(T.errorNotConfigured);
|
|
expect(screen.getByText(T.errorNotConfiguredAdminHint)).toBeInTheDocument();
|
|
const link = screen.getByRole('link', { name: T.errorNotConfiguredAdminLink });
|
|
expect(link.getAttribute('href')).toBe('/admin/smtp');
|
|
|
|
cleanup();
|
|
mockUser.role = 'USER';
|
|
await renderButton();
|
|
await openDialog(user);
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
|
|
await screen.findByText(T.errorNotConfigured);
|
|
expect(screen.queryByRole('link', { name: T.errorNotConfiguredAdminLink })).toBeNull();
|
|
expect(screen.queryByText(T.errorNotConfiguredAdminHint)).toBeNull();
|
|
});
|
|
|
|
it('Test 5: Escape schliesst den Dialog', async () => {
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.keyboard('{Escape}');
|
|
|
|
await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
|
|
});
|
|
|
|
it('Test 6: Aufnahme scheitert -> Dialog oeffnet trotzdem mit Hinweis, Haekchen abgeschaltet und aus, Senden ohne screenshot', async () => {
|
|
mockToPng.mockRejectedValue(new Error('kein Canvas'));
|
|
mockFetch.mockResolvedValue(jsonResponse(200, { sent: true }));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
expect(screen.getByText(T.screenshotUnavailable)).toBeInTheDocument();
|
|
const box = screen.getByRole('checkbox', { name: T.attachScreenshot });
|
|
expect(box).toBeDisabled();
|
|
expect(box).not.toBeChecked();
|
|
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
await screen.findByText(T.sent);
|
|
const body = (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as FormData;
|
|
expect(body.has('screenshot')).toBe(false);
|
|
});
|
|
|
|
it('Test 7: 413 -> errorTooLarge; Knoepfe bleiben, erneutes Senden ruft fetch ein zweites Mal', async () => {
|
|
mockFetch.mockResolvedValue(jsonResponse(413));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
await screen.findByText(T.errorTooLarge);
|
|
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
await waitFor(() => expect(mockFetch).toHaveBeenCalledTimes(2));
|
|
});
|
|
|
|
it('Test 8: 429 -> errorTooMany, nicht errorTooLarge', async () => {
|
|
mockFetch.mockResolvedValue(jsonResponse(429));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
|
|
await screen.findByText(T.errorTooMany);
|
|
expect(screen.queryByText(T.errorTooLarge)).toBeNull();
|
|
});
|
|
|
|
it('Test 9: 502 -> errorSendFailed', async () => {
|
|
mockFetch.mockResolvedValue(jsonResponse(502));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
|
|
await screen.findByText(T.errorSendFailed);
|
|
});
|
|
|
|
it('Test 10: 500 und Netzwerkfehler -> errorGeneric, keine der vier spezifischen Meldungen', async () => {
|
|
mockFetch.mockResolvedValueOnce(jsonResponse(500));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
await screen.findByText(T.errorGeneric);
|
|
|
|
mockFetch.mockRejectedValueOnce(new Error('netz'));
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
await waitFor(() => expect(mockFetch).toHaveBeenCalledTimes(2));
|
|
await screen.findByText(T.errorGeneric);
|
|
for (const specific of [T.errorNotConfigured, T.errorTooMany, T.errorTooLarge, T.errorSendFailed]) {
|
|
expect(screen.queryByText(specific)).toBeNull();
|
|
}
|
|
});
|
|
|
|
it('Test 12 (quick-260918-gza): Desktop-Cookies gesetzt -> clientKind desktop mit Os/Version/Commit aus dem Cookie', async () => {
|
|
document.cookie = 'tessera_desktop=1; path=/';
|
|
document.cookie = 'tessera_desktop_client=1.2.0%7Ca6d1a64%7Cwindows; path=/';
|
|
mockFetch.mockResolvedValue(jsonResponse(200, { sent: true }));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
await screen.findByText(T.sent);
|
|
|
|
const body = (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as FormData;
|
|
expect(body.get('clientKind')).toBe('desktop');
|
|
expect(body.get('clientOs')).toBe('windows');
|
|
expect(body.get('clientVersion')).toBe('1.2.0');
|
|
expect(body.get('clientCommit')).toBe('a6d1a64');
|
|
});
|
|
|
|
it('Test 13 (quick-260918-gza, alter Client): nur tessera_desktop ohne tessera_desktop_client -> desktop ohne Details', async () => {
|
|
document.cookie = 'tessera_desktop=1; path=/';
|
|
mockFetch.mockResolvedValue(jsonResponse(200, { sent: true }));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
await screen.findByText(T.sent);
|
|
|
|
const body = (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as FormData;
|
|
expect(body.get('clientKind')).toBe('desktop');
|
|
expect(body.get('clientOs')).toBe('');
|
|
expect(body.get('clientVersion')).toBe('');
|
|
expect(body.get('clientCommit')).toBe('');
|
|
});
|
|
|
|
/**
|
|
* Test 14/15 (quick-260921-ldf) sind der Nachweis fuer die Ursache des
|
|
* Wackeltests aus CI-Lauf 395. Der Dialog zog seinen Anfangszustand frueher
|
|
* per `useEffect` nach; React schrieb deshalb bei JEDEM Oeffnen zuerst einen
|
|
* falschen DOM-Zustand fest und korrigierte ihn erst einen Commit spaeter.
|
|
* Gemessen hielt der falsche Zustand zwei volle Makrotask-Runden — lange
|
|
* genug, dass der Browser ihn zeichnen konnte und der Test ihn gelegentlich
|
|
* sah. Die beiden Tests beobachten jeden Commit und dulden keinen einzigen
|
|
* falschen.
|
|
*/
|
|
function protokolliereCommits(): { commits: string[]; stop: () => void } {
|
|
const commits: string[] = [];
|
|
const obs = new MutationObserver(() => {
|
|
const dialog = document.querySelector('[role="dialog"]');
|
|
if (!dialog) return;
|
|
const bild = dialog.querySelector('img');
|
|
const box = document.getElementById('bug-report-attach') as HTMLInputElement | null;
|
|
const feld = document.getElementById('bug-report-description') as HTMLTextAreaElement | null;
|
|
const danke = dialog.textContent?.includes(T.sent) ?? false;
|
|
commits.push(
|
|
`bild=${bild ? 'ja' : 'nein'} haekchen=${box ? (box.checked ? 'an' : 'aus') : '-'} ` +
|
|
`gesperrt=${box ? box.disabled : '-'} text="${feld?.value ?? ''}" danke=${danke}`,
|
|
);
|
|
});
|
|
obs.observe(document.body, { childList: true, subtree: true, attributes: true });
|
|
return { commits, stop: () => obs.disconnect() };
|
|
}
|
|
|
|
it('Test 14 (quick-260921-ldf): kein falscher Zwischenzustand — in KEINEM Commit steht das Vorschaubild mit ausgeschaltetem Haekchen', async () => {
|
|
const { commits, stop } = protokolliereCommits();
|
|
try {
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
await openDialog(user);
|
|
} finally {
|
|
stop();
|
|
}
|
|
|
|
expect(commits.length).toBeGreaterThan(0);
|
|
const falsche = commits.filter((c) => c.includes('bild=ja') && c.includes('haekchen=aus') && c.includes('gesperrt=false'));
|
|
expect(falsche).toEqual([]);
|
|
// Und der allererste Commit mit Dialog ist schon der richtige.
|
|
expect(commits[0]).toContain('bild=ja');
|
|
expect(commits[0]).toContain('haekchen=an');
|
|
});
|
|
|
|
it('Test 15 (quick-260921-ldf): erneutes Oeffnen startet frisch — kein alter Dankestext, kein alter Text, Haekchen wieder an', async () => {
|
|
mockFetch.mockResolvedValue(jsonResponse(200, { sent: true }));
|
|
const user = userEvent.setup();
|
|
await renderButton();
|
|
|
|
// Erstes Oeffnen: Haekchen abwaehlen, Text tippen, senden, schliessen.
|
|
await openDialog(user);
|
|
await user.click(screen.getByRole('checkbox', { name: T.attachScreenshot }));
|
|
await user.type(screen.getByLabelText(T.descriptionLabel), 'alter Text');
|
|
await user.click(screen.getByRole('button', { name: T.send }));
|
|
await screen.findByText(T.sent);
|
|
await user.click(screen.getByRole('button', { name: T.close }));
|
|
await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
|
|
|
|
// Zweites Oeffnen: jeder Commit muss frisch sein.
|
|
const { commits, stop } = protokolliereCommits();
|
|
try {
|
|
await openDialog(user);
|
|
} finally {
|
|
stop();
|
|
}
|
|
|
|
expect(commits.length).toBeGreaterThan(0);
|
|
expect(commits.filter((c) => c.includes('danke=true'))).toEqual([]);
|
|
expect(commits.filter((c) => c.includes('text="alter Text"'))).toEqual([]);
|
|
expect(commits.filter((c) => c.includes('bild=ja') && c.includes('haekchen=aus') && c.includes('gesperrt=false'))).toEqual([]);
|
|
expect(screen.getByRole('checkbox', { name: T.attachScreenshot })).toBeChecked();
|
|
expect(screen.getByLabelText(T.descriptionLabel)).toHaveValue('');
|
|
});
|
|
});
|
|
|
|
describe('computeCaptureSize (quick-260914-m97, reine Funktion)', () => {
|
|
it('Test 11: laengste Kante hoechstens 1600 px, Seitenverhaeltnis bleibt, Mindestmass 1x1, maxEdge einstellbar', () => {
|
|
expect(computeCaptureSize(3200, 1000)).toEqual({ width: 1600, height: 500 });
|
|
expect(computeCaptureSize(800, 600)).toEqual({ width: 800, height: 600 });
|
|
expect(computeCaptureSize(1000, 4000)).toEqual({ width: 400, height: 1600 });
|
|
expect(computeCaptureSize(0, 0)).toEqual({ width: 1, height: 1 });
|
|
expect(computeCaptureSize(3200, 1000, 800)).toEqual({ width: 800, height: 250 });
|
|
});
|
|
});
|