feat(web-test): add showTitleSlide/hideTitleSlide for video intros

Full-screen overlay with gradient background, centered title text,
optional subtitle. Useful for intro/outro frames in video recordings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-03-01 11:50:25 +03:00
parent 38c82f4a2f
commit bb07bfae14
4 changed files with 86 additions and 5 deletions
+1
View File
@@ -284,6 +284,7 @@ Clear filters. Without arguments clears all, with `{ field }` clears specific ba
#### `getPage()` → Playwright Page (raw, for advanced scripting)
#### `startRecording(path, opts?)` / `stopRecording()` → MP4 video recording
#### `showCaption(text, opts?)` / `hideCaption()` → text overlay on page
#### `showTitleSlide(text, opts?)` / `hideTitleSlide()` → full-screen title card (intro/outro)
#### `isRecording()` → boolean
See [recording.md](recording.md) for setup (ffmpeg), API details, and examples.
+29 -5
View File
@@ -94,24 +94,48 @@ The overlay uses `pointer-events: none` — does not interfere with clicking.
Remove the caption overlay.
## Example: Record a workflow with captions
### `showTitleSlide(text, opts?)`
Display a full-screen title slide overlay (gradient background, centered text). Useful for intro/outro frames in video recordings. Calling again updates the content.
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `text` | string | required | Title text (`\n` → line break) |
| `opts.subtitle` | string | `''` | Smaller text below the title |
| `opts.background` | string | dark gradient | CSS background |
| `opts.color` | string | `'#fff'` | Text color |
| `opts.fontSize` | number | 36 | Title font size in px |
The overlay covers the entire viewport with `z-index: 999999` and `pointer-events: none`.
### `hideTitleSlide()`
Remove the title slide overlay.
## Example: Record a workflow with title slide and captions
```js
await startRecording('recordings/create-order.mp4');
await showCaption('Step 1: Navigate to Sales');
// Title slide — 4 seconds
await showTitleSlide('Создание заказа клиента', { subtitle: 'Демонстрация' });
await wait(4);
await hideTitleSlide();
// Steps with captions
await showCaption('Шаг 1. Переходим в раздел «Продажи»');
await navigateSection('Продажи');
await wait(1);
await showCaption('Step 2: Open Customer Orders');
await showCaption('Шаг 2. Открываем заказы клиентов');
await openCommand('Заказы клиентов');
await wait(1);
await showCaption('Step 3: Create new order');
await showCaption('Шаг 3. Создаём новый заказ');
await clickElement('Создать');
await wait(2);
await showCaption('Step 4: Fill header fields');
await showCaption('Шаг 4. Заполняем шапку');
await fillFields({ 'Организация': 'Конфетпром', 'Контрагент': 'Альфа' });
await wait(2);
@@ -2570,6 +2570,55 @@ export async function hideCaption() {
});
}
/**
* Show a full-screen title slide overlay (for video recordings).
* Repeated calls update the content. Use hideTitleSlide() to remove.
* @param {string} text Title text (\n → line break)
* @param {object} [opts]
* @param {string} [opts.subtitle] Smaller text below the title
* @param {string} [opts.background] CSS background (default: dark gradient)
* @param {string} [opts.color] Text color (default: '#fff')
* @param {number} [opts.fontSize] Title font size in px (default: 36)
*/
export async function showTitleSlide(text, opts = {}) {
ensureConnected();
const {
subtitle = '',
background = 'linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)',
color = '#fff',
fontSize = 36,
} = opts;
await page.evaluate(({ text, subtitle, background, color, fontSize }) => {
let div = document.getElementById('__web_test_title');
if (!div) {
div = document.createElement('div');
div.id = '__web_test_title';
document.body.appendChild(div);
}
div.style.cssText = [
'position:fixed', 'top:0', 'left:0', 'width:100%', 'height:100%',
`background:${background}`,
'display:flex', 'align-items:center', 'justify-content:center',
'z-index:999999', 'pointer-events:none'
].join(';');
const esc = s => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\n/g, '<br>');
let html = `<div style="font-size:${fontSize}px;font-weight:600;line-height:1.4;">${esc(text)}</div>`;
if (subtitle) {
html += `<div style="font-size:${Math.round(fontSize * 0.5)}px;margin-top:16px;opacity:0.7;">${esc(subtitle)}</div>`;
}
div.innerHTML = `<div style="text-align:center;max-width:70%;color:${color};font-family:'Segoe UI',Arial,sans-serif;">${html}</div>`;
}, { text, subtitle, background, color, fontSize });
}
/** Remove the title slide overlay. */
export async function hideTitleSlide() {
ensureConnected();
await page.evaluate(() => {
const el = document.getElementById('__web_test_title');
if (el) el.remove();
});
}
// ============================================================
// Private helpers
// ============================================================
+7
View File
@@ -251,6 +251,13 @@ await closeForm({ save: false });
| `screenshot()` | Скриншот (PNG Buffer) |
| `wait(seconds)` | Пауза, возвращает form state |
| `getPage()` | Сырой Playwright Page для горячих клавиш и нестандартных операций |
| `startRecording(path, opts?)` | Начать запись видео (CDP screencast → ffmpeg → MP4) |
| `stopRecording()` | Остановить запись, вернуть `{ file, duration, size }` |
| `showCaption(text, opts?)` | Текстовая подпись поверх страницы (для видеозаписей) |
| `hideCaption()` | Убрать подпись |
| `showTitleSlide(text, opts?)` | Полноэкранный титульный слайд (`\n` → перенос, `subtitle`, `background`) |
| `hideTitleSlide()` | Убрать титульный слайд |
| `isRecording()` | Идёт ли запись (boolean) |
## Клавиатурные сочетания