diff --git a/packages/e2e-react/README.md b/packages/e2e-react/README.md index 181d94ad92..355a8f983b 100644 --- a/packages/e2e-react/README.md +++ b/packages/e2e-react/README.md @@ -1 +1,51 @@ -# E2E EmbeddedChat setup +# E2E EmbeddedChat Tests + +End-to-end tests using Playwright for the EmbeddedChat React package. Tests focus on UI stability and deterministic flows without external service dependencies. + +## Setup + +### Install system dependencies (Linux) +```bash +sudo yarn playwright install-deps +``` + +### Install Playwright browsers +```bash +cd packages/e2e-react +npx playwright install chromium +``` + +## Run Tests + +### Local +```bash +yarn workspace e2e-react test +``` + +### Watch mode +```bash +yarn workspace e2e-react test --watch +``` + +### Debug UI +```bash +yarn workspace e2e-react test --debug +``` + +### View HTML report +```bash +yarn workspace e2e-react show-report +``` + +## Test Coverage + +- **renders unauthenticated chat state** — Verifies EmbeddedChat loads with login prompt +- **opens login modal from join button** — Ensures JOIN button opens password auth modal +- **shows required field validation for empty login submit** — Tests form validation without auth service + +## Configuration + +- Base URL: `http://127.0.0.1:5173` (dev server) +- Host (RC server): Configurable via `VITE_RC_HOST` env var, defaults to `http://127.0.0.1:3000` +- CI retries: 2 (enabled on CI only) +- Failure artifacts: Screenshots + traces automatically collected diff --git a/packages/e2e-react/playwright.config.ts b/packages/e2e-react/playwright.config.ts index a91deee96a..25f339254f 100644 --- a/packages/e2e-react/playwright.config.ts +++ b/packages/e2e-react/playwright.config.ts @@ -20,14 +20,17 @@ export default defineConfig({ /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: process.env.CI + ? [['github'], ['html', { open: 'never' }]] + : [['list'], ['html', { open: 'never' }]], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL: 'http://127.0.0.1:5173', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + trace: 'retain-on-failure', + screenshot: 'only-on-failure', }, /* Configure projects for major browsers */ diff --git a/packages/e2e-react/src/App.tsx b/packages/e2e-react/src/App.tsx index d0f16785a3..bfe088fbd0 100644 --- a/packages/e2e-react/src/App.tsx +++ b/packages/e2e-react/src/App.tsx @@ -2,9 +2,11 @@ import { EmbeddedChat } from "@embeddedchat/react"; function App() { + const host = import.meta.env.VITE_RC_HOST || "http://127.0.0.1:3000"; + return ( ); diff --git a/packages/e2e-react/src/vite-env.d.ts b/packages/e2e-react/src/vite-env.d.ts index 11f02fe2a0..15758147fa 100644 --- a/packages/e2e-react/src/vite-env.d.ts +++ b/packages/e2e-react/src/vite-env.d.ts @@ -1 +1,9 @@ /// + +interface ImportMetaEnv { + readonly VITE_RC_HOST?: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/packages/e2e-react/tests/example.spec.ts b/packages/e2e-react/tests/example.spec.ts index 1189f4442a..9bbdc1cf20 100644 --- a/packages/e2e-react/tests/example.spec.ts +++ b/packages/e2e-react/tests/example.spec.ts @@ -1,13 +1,27 @@ import { test, expect } from "@playwright/test"; -test("EmbeddedChat should render", async ({ page }) => { +test.beforeEach(async ({ page }) => { await page.goto("/"); +}); + +test("renders unauthenticated chat state", async ({ page }) => { await expect(page.locator(".ec-embedded-chat")).toBeVisible(); + await expect(page.getByRole("heading", { name: "Login to chat" })).toBeVisible(); + await expect(page.getByRole("button", { name: "JOIN" })).toBeVisible(); + await expect(page.getByPlaceholder("Sign in to chat")).toBeDisabled(); }); -test("EmbeddedChat has a title", async ({ page }) => { - await page.goto("/"); - await expect(page.locator(".ec-chat-header--channelName")).toHaveText( - "Login to chat" - ); +test("opens login modal from join button", async ({ page }) => { + await page.getByRole("button", { name: "JOIN" }).click(); + + await expect(page.getByRole("heading", { name: "Login" })).toBeVisible(); + await expect(page.getByText("Email or username")).toBeVisible(); + await expect(page.getByText("Password")).toBeVisible(); +}); + +test("shows required field validation for empty login submit", async ({ page }) => { + await page.getByRole("button", { name: "JOIN" }).click(); + await page.getByRole("dialog").getByRole("button", { name: "Login" }).click(); + + await expect(page.getByText("This field is required")).toHaveCount(2); }); diff --git a/packages/react/src/views/LoginForm/LoginForm.js b/packages/react/src/views/LoginForm/LoginForm.js index 1285e5e1eb..53a8001315 100644 --- a/packages/react/src/views/LoginForm/LoginForm.js +++ b/packages/react/src/views/LoginForm/LoginForm.js @@ -41,8 +41,14 @@ export default function LoginForm() { }, [userOrEmail, password]); const handleSubmit = () => { - if (!userOrEmail) setUserOrEmail(''); - if (!password) setPassword(''); + if (!userOrEmail) { + setUserOrEmail(''); + return; + } + if (!password) { + setPassword(''); + return; + } handleLogin(userOrEmail, password); }; const handleClose = () => {