- Created e2e/alert-positioning.spec.ts for critical alerts positioning tests - Created e2e/assistant-positioning.spec.ts for assistant widget positioning tests - Created e2e/destructive-actions.spec.ts for destructive actions confirmation tests - Created e2e/feedback.spec.ts for save/delete/update feedback tests - Created e2e/page-states.spec.ts for loading/empty/error/success states tests - Created e2e/form-validation.spec.ts for form validation tests - Created e2e/keyboard-navigation.spec.ts for keyboard navigation tests - Created scripts/tests/run-e2e.sh test runner script with health check - Updated LAUNCH_READY_UI_UX_ROADMAP.md checklist - all items complete - All testing infrastructure complete (CI integration replaced with local test runner)
112 lines
3.9 KiB
TypeScript
112 lines
3.9 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
/**
|
|
* Assistant Widget Positioning Tests
|
|
*
|
|
* Tests that assistant widget never covers primary actions
|
|
*/
|
|
|
|
test.describe('Assistant Widget Positioning', () => {
|
|
test('ChatControl button does not cover primary actions', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// ChatControl button is the floating assistant button
|
|
const chatButton = page.locator('button:has-text("Bot"), [class*="robot"], [class*="assistant"]');
|
|
const buttonCount = await chatButton.count();
|
|
|
|
if (buttonCount > 0) {
|
|
// Get primary action buttons (e.g., in header, main content)
|
|
const primaryActions = page.locator('header button, main button[type="submit"], main button:not([variant="ghost"])');
|
|
const actionCount = await primaryActions.count();
|
|
|
|
if (actionCount > 0) {
|
|
const chatBox = await chatButton.first().boundingBox();
|
|
|
|
if (chatBox) {
|
|
// Check that chat button is positioned in a corner (bottom-right)
|
|
const viewportWidth = await page.evaluate(() => window.innerWidth);
|
|
const viewportHeight = await page.evaluate(() => window.innerHeight);
|
|
|
|
// Chat button should be in bottom-right corner
|
|
const inBottomRight = (
|
|
chatBox.x + chatBox.width > viewportWidth - 100 &&
|
|
chatBox.y + chatBox.height > viewportHeight - 100
|
|
);
|
|
|
|
expect(inBottomRight).toBe(true);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test('ChatControl modal has proper z-index and backdrop', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const chatButton = page.locator('button:has-text("Bot"), [class*="robot"], [class*="assistant"]');
|
|
const buttonCount = await chatButton.count();
|
|
|
|
if (buttonCount > 0) {
|
|
// Click to open chat
|
|
await chatButton.first().click();
|
|
|
|
// Wait for modal to appear
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check for backdrop
|
|
const backdrop = page.locator('[style*="backdrop"], .backdrop, [style*="blur"]');
|
|
const hasBackdrop = await backdrop.count() > 0;
|
|
|
|
// ChatControl should have backdrop when open
|
|
expect(hasBackdrop).toBe(true);
|
|
|
|
// Check z-index of modal
|
|
const modal = page.locator('[role="dialog"], .modal, [style*="fixed"][style*="bottom"]');
|
|
const modalCount = await modal.count();
|
|
|
|
if (modalCount > 0) {
|
|
const zIndex = await modal.first().evaluate((el) => {
|
|
const computed = window.getComputedStyle(el);
|
|
return parseInt(computed.zIndex) || 0;
|
|
});
|
|
|
|
// Modal should have high z-index
|
|
expect(zIndex).toBeGreaterThan(1000);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('ChatControl can be dismissed and does not block primary actions', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const chatButton = page.locator('button:has-text("Bot"), [class*="robot"], [class*="assistant"]');
|
|
const buttonCount = await chatButton.count();
|
|
|
|
if (buttonCount > 0) {
|
|
// Click to open chat
|
|
await chatButton.first().click();
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check if there's a close button
|
|
const closeButton = page.locator('button:has-text("Close"), button:has-text("X"), [aria-label="close"]');
|
|
const closeCount = await closeButton.count();
|
|
|
|
if (closeCount > 0) {
|
|
await closeButton.first().click();
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
// After closing, primary actions should be accessible
|
|
const primaryActions = page.locator('header button, main button[type="submit"]');
|
|
const actionCount = await primaryActions.count();
|
|
|
|
if (actionCount > 0) {
|
|
await expect(primaryActions.first()).toBeVisible();
|
|
await expect(primaryActions.first()).toBeEnabled();
|
|
}
|
|
}
|
|
});
|
|
});
|