diff --git a/.github/workflows/_reusable-lighthouse.yml b/.github/workflows/_reusable-lighthouse.yml index d1ed4f4..f2abe0d 100644 --- a/.github/workflows/_reusable-lighthouse.yml +++ b/.github/workflows/_reusable-lighthouse.yml @@ -10,7 +10,7 @@ on: project_path: required: true type: string - description: 'Path to the Next.js project (relative to repo root)' + description: 'Path to the Astro project (relative to repo root)' jobs: lighthouse: @@ -36,10 +36,10 @@ jobs: run: npm install -g @lhci/cli@0.13.x - name: Run Lighthouse CI working-directory: ${{ inputs.project_path }} - # Non-blocking: chatislam/web is an SSR app (output: 'server', Vercel - # adapter) with no static dist for LHCI's staticDistDir to serve. - # Tracked as follow-up to rewire LHCI to build + serve the SSR app. - continue-on-error: true + # Blocking gate. The app is SSR (output: 'server', Vercel adapter); the + # landing pages audited by lighthouserc.cjs are `export const prerender = + # true`, so `pnpm build` emits their static HTML into + # .vercel/output/static, which LHCI serves via staticDistDir. run: lhci autorun - name: Upload Lighthouse report if: always() diff --git a/.github/workflows/a11y-axe.yml b/.github/workflows/a11y-axe.yml index 34cd1e9..cd1244b 100644 --- a/.github/workflows/a11y-axe.yml +++ b/.github/workflows/a11y-axe.yml @@ -54,12 +54,15 @@ jobs: NEXT_PUBLIC_AUTH_URL: 'https://auth.ummat.dev' ANTHROPIC_API_KEY: 'dummy-for-build' - - name: Start production server + # `pnpm start` (astro preview) is unsupported by @astrojs/vercel/serverless, + # so it never binds and wait-on times out. Use the dev server, which serves + # SSR routes with any adapter (same approach as playwright.config.ts). + - name: Start dev server working-directory: web - run: pnpm start --port 3042 & + run: pnpm dev & - name: Wait for server - run: npx wait-on http://localhost:3042 --timeout 30000 + run: npx wait-on http://localhost:3042 --timeout 120000 - name: Install axe-cli run: npm install -g @axe-core/cli @@ -68,13 +71,13 @@ jobs: # Tags: wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa # --exit: exit non-zero on violation # --tags: enforce up to WCAG 2.2 AA + # @axe-core/cli prints results to stdout by default; there is no + # --reporter flag (it errors "unknown option '--reporter'"). run: | axe http://localhost:3042 \ --tags wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22aa \ --exit \ - --reporter cli \ --include "main, nav, [role='main']" axe http://localhost:3042/chat \ --tags wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22aa \ - --exit \ - --reporter cli + --exit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e93c3c9..c8f4ced 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,11 +73,6 @@ jobs: name: E2E Tests runs-on: ubuntu-latest needs: build - # Non-blocking: the Astro migration left pre-existing e2e failures (RTL - # dir/lang not applied in ar/ur locales, empty-state assertion, missing - # platform snapshot baselines). These are tracked as follow-up app/test - # work; the blocking gates are lint, type-check, unit tests, and build. - continue-on-error: true steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 diff --git a/web/.gitignore b/web/.gitignore index 9f5bed3..3282053 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -31,6 +31,7 @@ coverage/ playwright-report/ test-results/ __tests__/e2e/**/*-snapshots/ +.lighthouseci/ # Astro .astro/ diff --git a/web/__tests__/e2e/rtl.spec.ts b/web/__tests__/e2e/rtl.spec.ts index 55ceda4..2e6ede0 100644 --- a/web/__tests__/e2e/rtl.spec.ts +++ b/web/__tests__/e2e/rtl.spec.ts @@ -67,14 +67,31 @@ test.describe('RTL layout — Arabic locale', () => { expect(hasOverflow).toBe(false) }) - test('RTL baseline screenshot — homepage Arabic', async ({ page }) => { + // Visual PNG snapshots are platform-dependent — Arabic shaping/fonts render + // differently across OSes, so a baseline captured on one platform is not a + // reliable gate on CI (linux). Instead assert the RTL layout properties a + // baseline would protect: computed direction on the document + main region, + // translated (Arabic) content, and no horizontal overflow. + test('RTL layout renders correctly — homepage Arabic', async ({ page }) => { await page.goto('/') await page.waitForLoadState('networkidle') - // Baseline: run once with --update-snapshots to create; subsequent runs compare. - await expect(page).toHaveScreenshot('homepage-ar-rtl.png', { - fullPage: false, - animations: 'disabled', + + const htmlDir = await page.evaluate(() => getComputedStyle(document.documentElement).direction) + expect(htmlDir).toBe('rtl') + + const mainDir = await page.evaluate(() => { + const main = document.querySelector('#main-content') + return main ? getComputedStyle(main).direction : null }) + expect(mainDir).toBe('rtl') + + const bodyText = await page.evaluate(() => document.body.innerText) + expect(bodyText).toMatch(/[؀-ۿ]/) + + const hasOverflow = await page.evaluate( + () => document.documentElement.scrollWidth > window.innerWidth, + ) + expect(hasOverflow).toBe(false) }) }) @@ -110,13 +127,28 @@ test.describe('RTL layout — Urdu locale', () => { expect(hasOverflow).toBe(false) }) - test('RTL baseline screenshot — homepage Urdu', async ({ page }) => { + // See the Arabic case above — assert RTL layout properties rather than commit + // a platform-dependent PNG baseline. + test('RTL layout renders correctly — homepage Urdu', async ({ page }) => { await page.goto('/') await page.waitForLoadState('networkidle') - await expect(page).toHaveScreenshot('homepage-ur-rtl.png', { - fullPage: false, - animations: 'disabled', + + const htmlDir = await page.evaluate(() => getComputedStyle(document.documentElement).direction) + expect(htmlDir).toBe('rtl') + + const mainDir = await page.evaluate(() => { + const main = document.querySelector('#main-content') + return main ? getComputedStyle(main).direction : null }) + expect(mainDir).toBe('rtl') + + const bodyText = await page.evaluate(() => document.body.innerText) + expect(bodyText).toMatch(/[؀-ۿ]/) + + const hasOverflow = await page.evaluate( + () => document.documentElement.scrollWidth > window.innerWidth, + ) + expect(hasOverflow).toBe(false) }) }) diff --git a/web/astro.config.ts b/web/astro.config.ts index 459fc40..5a87c42 100644 --- a/web/astro.config.ts +++ b/web/astro.config.ts @@ -20,6 +20,10 @@ import tailwindcss from '@tailwindcss/vite' export default defineConfig({ site: 'https://chatislam.org', output: 'server', + // Dev-only overlay injects unlabeled