Build compact live control view
This commit is contained in:
1 parent
3389ba17f4
commit
bd9ffe6faa
10 files changed
+1735
-787
No files matched your search
+193
-13
@@ -1003,6 +1003,7 @@ test('validated YouTube and TikTok players switch without exposing private field
|
||||
})
|
||||
);
|
||||
await page.goto('/live?show=e2e-presentation');
|
||||
await page.getByText('Menü', { exact: true }).click();
|
||||
const popupPromise = page.waitForEvent('popup');
|
||||
await page.getByRole('button', { name: 'Ausgabe verbinden und öffnen' }).click();
|
||||
const output = await popupPromise;
|
||||
@@ -1076,10 +1077,14 @@ test('live control persists state and exposes only explicit presentation data',
|
||||
await plannedConsole.getByRole('button', { name: 'Aus Fundus aktualisieren' }).click();
|
||||
|
||||
await page.goto('/live?show=show-october');
|
||||
await expect(page.getByText('Noch nicht gestartet.')).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: 'Bereit für die Sendung' })).toBeVisible();
|
||||
await expect(page.locator('.sidebar')).toHaveCount(0);
|
||||
await expect(page.locator('.mobile-header')).toHaveCount(0);
|
||||
await expect(page.locator('.mobile-navigation')).toHaveCount(0);
|
||||
await page.getByRole('button', { name: 'Sendung starten' }).click();
|
||||
await expect(page.locator('#current-title')).toHaveText('Die kleine Konsole mit dem großen Dreh');
|
||||
await expect(page.locator('.section-countdown')).toContainText(/Noch 0[45]:\d{2}/);
|
||||
await expect(page.locator('.section-countdown')).toContainText(/0[45]:\d{2}/);
|
||||
await page.getByText('Quellen · 1', { exact: true }).click();
|
||||
await expect(page.locator('.live-materials')).toContainText('Herstellerseite');
|
||||
await expect(
|
||||
page.getByText('Herstellerangaben gegen unabhängige Tests prüfen')
|
||||
@@ -1088,15 +1093,17 @@ test('live control persists state and exposes only explicit presentation data',
|
||||
const before = await page
|
||||
.locator('.live-current')
|
||||
.evaluate((element) => getComputedStyle(element).getPropertyValue('--prompt-size'));
|
||||
await page.getByText('Menü', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Schrift vergrößern' }).click();
|
||||
await page.reload();
|
||||
const restored = await page
|
||||
.locator('.live-current')
|
||||
.evaluate((element) => getComputedStyle(element).getPropertyValue('--prompt-size'));
|
||||
expect(restored).not.toBe(before);
|
||||
await page.getByText('Manuelle Recherche öffnen').click();
|
||||
await page.locator('.research-details > summary').click();
|
||||
await expect(page.locator('.research-details')).toHaveAttribute('open', '');
|
||||
|
||||
await page.getByText('Menü', { exact: true }).click();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.getByRole('button', { name: 'Ausgabe verbinden und öffnen' }).click()
|
||||
@@ -1140,28 +1147,201 @@ test('live control persists state and exposes only explicit presentation data',
|
||||
await expect(output.locator('.heinzlive-output')).toHaveClass(/empty/);
|
||||
await page.getByRole('button', { name: 'Präsentieren', exact: true }).click();
|
||||
|
||||
await page.getByText('Reserve', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Als Nächstes' }).click();
|
||||
await expect(page.locator('.next-card')).toContainText('Pixelart');
|
||||
await page.getByRole('button', { name: 'Erledigen und weiter' }).click();
|
||||
await expect(page.locator('.live-next-summary')).toContainText('Pixelart');
|
||||
await page.getByRole('button', { name: 'Erledigen & weiter' }).click();
|
||||
await expect(page.locator('.live-current')).toContainText('Pixelart');
|
||||
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||
await page.getByRole('button', { name: 'Zurück' }).click();
|
||||
await expect(page.locator('.live-current')).toContainText('Pixelart');
|
||||
await expect(page.locator('#current-title')).toHaveText(
|
||||
'Warum praktische Effekte wieder auffallen'
|
||||
);
|
||||
await expect(
|
||||
page.locator('.live-run-item').filter({ hasText: 'Die kleine Konsole mit dem großen Dreh' })
|
||||
).toContainText('Erledigt');
|
||||
await expect(
|
||||
page.locator('.live-run-item').filter({ hasText: 'Pixelart, die nicht nostalgisch sein will' })
|
||||
).toContainText('Übersprungen');
|
||||
await expect(
|
||||
page.locator('.live-run-item').filter({ hasText: 'Warum praktische Effekte wieder auffallen' })
|
||||
).toContainText('Aktuell');
|
||||
await page.locator('.live-current-scroll').evaluate((element) => {
|
||||
for (let index = 0; index < 12; index += 1) {
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.textContent = 'Temporärer Langtext für die Lesepositionsprüfung.';
|
||||
element.append(paragraph);
|
||||
}
|
||||
element.scrollTop = element.scrollHeight;
|
||||
element.dispatchEvent(new Event('scroll'));
|
||||
});
|
||||
expect(
|
||||
await page.locator('.live-current-scroll').evaluate((element) => element.scrollTop)
|
||||
).toBeGreaterThan(0);
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Die kleine Konsole mit dem großen Dreh als aktuelles Piece auswählen'
|
||||
})
|
||||
.click();
|
||||
await expect(page.locator('#current-title')).toHaveText('Die kleine Konsole mit dem großen Dreh');
|
||||
await expect
|
||||
.poll(() => page.locator('.live-current-scroll').evaluate((element) => element.scrollTop))
|
||||
.toBe(0);
|
||||
await expect(
|
||||
page.locator('.live-run-item').filter({ hasText: 'Warum praktische Effekte wieder auffallen' })
|
||||
).toContainText('Kommend');
|
||||
await expect(
|
||||
output.getByRole('heading', { name: 'Die kleine Konsole mit dem großen Dreh' })
|
||||
).toBeVisible();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Warum praktische Effekte wieder auffallen als aktuelles Piece auswählen'
|
||||
})
|
||||
.click();
|
||||
await expect(page.locator('#current-title')).toHaveText(
|
||||
'Warum praktische Effekte wieder auffallen'
|
||||
);
|
||||
await expect(output.getByText('Wenn das Licht wirklich auf ein Modell fällt.')).toHaveCount(0);
|
||||
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await setTheme(page, 'dark');
|
||||
await page.reload();
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
await expectNoHorizontalOverflow(page);
|
||||
await page.screenshot({ path: testInfo.outputPath('live-controller-desktop-dark.png') });
|
||||
|
||||
await page.setViewportSize({ width: 900, height: 450 });
|
||||
await setTheme(page, 'light');
|
||||
await page.reload();
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'light');
|
||||
await page.evaluate(() => {
|
||||
const title = document.querySelector('#current-title');
|
||||
if (title)
|
||||
title.textContent =
|
||||
'Warum praktische Effekte in einer außergewöhnlich langen Live-Moderation wieder auffallen';
|
||||
const copy = document.querySelector('.prompter-copy');
|
||||
if (copy) {
|
||||
for (let index = 0; index < 8; index += 1) {
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.textContent =
|
||||
'Zusätzlicher langer Moderationstext prüft die unabhängige Scrollfläche ohne die feste Bedienleiste zu verdrängen.';
|
||||
copy.append(paragraph);
|
||||
}
|
||||
}
|
||||
const rail = document.querySelector('.live-run-scroll');
|
||||
const section = rail?.querySelector('.rail-section');
|
||||
if (rail && section) {
|
||||
for (let index = 0; index < 8; index += 1) rail.append(section.cloneNode(true));
|
||||
}
|
||||
});
|
||||
const compactLayout = await page.evaluate(() => {
|
||||
const current = document.querySelector('.live-current')?.getBoundingClientRect();
|
||||
const rail = document.querySelector('.live-rail')?.getBoundingClientRect();
|
||||
const footer = document.querySelector('.live-action-bar')?.getBoundingClientRect();
|
||||
const pieceScroll = document.querySelector('.live-current-scroll');
|
||||
const railScroll = document.querySelector('.live-run-scroll');
|
||||
return {
|
||||
pageFits: document.documentElement.scrollHeight <= innerHeight,
|
||||
twoColumns: Boolean(current && rail && rail.x > current.x + current.width - 1),
|
||||
footerVisible: Boolean(footer && footer.bottom <= innerHeight && footer.top >= 0),
|
||||
pieceScrolls: Boolean(pieceScroll && pieceScroll.scrollHeight > pieceScroll.clientHeight),
|
||||
railScrolls: Boolean(railScroll && railScroll.scrollHeight > railScroll.clientHeight)
|
||||
};
|
||||
});
|
||||
expect(compactLayout).toEqual({
|
||||
pageFits: true,
|
||||
twoColumns: true,
|
||||
footerVisible: true,
|
||||
pieceScrolls: true,
|
||||
railScrolls: true
|
||||
});
|
||||
await page.screenshot({ path: testInfo.outputPath('live-controller-compact-desktop.png') });
|
||||
|
||||
const countdownBeforeReload = await page.locator('.section-countdown strong').textContent();
|
||||
await page.reload();
|
||||
await expect(page.locator('#current-title')).toHaveText(
|
||||
'Warum praktische Effekte wieder auffallen'
|
||||
);
|
||||
const countdownAfterReload = await page.locator('.section-countdown strong').textContent();
|
||||
expect(countdownAfterReload).not.toBe('05:00');
|
||||
expect(countdownAfterReload).toMatch(/0[34]:\d{2}/);
|
||||
expect(countdownBeforeReload).toMatch(/0[34]:\d{2}/);
|
||||
|
||||
await page.setViewportSize({ width: 390, height: 700 });
|
||||
const liveRail = page.locator('.live-rail');
|
||||
const railClose = liveRail.getByRole('button', { name: 'Ablauf schließen' });
|
||||
await expect(liveRail).toBeHidden();
|
||||
await expect(page.locator('.live-action-bar')).toBeVisible();
|
||||
await expectNoHorizontalOverflow(page);
|
||||
expect(
|
||||
await page.evaluate(() => document.documentElement.scrollHeight <= window.innerHeight)
|
||||
).toBe(true);
|
||||
await page.screenshot({ path: testInfo.outputPath('live-controller-mobile.png') });
|
||||
await page.getByRole('button', { name: /Ablauf · Piece/ }).click();
|
||||
await expect(liveRail).toHaveClass(/open/);
|
||||
await expect(railClose).toBeFocused();
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const box = await liveRail.boundingBox();
|
||||
return box ? Math.ceil(box.x + box.width) : Number.POSITIVE_INFINITY;
|
||||
})
|
||||
.toBeLessThanOrEqual(390);
|
||||
const mobileRailBox = await liveRail.boundingBox();
|
||||
expect(mobileRailBox?.width ?? 0).toBeGreaterThan(340);
|
||||
await expect(page.locator('.live-run-item.current')).toBeInViewport();
|
||||
await page.screenshot({ path: testInfo.outputPath('live-controller-mobile-run.png') });
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(liveRail).toBeHidden();
|
||||
await expect(page.getByRole('button', { name: /Ablauf · Piece/ })).toBeFocused();
|
||||
await page.getByRole('button', { name: /Ablauf · Piece/ }).click();
|
||||
await page.setViewportSize({ width: 900, height: 450 });
|
||||
await expect(liveRail).not.toHaveAttribute('role', 'dialog');
|
||||
await expect(page.locator('.live-command-header')).not.toHaveAttribute('inert', '');
|
||||
await page.setViewportSize({ width: 390, height: 700 });
|
||||
await expect(liveRail).toBeHidden();
|
||||
|
||||
await page.getByRole('button', { name: /Ablauf · Piece/ }).click();
|
||||
await liveRail
|
||||
.getByRole('button', {
|
||||
name: 'Die kleine Konsole mit dem großen Dreh als aktuelles Piece auswählen'
|
||||
})
|
||||
.click();
|
||||
await expect(liveRail).toBeHidden();
|
||||
await expect(page.locator('#current-title')).toHaveText('Die kleine Konsole mit dem großen Dreh');
|
||||
await page.getByRole('button', { name: /Ablauf · Piece/ }).click();
|
||||
await liveRail
|
||||
.getByRole('button', {
|
||||
name: 'Warum praktische Effekte wieder auffallen als aktuelles Piece auswählen'
|
||||
})
|
||||
.click();
|
||||
await expect(liveRail).toBeHidden();
|
||||
await expect(page.locator('#current-title')).toHaveText(
|
||||
'Warum praktische Effekte wieder auffallen'
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Erledigen' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Live-Stand wiederaufnehmen' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Live-Stand wiederaufnehmen' }).click();
|
||||
await expect(page.locator('#current-title')).toHaveText('Die kleine Konsole mit dem großen Dreh');
|
||||
await expectNoHorizontalOverflow(page);
|
||||
await page.getByRole('button', { name: 'Erledigen & Sendungsende' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Alle Pieces bearbeitet' })).toBeVisible();
|
||||
await expect(page.locator('.live-finished-label')).toHaveText('Ablauf abgeschlossen');
|
||||
await expect(page.getByRole('button', { name: 'Erledigen & weiter' })).toHaveCount(0);
|
||||
|
||||
await output.route('**/state', (route) => route.abort());
|
||||
await expect(output.getByText('Verbindung unterbrochen')).toBeVisible({ timeout: 5_000 });
|
||||
await output.unroute('**/state');
|
||||
await expect(output.getByText('Verbunden')).toBeVisible({ timeout: 5_000 });
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await expectNoHorizontalOverflow(page);
|
||||
await page.screenshot({
|
||||
path: testInfo.outputPath('live-controller-mobile.png'),
|
||||
fullPage: true
|
||||
});
|
||||
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.setViewportSize({ width: 1280, height: 900 });
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.open = () => null;
|
||||
});
|
||||
await page.getByText('Menü', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Ausgabe verbinden und öffnen' }).click();
|
||||
await expect(
|
||||
page
|
||||
|
||||
Reference in new issue
Block a user