Release 0.5.10
This commit is contained in:
1 parent
c3033479b5
commit
4fe61971e4
52 files changed
+38565
-466
No files matched your search
+104
-40
@@ -1,4 +1,4 @@
|
||||
import { expect, test, type Page } from '@playwright/test';
|
||||
import { expect, test, type Locator, type Page } from '@playwright/test';
|
||||
import Database from 'better-sqlite3';
|
||||
import { readFileSync, readdirSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
@@ -40,6 +40,12 @@ async function openEditorialField(page: Page, heading: string) {
|
||||
}
|
||||
}
|
||||
|
||||
async function editInlineTitle(scope: Page | Locator, title: string, value: string) {
|
||||
await scope.getByRole('button', { name: `${title} Titel bearbeiten` }).click();
|
||||
await scope.getByLabel('Titel', { exact: true }).fill(value);
|
||||
await scope.getByLabel('Titel', { exact: true }).press('Enter');
|
||||
}
|
||||
|
||||
async function login(page: Page, email = adminEmail, password = adminPassword) {
|
||||
await page.goto('/login');
|
||||
await page.getByLabel('E-Mail').fill(email);
|
||||
@@ -413,8 +419,9 @@ test('sent and planned source filters stay independent and removing the last usa
|
||||
});
|
||||
|
||||
await page.goto('/sendungen');
|
||||
await page.getByText('Neue Sendung planen', { exact: true }).click();
|
||||
await page.getByLabel('Titel', { exact: true }).fill('E2E Filterprüfung');
|
||||
await page.getByLabel('Ausgangspunkt').selectOption('template-default');
|
||||
await page.getByRole('radio', { name: /Standardablauf/ }).check();
|
||||
await page.getByRole('button', { name: 'Sendung anlegen' }).click();
|
||||
await page.getByPlaceholder('Fundus durchsuchen').fill('kleine Konsole');
|
||||
await page.getByRole('button', { name: 'Filtern' }).click();
|
||||
@@ -564,8 +571,9 @@ test('a show can be planned from a template with independent usages and reserve'
|
||||
test.slow();
|
||||
await page.setViewportSize({ width: 1440, height: 1000 });
|
||||
await page.goto('/sendungen');
|
||||
await page.getByText('Neue Sendung planen', { exact: true }).click();
|
||||
await page.getByLabel('Titel', { exact: true }).fill('E2E Planung Spiele und JC');
|
||||
await page.getByLabel('Ausgangspunkt').selectOption('template-default');
|
||||
await page.getByRole('radio', { name: /Standardablauf/ }).check();
|
||||
await page.getByRole('button', { name: 'Sendung anlegen' }).click();
|
||||
await expect(page).toHaveURL(/\/sendungen\/[a-f0-9-]+$/);
|
||||
const showUrl = page.url();
|
||||
@@ -647,7 +655,11 @@ test('a show can be planned from a template with independent usages and reserve'
|
||||
name: 'Die kleine Konsole mit dem großen Dreh bearbeiten und verschieben'
|
||||
})
|
||||
.click();
|
||||
await usages.nth(0).getByLabel('Titel', { exact: true }).fill('Konsole als Auftakt');
|
||||
await editInlineTitle(
|
||||
usages.nth(0),
|
||||
'Die kleine Konsole mit dem großen Dreh',
|
||||
'Konsole als Auftakt'
|
||||
);
|
||||
await usages.nth(0).locator('.piece-meta-details > summary').click();
|
||||
await usages.nth(0).getByLabel('Dauer in Minuten').fill('5');
|
||||
await usages.nth(0).getByRole('button', { name: 'Recherche & Quellen' }).click();
|
||||
@@ -668,7 +680,11 @@ test('a show can be planned from a template with independent usages and reserve'
|
||||
name: 'Die kleine Konsole mit dem großen Dreh bearbeiten und verschieben'
|
||||
})
|
||||
.click();
|
||||
await usages.nth(1).getByLabel('Titel', { exact: true }).fill('Konsole als Rückblick');
|
||||
await editInlineTitle(
|
||||
usages.nth(1),
|
||||
'Die kleine Konsole mit dem großen Dreh',
|
||||
'Konsole als Rückblick'
|
||||
);
|
||||
await usages.nth(1).locator('.piece-meta-details > summary').click();
|
||||
await usages.nth(1).getByLabel('Dauer in Minuten').fill('15');
|
||||
await usages.nth(1).getByRole('button', { name: 'Recherche & Quellen' }).click();
|
||||
@@ -879,34 +895,56 @@ test('inbox, archive and administration stay concise', async ({ page }) => {
|
||||
|
||||
test('tools stay editable in pieces after catalog deactivation', async ({ page }, testInfo) => {
|
||||
await page.goto('/verwaltung');
|
||||
const create = page.locator('.new-tool');
|
||||
await create.getByText('Werkzeug hinzufügen').click();
|
||||
await page.getByRole('button', { name: 'Werkzeug hinzufügen' }).click();
|
||||
const create = page.getByRole('dialog', { name: 'Neues Werkzeug' });
|
||||
await create.getByLabel('Name').fill('E2E Tierlist');
|
||||
await create.getByLabel('Start-URL').fill('https://example.com/tierlist');
|
||||
await create.getByLabel('Beschreibung').fill('Ranking für die Sendung');
|
||||
await create.getByLabel('Quiz').check();
|
||||
await create.getByText('Icon auswählen', { exact: true }).click();
|
||||
await create.getByPlaceholder('Icon suchen').fill('Satellite Dish');
|
||||
await page.screenshot({ path: testInfo.outputPath('tool-icon-picker-desktop.png') });
|
||||
await create.getByRole('button', { name: 'Satellite Dish', exact: true }).click();
|
||||
await create.getByText('Formate auswählen', { exact: true }).click();
|
||||
await create.getByRole('checkbox', { name: 'Quiz' }).check();
|
||||
await create.getByRole('button', { name: 'Auswahl schließen' }).click();
|
||||
await page.screenshot({ path: testInfo.outputPath('tool-create-desktop.png'), fullPage: true });
|
||||
await create.getByRole('button', { name: 'Werkzeug anlegen' }).click();
|
||||
const tool = page.locator('.tool-settings-list details').filter({ hasText: 'E2E Tierlist' });
|
||||
const tool = page.locator('.tool-settings-list > button').filter({ hasText: 'E2E Tierlist' });
|
||||
await expect(tool).toContainText('Aktiv');
|
||||
await expect(tool.locator('.catalog-entry-icon svg')).toBeVisible();
|
||||
await page.screenshot({ path: testInfo.outputPath('tool-catalog-desktop.png'), fullPage: true });
|
||||
|
||||
await page.goto('/fundus/piece-quiz');
|
||||
await page.getByRole('button', { name: 'Recherche & Quellen' }).click();
|
||||
await page
|
||||
.locator('#piece-tool-picker')
|
||||
.selectOption({ label: 'E2E Tierlist · passt zum Format' });
|
||||
await page.getByRole('button', { name: 'Hinzufügen', exact: true }).click();
|
||||
await page.getByText('Werkzeuge auswählen', { exact: true }).click();
|
||||
const toolChoice = page.getByRole('checkbox', { name: /E2E Tierlist/ });
|
||||
await expect(toolChoice.locator('xpath=..').locator('svg')).toBeVisible();
|
||||
await toolChoice.check();
|
||||
await page.getByLabel('Arbeitslink').fill('https://example.com/tierlist/e2e-board');
|
||||
await page.getByLabel('Notiz').fill('Vor der Sendung sortieren');
|
||||
await expect(page.locator('.save-indicator')).toContainText('Gespeichert', { timeout: 10_000 });
|
||||
const toolsSection = page.locator('.piece-source-editor').filter({ hasText: 'Werkzeuge' });
|
||||
await toolsSection.scrollIntoViewIfNeeded();
|
||||
await page.screenshot({ path: testInfo.outputPath('piece-tools-desktop.png') });
|
||||
|
||||
await page.goto('/verwaltung');
|
||||
const storedTool = page
|
||||
.locator('.tool-settings-list details')
|
||||
.locator('.tool-settings-list > button')
|
||||
.filter({ hasText: 'E2E Tierlist' });
|
||||
await storedTool.getByText('E2E Tierlist', { exact: true }).click();
|
||||
await storedTool.getByLabel('Aktiv', { exact: true }).uncheck();
|
||||
await storedTool.getByRole('button', { name: 'Speichern' }).click();
|
||||
await storedTool.click();
|
||||
const edit = page.getByRole('dialog', { name: 'E2E Tierlist' });
|
||||
await edit.getByRole('checkbox', { name: /^Aktiv/ }).uncheck();
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await expectNoHorizontalOverflow(page);
|
||||
await expect(edit.getByRole('button', { name: 'Änderungen speichern' })).toBeVisible();
|
||||
await edit.getByText('Icon auswählen', { exact: true }).click();
|
||||
await edit.getByPlaceholder('Icon suchen').fill('Satellite Dish');
|
||||
await page.screenshot({ path: testInfo.outputPath('tool-icon-picker-mobile.png') });
|
||||
await edit.getByRole('button', { name: 'Satellite Dish', exact: true }).click();
|
||||
await page.screenshot({ path: testInfo.outputPath('tool-edit-mobile.png') });
|
||||
await edit.getByRole('button', { name: 'Änderungen speichern' }).click();
|
||||
await expect(storedTool).toContainText('Deaktiviert');
|
||||
await page.screenshot({ path: testInfo.outputPath('tool-catalog-mobile.png'), fullPage: true });
|
||||
|
||||
await page.goto('/fundus/piece-quiz');
|
||||
await page.getByRole('button', { name: 'Recherche & Quellen' }).click();
|
||||
@@ -916,8 +954,12 @@ test('tools stay editable in pieces after catalog deactivation', async ({ page }
|
||||
'https://example.com/tierlist/e2e-board'
|
||||
);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page
|
||||
.locator('.piece-source-editor')
|
||||
.filter({ hasText: 'Werkzeuge' })
|
||||
.scrollIntoViewIfNeeded();
|
||||
await expectNoHorizontalOverflow(page);
|
||||
await page.screenshot({ path: testInfo.outputPath('piece-tools-mobile.png'), fullPage: true });
|
||||
await page.screenshot({ path: testInfo.outputPath('piece-tools-mobile.png') });
|
||||
});
|
||||
|
||||
test('validated YouTube and TikTok players switch without exposing private fields', async ({
|
||||
@@ -941,7 +983,8 @@ test('validated YouTube and TikTok players switch without exposing private field
|
||||
const popupPromise = page.waitForEvent('popup');
|
||||
await page.getByRole('button', { name: 'Ausgabe verbinden und öffnen' }).click();
|
||||
const output = await popupPromise;
|
||||
await expect(output.getByText('Bereit für den nächsten Inhalt')).toBeVisible();
|
||||
await expect(output.locator('.output-content')).toHaveCount(0);
|
||||
await expect(output.locator('.heinzlive-output')).toHaveClass(/empty/);
|
||||
|
||||
await page.getByRole('button', { name: 'YouTube Kurzlink präsentieren' }).click();
|
||||
const youtube = output.locator('iframe[title="YouTube Vorschau"]');
|
||||
@@ -949,6 +992,9 @@ test('validated YouTube and TikTok players switch without exposing private field
|
||||
'src',
|
||||
'https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ?start=75'
|
||||
);
|
||||
await page.getByRole('button', { name: 'Präsentation leeren' }).click();
|
||||
await expect(youtube).toHaveCount(0);
|
||||
await expect(output.locator('.heinzlive-output')).toHaveClass(/empty/);
|
||||
await expect(output.locator('.output-connection')).toHaveClass(/sr-only/);
|
||||
const publicState = await output.evaluate(async () =>
|
||||
JSON.stringify(await fetch(`${location.pathname}/state`).then((response) => response.json()))
|
||||
@@ -986,7 +1032,7 @@ test('live control persists state and exposes only explicit presentation data',
|
||||
await page.setViewportSize({ width: 1280, height: 900 });
|
||||
await page.goto('/fundus?q=kleine+Konsole&includePlanned=1');
|
||||
await page.getByRole('heading', { name: 'Die kleine Konsole mit dem großen Dreh' }).click();
|
||||
await page.getByRole('button', { name: 'Recherche & Quellen' }).click();
|
||||
await page.getByRole('button', { name: 'Präsentation', exact: true }).click();
|
||||
const png = Buffer.from(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=',
|
||||
'base64'
|
||||
@@ -998,6 +1044,13 @@ test('live control persists state and exposes only explicit presentation data',
|
||||
});
|
||||
await page.getByLabel('Alternativtext').fill('Kleine Konsole im Live-Bild');
|
||||
await page.getByRole('button', { name: 'Bild hochladen' }).click();
|
||||
await page.goto('/sendungen/show-october');
|
||||
const plannedConsole = page
|
||||
.locator('.editable-usage')
|
||||
.filter({ hasText: 'Die kleine Konsole mit dem großen Dreh' })
|
||||
.first();
|
||||
await plannedConsole.getByRole('button', { name: /bearbeiten und verschieben/ }).click();
|
||||
await plannedConsole.getByRole('button', { name: 'Aus Fundus aktualisieren' }).click();
|
||||
|
||||
await page.goto('/live?show=show-october');
|
||||
await expect(page.getByText('Noch nicht gestartet.')).toBeVisible();
|
||||
@@ -1030,9 +1083,8 @@ test('live control persists state and exposes only explicit presentation data',
|
||||
const anonymous = await browser.newContext();
|
||||
const output = await anonymous.newPage();
|
||||
await output.goto(outputUrl);
|
||||
await expect(
|
||||
output.getByRole('heading', { name: 'Bereit für den nächsten Inhalt' })
|
||||
).toBeVisible();
|
||||
await expect(output.locator('.output-content')).toHaveCount(0);
|
||||
await expect(output.locator('.heinzlive-output')).toHaveClass(/empty/);
|
||||
|
||||
await page.getByRole('button', { name: 'Präsentieren', exact: true }).click();
|
||||
await expect(
|
||||
@@ -1040,9 +1092,9 @@ test('live control persists state and exposes only explicit presentation data',
|
||||
).toBeVisible({
|
||||
timeout: 10_000
|
||||
});
|
||||
await expect(output.getByText('Weniger Leistung, mehr Idee.')).toBeVisible();
|
||||
await expect(output.getByRole('heading', { name: 'Weniger Leistung, mehr Idee' })).toBeVisible();
|
||||
await expect(output.locator('.output-topics')).toHaveText('Gaming');
|
||||
await expect(output.getByAltText('Kleine Konsole im Live-Bild')).toHaveCount(0);
|
||||
await expect(output.getByAltText('Kleine Konsole im Live-Bild')).toBeVisible();
|
||||
await output.screenshot({ path: testInfo.outputPath('heinzlive-output.png'), fullPage: true });
|
||||
const outputHtml = await output.content();
|
||||
expect(outputHtml).not.toContain('Herstellerangaben');
|
||||
@@ -1060,6 +1112,10 @@ test('live control persists state and exposes only explicit presentation data',
|
||||
expect(outputJson).not.toContain('Herstellerangaben');
|
||||
expect(outputJson).not.toContain('Moderationsstichpunkte');
|
||||
expect(outputJson).not.toContain('live-konsole.png');
|
||||
await page.getByRole('button', { name: 'Präsentation leeren' }).click();
|
||||
await expect(output.locator('.output-content')).toHaveCount(0);
|
||||
await expect(output.locator('.heinzlive-output')).toHaveClass(/empty/);
|
||||
await page.getByRole('button', { name: 'Präsentieren', exact: true }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Als Nächstes' }).click();
|
||||
await expect(page.locator('.next-card')).toContainText('Pixelart');
|
||||
@@ -1105,7 +1161,7 @@ test('pieces autosave safely, detect conflicts and protect uploaded images', asy
|
||||
await expect(page).toHaveURL(/\/fundus\/[a-f0-9-]+$/);
|
||||
const pieceUrl = page.url();
|
||||
|
||||
await page.getByLabel('Titel', { exact: true }).fill('E2E Fundstück mit Markdown');
|
||||
await editInlineTitle(page, 'Neues Fundstück', 'E2E Fundstück mit Markdown');
|
||||
await expect(page.locator('.save-indicator')).toContainText('Gespeichert', { timeout: 10_000 });
|
||||
await page.getByRole('button', { name: 'Editor schließen' }).click();
|
||||
await expect(page).toHaveURL(/\/fundus$/);
|
||||
@@ -1117,6 +1173,7 @@ test('pieces autosave safely, detect conflicts and protect uploaded images', asy
|
||||
.fill('Persistiert nach Neuladen und ist gezielt suchbar.');
|
||||
await page.getByLabel('Status').selectOption('ready');
|
||||
await page.getByLabel('Dauer in Minuten').fill('10.25');
|
||||
await page.getByText('Themen auswählen', { exact: true }).click();
|
||||
await page.getByLabel('Gaming').check();
|
||||
await openEditorialField(page, 'Private Recherche');
|
||||
await page
|
||||
@@ -1147,7 +1204,9 @@ test('pieces autosave safely, detect conflicts and protect uploaded images', asy
|
||||
await expect(page.locator('.piece-presentation-preview script')).toHaveCount(0);
|
||||
await expect(page.locator('.piece-presentation-preview')).toContainText('Sichere Vorschau');
|
||||
await page.reload();
|
||||
await expect(page.getByLabel('Titel', { exact: true })).toHaveValue('E2E Fundstück mit Markdown');
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'E2E Fundstück mit Markdown Titel bearbeiten' })
|
||||
).toBeVisible();
|
||||
await page.getByText('Status, Dauer und Einordnung').click();
|
||||
await expect(page.getByLabel('Dauer in Minuten')).toHaveValue('10.25');
|
||||
await openEditorialField(page, 'Private Recherche');
|
||||
@@ -1159,6 +1218,7 @@ test('pieces autosave safely, detect conflicts and protect uploaded images', asy
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=',
|
||||
'base64'
|
||||
);
|
||||
await page.getByRole('button', { name: 'Präsentation', exact: true }).click();
|
||||
await page.getByLabel('Bild', { exact: true }).setInputFiles({
|
||||
name: 'redaktionsbild.png',
|
||||
mimeType: 'image/png',
|
||||
@@ -1179,7 +1239,7 @@ test('pieces autosave safely, detect conflicts and protect uploaded images', asy
|
||||
|
||||
const concurrent = await page.context().newPage();
|
||||
await concurrent.goto(pieceUrl);
|
||||
await page.getByLabel('Titel', { exact: true }).fill('E2E Fundstück, extern aktualisiert');
|
||||
await editInlineTitle(page, 'E2E Fundstück mit Markdown', 'E2E Fundstück, extern aktualisiert');
|
||||
await expect(page.locator('.save-indicator')).toContainText('Gespeichert', { timeout: 10_000 });
|
||||
await concurrent.getByText('Status, Dauer und Einordnung').click();
|
||||
await concurrent
|
||||
@@ -1189,9 +1249,9 @@ test('pieces autosave safely, detect conflicts and protect uploaded images', asy
|
||||
timeout: 10_000
|
||||
});
|
||||
await concurrent.reload();
|
||||
await expect(concurrent.getByLabel('Titel', { exact: true })).toHaveValue(
|
||||
'E2E Fundstück, extern aktualisiert'
|
||||
);
|
||||
await expect(
|
||||
concurrent.getByRole('button', { name: 'E2E Fundstück, extern aktualisiert Titel bearbeiten' })
|
||||
).toBeVisible();
|
||||
await concurrent.getByText('Status, Dauer und Einordnung').click();
|
||||
await expect(concurrent.getByLabel('Kurzbeschreibung')).not.toHaveValue(
|
||||
'Diese veraltete Änderung darf nicht gewinnen.'
|
||||
@@ -1214,12 +1274,18 @@ test('pieces autosave safely, detect conflicts and protect uploaded images', asy
|
||||
await expect(page.locator('.piece-card')).toHaveCount(1);
|
||||
await page.locator('.piece-card .piece-actions > summary').click();
|
||||
await page.locator('.piece-card').getByRole('button', { name: 'Duplizieren' }).click();
|
||||
await expect(page.getByLabel('Titel', { exact: true })).toHaveValue(
|
||||
'Kopie von E2E Fundstück, extern aktualisiert'
|
||||
);
|
||||
await expect(
|
||||
page.getByRole('button', {
|
||||
name: 'Kopie von E2E Fundstück, extern aktualisiert Titel bearbeiten'
|
||||
})
|
||||
).toBeVisible();
|
||||
|
||||
await page.context().setOffline(true);
|
||||
await page.getByLabel('Titel', { exact: true }).fill('Kopie mit unterbrochener Verbindung');
|
||||
await editInlineTitle(
|
||||
page,
|
||||
'Kopie von E2E Fundstück, extern aktualisiert',
|
||||
'Kopie mit unterbrochener Verbindung'
|
||||
);
|
||||
await expect(page.locator('.save-indicator')).toContainText('Speichern fehlgeschlagen', {
|
||||
timeout: 10_000
|
||||
});
|
||||
@@ -1298,15 +1364,13 @@ test('link preview uses one existing source, persists manual fields and isolates
|
||||
await page.getByRole('button', { name: 'Präsentation', exact: true }).click();
|
||||
await page.getByRole('radio', { name: 'Linkvorschau', exact: true }).check();
|
||||
await page.getByRole('combobox', { name: 'Quelle', exact: true }).selectOption('0');
|
||||
await page.getByLabel('Titel', { exact: true }).last().fill('Manuell korrigierte Linkvorschau');
|
||||
await page.getByLabel('Kartentitel').fill('Manuell korrigierte Linkvorschau');
|
||||
await page.getByLabel('Anreißer').fill('Ein kurzer manueller Anreißer ohne Vorschaubild.');
|
||||
await expect(page.locator('.save-indicator')).toContainText('Gespeichert', { timeout: 10_000 });
|
||||
await page.reload();
|
||||
await page.getByRole('button', { name: 'Präsentation', exact: true }).click();
|
||||
await expect(page.getByRole('radio', { name: 'Linkvorschau', exact: true })).toBeChecked();
|
||||
await expect(page.getByLabel('Titel', { exact: true }).last()).toHaveValue(
|
||||
'Manuell korrigierte Linkvorschau'
|
||||
);
|
||||
await expect(page.getByLabel('Kartentitel')).toHaveValue('Manuell korrigierte Linkvorschau');
|
||||
|
||||
const previewPromise = page.waitForEvent('popup');
|
||||
await page.getByRole('link', { name: 'In neuem Fenster testen' }).click();
|
||||
@@ -1317,7 +1381,7 @@ test('link preview uses one existing source, persists manual fields and isolates
|
||||
preview.getByRole('heading', { name: 'Manuell korrigierte Linkvorschau' })
|
||||
).toBeVisible();
|
||||
await expect(preview.getByText('Ein kurzer manueller Anreißer ohne Vorschaubild.')).toBeVisible();
|
||||
await expect(preview.locator('.output-link-image')).toHaveCount(0);
|
||||
await expect(preview.locator('.output-link-image')).toHaveCount(1);
|
||||
await preview.screenshot({
|
||||
path: testInfo.outputPath('linkvorschau-desktop.png'),
|
||||
fullPage: true
|
||||
|
||||
Reference in new issue
Block a user