From 14acfcef06f1845658f76b835fcc7f457ae17c8a Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Wed, 9 Sep 2026 23:31:56 +0330 Subject: [PATCH] test: cover case study gallery and sharing --- e2e/project-case-study.spec.ts | 29 ++++++++++++++++++++++++++--- tests/project-case-studies.test.mjs | 26 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/e2e/project-case-study.spec.ts b/e2e/project-case-study.spec.ts index 49dd8a4..fbb1ca5 100644 --- a/e2e/project-case-study.spec.ts +++ b/e2e/project-case-study.spec.ts @@ -47,21 +47,44 @@ test('a shared route opens over the portfolio and adjacent navigation replaces h await expect(page).toHaveURL(/\/$/); }); -test('copy feedback, focus containment, and the localized RTL route work', async ({ page, context }) => { +test('share fallback, copy feedback, focus containment, and the localized RTL route work', async ({ page, context }) => { await context.grantPermissions(['clipboard-read', 'clipboard-write']); await page.goto('/fa/projects/qlockify', { waitUntil: 'domcontentloaded' }); const dialog = await expectDrawerOpen(page); await expect(page.locator('html')).toHaveAttribute('dir', 'rtl'); await expect(dialog.getByRole('button', { name: 'بستن مطالعه موردی' })).toBeFocused(); - await dialog.getByRole('button', { name: 'کپی پیوند' }).click(); - await expect(dialog.getByText('پیوند کپی شد', { exact: true }).first()).toBeVisible(); + await dialog.getByRole('button', { name: 'اشتراک‌گذاری این مطالعه موردی' }).click(); + const shareDialog = page.getByRole('dialog', { name: 'اشتراک‌گذاری مطالعه موردی' }); + await expect(shareDialog).toBeVisible(); + await shareDialog.getByRole('button', { name: 'کپی پیوند' }).click(); + await expect(shareDialog.getByText('پیوند کپی شد', { exact: true }).first()).toBeVisible(); expect(await page.evaluate(() => navigator.clipboard.readText())).toMatch(/\/fa\/projects\/qlockify$/); + await shareDialog.getByRole('button', { name: 'بستن گزینه‌های اشتراک‌گذاری' }).click(); + await expect(dialog.getByRole('button', { name: 'اشتراک‌گذاری این مطالعه موردی' })).toBeFocused(); + await page.keyboard.press('Tab'); expect(await page.evaluate(() => document.activeElement?.closest('dialog') !== null)).toBe(true); }); +test('the gallery serves original media and opens an accessible full-screen viewer', async ({ page }) => { + await page.goto(caseStudyPath, { waitUntil: 'domcontentloaded' }); + const drawer = await expectDrawerOpen(page); + const stage = drawer.getByRole('button', { name: /Open full-screen image/ }); + const selectedImage = stage.getByRole('img'); + await expect(selectedImage).toHaveAttribute('src', '/images/screenshots/qlockify/qlockify-landing.png'); + + await stage.click(); + const viewer = page.getByRole('dialog', { name: 'Full-screen project gallery' }); + await expect(viewer).toBeVisible(); + await expect(viewer.getByText('1 of 8')).toBeVisible(); + await viewer.getByRole('button', { name: 'Next image' }).click(); + await expect(viewer.getByText('2 of 8')).toBeVisible(); + await viewer.getByRole('button', { name: 'Close full-screen image' }).click(); + await expect(stage).toBeFocused(); +}); + test('the drawer stays bottom-anchored with its own scrolling surface', async ({ page }) => { await page.goto(caseStudyPath, { waitUntil: 'domcontentloaded' }); const dialog = await expectDrawerOpen(page); diff --git a/tests/project-case-studies.test.mjs b/tests/project-case-studies.test.mjs index 415d737..44aee54 100644 --- a/tests/project-case-studies.test.mjs +++ b/tests/project-case-studies.test.mjs @@ -36,6 +36,9 @@ test('every project exposes an evidence-based localized case study', async () => assert.match(source, /export const getProject\s*=/); assert.match(source, /export const getAdjacentProjects\s*=/); assert.match(source, /export const getProjectPath\s*=/); + assert.match(source, /screenshots\/qlockify\/qlockify-landing\.png/); + assert.match(source, /screenshots\/guilan-ace\/guilan-ace-landing-dark\.png/); + assert.match(source, /screenshots\/subsource\/subtitle-detail-light\.png/); }); test('project cards separate case study navigation from live links', async () => { @@ -54,6 +57,8 @@ test('the case study drawer has accessible and pointer-safe dismissal paths', as const [component, styles] = await Promise.all([ Promise.all([ 'src/components/CaseStudyDrawer.tsx', + 'src/components/CaseStudyGallery.tsx', + 'src/components/CaseStudyShare.tsx', 'src/components/useDrawerDrag.ts', ].map((file) => readFile(file, 'utf8'))).then((files) => files.join('\n')), readFile('src/components/CaseStudyDrawer.module.css', 'utf8'), @@ -63,11 +68,32 @@ test('the case study drawer has accessible and pointer-safe dismissal paths', as assert.match(component, /onCancel=/); assert.match(component, /setPointerCapture/); assert.match(component, /navigator\.clipboard/); + assert.match(component, /navigator\.share/); assert.match(component, /aria-live="polite"/); assert.match(component, /data-case-study-initial-focus/); assert.doesNotMatch(component, /data-case-study-heading/); + assert.match(component, /unoptimized/); + assert.match(component, /quality=\{60\}/); + assert.match(component, /aria-haspopup="dialog"/); assert.match(styles, /::backdrop/); assert.match(styles, /env\(safe-area-inset-bottom/); assert.match(styles, /prefers-reduced-motion:\s*reduce/); assert.doesNotMatch(styles, /transition:\s*all/); }); + +test('the full-screen gallery and share fallback expose complete interaction paths', async () => { + const [gallery, share] = await Promise.all([ + readFile('src/components/CaseStudyGallery.tsx', 'utf8'), + readFile('src/components/CaseStudyShare.tsx', 'utf8'), + ]); + + assert.match(gallery, /ArrowLeft/); + assert.match(gallery, /ArrowRight/); + assert.match(gallery, /onPointerDown/); + assert.match(gallery, /onPointerUp/); + assert.match(gallery, /scrollIntoView/); + assert.match(share, /t\.me\/share\/url/); + assert.match(share, /wa\.me/); + assert.match(share, /linkedin\.com\/sharing\/share-offsite/); + assert.match(share, /mailto:/); +});