Files
LangBot/web/tests/e2e/guide-appearance.spec.ts
T

120 lines
4.5 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { installLangBotApiMocks } from './fixtures/langbot-api';
test('sidebar introduction takes priority without losing the page guide step', async ({
page,
}) => {
await installLangBotApiMocks(page, {
authenticated: true,
storage: {
langbot_sidebar_guide_v1: '0',
langbot_pipeline_setup_guide_v1: 'trigger',
},
});
await page.goto('/home/agents?id=pipeline-guide');
const sidebar = page.getByTestId('sidebar-guide');
const contextual = page.getByTestId('pipeline-setup-guide');
await expect(sidebar.getByRole('dialog')).toBeVisible();
await expect(contextual).toHaveCount(1);
await expect(contextual).toBeHidden();
await sidebar.getByRole('button', { name: 'Skip' }).click();
await expect(sidebar).toHaveCount(0);
await expect(contextual.getByRole('dialog')).toBeVisible();
await expect(contextual).toHaveAttribute('data-active-step', 'trigger');
await contextual.getByRole('button', { name: 'Next' }).click();
await expect(contextual).not.toHaveAttribute('data-active-step', 'trigger');
});
for (const theme of ['light', 'dark']) {
for (const sidebar of [false, true]) {
test(`${sidebar ? 'sidebar' : 'contextual'} guide keeps its highlight ring and backdrop in ${theme} mode`, async ({
page,
}, testInfo) => {
await installLangBotApiMocks(page, {
authenticated: true,
storage: {
'langbot-theme': theme,
...(sidebar
? { langbot_sidebar_guide_v1: '0' }
: { langbot_pipeline_setup_guide_v1: 'trigger' }),
},
});
await page.goto(
sidebar ? '/home/monitoring' : '/home/agents?id=pipeline-guide',
);
const id = sidebar ? 'sidebar-guide' : 'pipeline-setup-guide';
const guide = page.getByTestId(id);
await expect(guide.getByRole('dialog')).toBeVisible();
await expect(page.locator('html')).toHaveClass(theme);
const highlight = page.getByTestId(`${id}-highlight`);
const appearance = await highlight.evaluate((element) => {
const style = getComputedStyle(element);
const probe = document.createElement('span');
probe.style.color = style.getPropertyValue('--tw-ring-color');
element.append(probe);
const ring = getComputedStyle(probe).color;
probe.remove();
return { shadow: style.boxShadow, ring };
});
// A standalone backdrop shadow used to override Tailwind's ring shadows.
expect(appearance.ring).not.toBe('');
expect(appearance.shadow).toContain(appearance.ring);
expect(appearance.shadow).toContain('9999px');
if (theme === 'dark') {
expect(appearance.shadow).toContain('rgba(0, 0, 0, 0.72)');
}
await guide.getByRole('dialog').evaluate(async (element) => {
await Promise.all(
element.getAnimations().map((animation) => animation.finished),
);
});
await page.screenshot({ path: testInfo.outputPath('guide.png') });
});
}
}
for (const [language, label] of [
['en-US', 'User guide'],
['zh-Hans', '使用指引'],
['zh-Hant', '使用指引'],
['ja-JP', '使い方ガイド'],
['es-ES', 'Guía de uso'],
['ru-RU', 'Руководство'],
['th-TH', 'คำแนะนำการใช้งาน'],
['vi-VN', 'Hướng dẫn sử dụng'],
]) {
test(`shared guide controls fit a narrow viewport in ${language}`, async ({
page,
}) => {
await page.setViewportSize({ width: 390, height: 600 });
await installLangBotApiMocks(page, {
authenticated: true,
language,
storage: { langbot_sidebar_guide_v1: '1', 'langbot-theme': 'dark' },
});
await page.goto('/home/monitoring');
const guide = page.getByTestId('sidebar-guide');
const dialog = guide.getByRole('dialog');
await expect(dialog).toBeVisible();
await expect(guide.getByText(label, { exact: true })).toBeVisible();
await expect(
guide.locator('[data-guided-tour-action="previous"]'),
).toBeVisible();
await expect(
guide.locator('[data-guided-tour-action="next"]'),
).toBeFocused();
const box = await dialog.boundingBox();
expect(box!.x).toBeGreaterThanOrEqual(0);
expect(box!.y).toBeGreaterThanOrEqual(0);
expect(box!.x + box!.width).toBeLessThanOrEqual(390);
expect(box!.y + box!.height).toBeLessThanOrEqual(600);
expect(
await dialog.evaluate(
(element) => element.scrollWidth <= element.clientWidth,
),
).toBe(true);
await guide.locator('[data-guided-tour-action="skip"]').click();
await expect(guide).toHaveCount(0);
});
}