From af0b655df7320c52614285bf0d74888ff4d5fb3f Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 21 Dec 2024 18:54:37 +0100 Subject: [PATCH 01/13] chore: add e2e tests powered by playwright --- .github/workflows/playwright.yml | 27 ++++++++ .gitignore | 4 ++ e2e-tests/hp.e2e.test.ts | 30 +++++++++ package.json | 4 ++ playwright.config.ts | 74 +++++++++++++++++++++ pnpm-lock.yaml | 108 +++++++++++++++++++++++-------- vitest.config.mts | 1 + 7 files changed, 222 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 e2e-tests/hp.e2e.test.ts create mode 100644 playwright.config.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..f6d7e71 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm install -g pnpm && pnpm install + - name: Install Playwright Browsers + run: pnpm exec playwright install --with-deps + - name: Run Playwright tests + run: pnpm exec playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 2bc9941..0c9793b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ package-lock.json *.idea .astro +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/e2e-tests/hp.e2e.test.ts b/e2e-tests/hp.e2e.test.ts new file mode 100644 index 0000000..0b2434c --- /dev/null +++ b/e2e-tests/hp.e2e.test.ts @@ -0,0 +1,30 @@ +import { test, expect } from '@playwright/test'; + +test('italian HP is well formed', async ({ page }) => { + await page.goto('./'); + + const html = page.locator('html'); + const lang = await html.getAttribute('lang'); + + expect(lang).toBe('it'); + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/La tech community di Javascript su Roma/); + + const h1 = page.locator('h1'); + await h1.isVisible(); + await expect(h1).toHaveText(/La tech community di Javascript su Roma/); +}); + +test('english HP is well formed', async ({ page }) => { + await page.goto('./en'); + + const html = page.locator('html'); + const lang = await html.getAttribute('lang'); + + expect(lang).toBe('en'); + await expect(page).toHaveTitle(/The Javascript community in Rome/); + + const h1 = page.locator('h1'); + await h1.isVisible(); + await expect(h1).toHaveText(/The Javascript community in Rome/); +}); diff --git a/package.json b/package.json index 5723c19..3448e3c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "build": "npm run lint:ts && astro build", "astro:sync": "astro sync", "astro:upgrade": "pnpm dlx @astrojs/upgrade", + "test:e2e": "pnpm exec playwright test", + "test:e2e:ui": "pnpm exec playwright test --ui", "lint:ts": "tsc --noEmit", "lint:astro": "astro check", "create-post": "node scripts/create-post.mjs", @@ -29,6 +31,8 @@ "@astrojs/rss": "^4.0.7", "@astrojs/sitemap": "^3.1.6", "@astrojs/solid-js": "^4.4.1", + "@playwright/test": "^1.49.1", + "@types/node": "^22.10.2", "astro": "4.15.4", "astro-i18next": "1.0.0-beta.21", "chalk": "^5.0.1", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..a18aadd --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,74 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +const isCi = process.env.CI === 'true'; +const baseURL = 'http://localhost:4321/'; +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e-tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: isCi, + /* Retry on CI only */ + retries: isCi ? 1 : 0, + /* Opt out of parallel tests on CI. */ + workers: isCi ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* 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, + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'android(Pixel 7)', + use: { ...devices['Pixel 7'] }, + }, + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'pnpm run build && pnpm run preview', + url: baseURL, + reuseExistingServer: !isCi, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f019e4d..04d74e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,13 +56,19 @@ importers: version: 3.1.6 '@astrojs/solid-js': specifier: ^4.4.1 - version: 4.4.1(solid-js@1.8.21)(vite@5.4.2(sass@1.77.8)) + version: 4.4.1(solid-js@1.8.21)(vite@5.4.2(@types/node@22.10.2)(sass@1.77.8)) + '@playwright/test': + specifier: ^1.49.1 + version: 1.49.1 + '@types/node': + specifier: ^22.10.2 + version: 22.10.2 astro: specifier: 4.15.4 - version: 4.15.4(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4) + version: 4.15.4(@types/node@22.10.2)(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4) astro-i18next: specifier: 1.0.0-beta.21 - version: 1.0.0-beta.21(astro@4.15.4(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4)) + version: 1.0.0-beta.21(astro@4.15.4(@types/node@22.10.2)(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4)) chalk: specifier: ^5.0.1 version: 5.3.0 @@ -92,7 +98,7 @@ importers: version: 1.3.3 vitest: specifier: ^2.0.2 - version: 2.0.2(jsdom@24.1.0)(sass@1.77.8) + version: 2.0.2(@types/node@22.10.2)(jsdom@24.1.0)(sass@1.77.8) packages: @@ -647,6 +653,11 @@ packages: '@oslojs/encoding@0.4.1': resolution: {integrity: sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==} + '@playwright/test@1.49.1': + resolution: {integrity: sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==} + engines: {node: '>=18'} + hasBin: true + '@proload/core@0.3.3': resolution: {integrity: sha512-7dAFWsIK84C90AMl24+N/ProHKm4iw0akcnoKjRvbfHifJZBLhaDsDus1QJmhG12lXj4e/uB/8mB/0aduCW+NQ==} @@ -917,6 +928,9 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@22.10.2': + resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} + '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} @@ -1565,6 +1579,11 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2210,6 +2229,16 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + playwright-core@1.49.1: + resolution: {integrity: sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.49.1: + resolution: {integrity: sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==} + engines: {node: '>=18'} + hasBin: true + postcss@8.4.39: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} @@ -2609,6 +2638,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -3120,10 +3152,10 @@ snapshots: stream-replace-string: 2.0.0 zod: 3.23.8 - '@astrojs/solid-js@4.4.1(solid-js@1.8.21)(vite@5.4.2(sass@1.77.8))': + '@astrojs/solid-js@4.4.1(solid-js@1.8.21)(vite@5.4.2(@types/node@22.10.2)(sass@1.77.8))': dependencies: solid-js: 1.8.21 - vite-plugin-solid: 2.10.2(solid-js@1.8.21)(vite@5.4.2(sass@1.77.8)) + vite-plugin-solid: 2.10.2(solid-js@1.8.21)(vite@5.4.2(@types/node@22.10.2)(sass@1.77.8)) transitivePeerDependencies: - '@testing-library/jest-dom' - supports-color @@ -3616,6 +3648,10 @@ snapshots: '@oslojs/encoding@0.4.1': {} + '@playwright/test@1.49.1': + dependencies: + playwright: 1.49.1 + '@proload/core@0.3.3': dependencies: deepmerge: 4.2.2 @@ -3831,9 +3867,13 @@ snapshots: '@types/node@17.0.45': {} + '@types/node@22.10.2': + dependencies: + undici-types: 6.20.0 + '@types/sax@1.2.7': dependencies: - '@types/node': 17.0.45 + '@types/node': 22.10.2 '@types/unist@3.0.2': {} @@ -3983,11 +4023,11 @@ snapshots: assertion-error@2.0.1: {} - astro-i18next@1.0.0-beta.21(astro@4.15.4(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4)): + astro-i18next@1.0.0-beta.21(astro@4.15.4(@types/node@22.10.2)(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4)): dependencies: '@proload/core': 0.3.3 '@proload/plugin-tsm': 0.2.1(@proload/core@0.3.3) - astro: 4.15.4(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4) + astro: 4.15.4(@types/node@22.10.2)(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4) i18next: 22.4.10 i18next-browser-languagedetector: 7.1.0 i18next-fs-backend: 2.1.5 @@ -3998,7 +4038,7 @@ snapshots: transitivePeerDependencies: - encoding - astro@4.15.4(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4): + astro@4.15.4(@types/node@22.10.2)(rollup@4.21.0)(sass@1.77.8)(typescript@5.5.4): dependencies: '@astrojs/compiler': 2.10.3 '@astrojs/internal-helpers': 0.4.1 @@ -4058,8 +4098,8 @@ snapshots: tsconfck: 3.1.3(typescript@5.5.4) unist-util-visit: 5.0.0 vfile: 6.0.3 - vite: 5.4.2(sass@1.77.8) - vitefu: 1.0.2(vite@5.4.2(sass@1.77.8)) + vite: 5.4.2(@types/node@22.10.2)(sass@1.77.8) + vitefu: 1.0.2(vite@5.4.2(@types/node@22.10.2)(sass@1.77.8)) which-pm: 3.0.0 xxhash-wasm: 1.0.2 yargs-parser: 21.1.1 @@ -4549,6 +4589,9 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -5386,6 +5429,14 @@ snapshots: dependencies: find-up: 4.1.0 + playwright-core@1.49.1: {} + + playwright@1.49.1: + dependencies: + playwright-core: 1.49.1 + optionalDependencies: + fsevents: 2.3.2 + postcss@8.4.39: dependencies: nanoid: 3.3.7 @@ -5838,6 +5889,8 @@ snapshots: typescript@5.5.4: {} + undici-types@6.20.0: {} + unified@11.0.5: dependencies: '@types/unist': 3.0.2 @@ -5926,13 +5979,13 @@ snapshots: '@types/unist': 3.0.2 vfile-message: 4.0.2 - vite-node@2.0.2(sass@1.77.8): + vite-node@2.0.2(@types/node@22.10.2)(sass@1.77.8): dependencies: cac: 6.7.14 debug: 4.3.5 pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.3.3(sass@1.77.8) + vite: 5.3.3(@types/node@22.10.2)(sass@1.77.8) transitivePeerDependencies: - '@types/node' - less @@ -5943,7 +5996,7 @@ snapshots: - supports-color - terser - vite-plugin-solid@2.10.2(solid-js@1.8.21)(vite@5.4.2(sass@1.77.8)): + vite-plugin-solid@2.10.2(solid-js@1.8.21)(vite@5.4.2(@types/node@22.10.2)(sass@1.77.8)): dependencies: '@babel/core': 7.24.7 '@types/babel__core': 7.20.5 @@ -5951,38 +6004,40 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.8.21 solid-refresh: 0.6.3(solid-js@1.8.21) - vite: 5.4.2(sass@1.77.8) - vitefu: 0.2.5(vite@5.4.2(sass@1.77.8)) + vite: 5.4.2(@types/node@22.10.2)(sass@1.77.8) + vitefu: 0.2.5(vite@5.4.2(@types/node@22.10.2)(sass@1.77.8)) transitivePeerDependencies: - supports-color - vite@5.3.3(sass@1.77.8): + vite@5.3.3(@types/node@22.10.2)(sass@1.77.8): dependencies: esbuild: 0.21.5 postcss: 8.4.39 rollup: 4.18.1 optionalDependencies: + '@types/node': 22.10.2 fsevents: 2.3.3 sass: 1.77.8 - vite@5.4.2(sass@1.77.8): + vite@5.4.2(@types/node@22.10.2)(sass@1.77.8): dependencies: esbuild: 0.21.5 postcss: 8.4.41 rollup: 4.21.0 optionalDependencies: + '@types/node': 22.10.2 fsevents: 2.3.3 sass: 1.77.8 - vitefu@0.2.5(vite@5.4.2(sass@1.77.8)): + vitefu@0.2.5(vite@5.4.2(@types/node@22.10.2)(sass@1.77.8)): optionalDependencies: - vite: 5.4.2(sass@1.77.8) + vite: 5.4.2(@types/node@22.10.2)(sass@1.77.8) - vitefu@1.0.2(vite@5.4.2(sass@1.77.8)): + vitefu@1.0.2(vite@5.4.2(@types/node@22.10.2)(sass@1.77.8)): optionalDependencies: - vite: 5.4.2(sass@1.77.8) + vite: 5.4.2(@types/node@22.10.2)(sass@1.77.8) - vitest@2.0.2(jsdom@24.1.0)(sass@1.77.8): + vitest@2.0.2(@types/node@22.10.2)(jsdom@24.1.0)(sass@1.77.8): dependencies: '@ampproject/remapping': 2.3.0 '@vitest/expect': 2.0.2 @@ -6000,10 +6055,11 @@ snapshots: tinybench: 2.8.0 tinypool: 1.0.0 tinyrainbow: 1.2.0 - vite: 5.3.3(sass@1.77.8) - vite-node: 2.0.2(sass@1.77.8) + vite: 5.3.3(@types/node@22.10.2)(sass@1.77.8) + vite-node: 2.0.2(@types/node@22.10.2)(sass@1.77.8) why-is-node-running: 2.3.0 optionalDependencies: + '@types/node': 22.10.2 jsdom: 24.1.0 transitivePeerDependencies: - less diff --git a/vitest.config.mts b/vitest.config.mts index 17dcb40..6a17170 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -10,6 +10,7 @@ export default defineConfig({ globals: true, environment: 'jsdom', setupFiles: [], + include: ['src/**/*.{test,spec}.?(c|m)[jt]s?(x)'], }, resolve: { alias: { From a415e64dc25538509ac68951c16d0a0781887425 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 21 Dec 2024 19:06:25 +0100 Subject: [PATCH 02/13] chore(playwright): get node version from nvmrc --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f6d7e71..2a281e1 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: lts/* + node-version-file: '.nvmrc' - name: Install dependencies run: npm install -g pnpm && pnpm install - name: Install Playwright Browsers From 5ce3e6cee8106fd96c368be3017102b27add1056 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 21 Dec 2024 19:07:31 +0100 Subject: [PATCH 03/13] chore: install pnpm version using corepack --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 2a281e1..7580d8e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -14,7 +14,7 @@ jobs: with: node-version-file: '.nvmrc' - name: Install dependencies - run: npm install -g pnpm && pnpm install + run: corepack enable && pnpm install - name: Install Playwright Browsers run: pnpm exec playwright install --with-deps - name: Run Playwright tests From 09d7e7c50776b237df402be9ef28e733a8272551 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 21 Dec 2024 19:12:35 +0100 Subject: [PATCH 04/13] docs: add e2e scripts to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e464755..7444126 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,8 @@ periodically triggers the deployment. - `pnpm run test` runs tests - `pnpm run test:watch` runs tests in watch mode - `pnpm run astro:upgrade` upgrade astro deps to latest +- `pnpm run test:e2e` run e2e tests +- `pnpm run test:e2e:ui` run e2e2 tests in UI mode ## Designs From 82b888e024bd3a20c726721b7316e9129989f067 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 21 Dec 2024 19:14:17 +0100 Subject: [PATCH 05/13] chore: add Mobile Safari(iPhone) as playwright browser --- playwright.config.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index a18aadd..e6f47e9 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -44,15 +44,10 @@ export default defineConfig({ name: 'android(Pixel 7)', use: { ...devices['Pixel 7'] }, }, - /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { ...devices['Pixel 5'] }, - // }, - // { - // name: 'Mobile Safari', - // use: { ...devices['iPhone 12'] }, - // }, + { + name: 'Mobile Safari(iPhone)', + use: { ...devices['iPhone 15 Pro'] }, + }, /* Test against branded browsers. */ // { From 6759bd2738d9e8cfee6b92a2f0df45a4a4121984 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 21 Dec 2024 20:13:25 +0100 Subject: [PATCH 06/13] chore(e2e): add about and blog page tests --- e2e-tests/about.e2e.test.ts | 32 ++++++++++++ e2e-tests/blog-index.e2e.test.ts | 15 ++++++ e2e-tests/hp.e2e.test.ts | 34 ++++++++----- e2e-tests/shared-e2e-tests.ts | 84 ++++++++++++++++++++++++++++++++ playwright.config.ts | 10 ++-- 5 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 e2e-tests/about.e2e.test.ts create mode 100644 e2e-tests/blog-index.e2e.test.ts create mode 100644 e2e-tests/shared-e2e-tests.ts diff --git a/e2e-tests/about.e2e.test.ts b/e2e-tests/about.e2e.test.ts new file mode 100644 index 0000000..f3c32a1 --- /dev/null +++ b/e2e-tests/about.e2e.test.ts @@ -0,0 +1,32 @@ +import { test } from '@playwright/test'; +import { + expectH1, + expectHtmlLang, + expectValidCountrySelector, + expectValidFooter, +} from './shared-e2e-tests'; + +const urlMap = { + it: '/it/about', + en: '/en/about', +}; + +test('italian about page is well formed', async ({ page }) => { + await page.goto('./it/about'); + const lang = 'it'; + await expectHtmlLang(page, lang); + await expectValidFooter(page); + await expectValidCountrySelector(page, lang, urlMap); + + await expectH1(page, /About RomaJS/); +}); + +test('english about page is well formed', async ({ page }) => { + await page.goto('./en/about'); + const lang = 'en'; + await expectHtmlLang(page, lang); + await expectValidFooter(page); + await expectValidCountrySelector(page, lang, urlMap); + + await expectH1(page, /About RomaJS/); +}); diff --git a/e2e-tests/blog-index.e2e.test.ts b/e2e-tests/blog-index.e2e.test.ts new file mode 100644 index 0000000..4206ad1 --- /dev/null +++ b/e2e-tests/blog-index.e2e.test.ts @@ -0,0 +1,15 @@ +import { test } from '@playwright/test'; +import { + expectH1, + expectHtmlLang, + expectValidFooter, +} from './shared-e2e-tests'; + +test('blog index is well formed', async ({ page }) => { + await page.goto('./blog'); + const lang = 'it'; + await expectHtmlLang(page, lang); + await expectValidFooter(page); + + await expectH1(page, /RomaJS Blog/); +}); diff --git a/e2e-tests/hp.e2e.test.ts b/e2e-tests/hp.e2e.test.ts index 0b2434c..5c1d7d9 100644 --- a/e2e-tests/hp.e2e.test.ts +++ b/e2e-tests/hp.e2e.test.ts @@ -1,30 +1,38 @@ import { test, expect } from '@playwright/test'; +import { + expectH1, + expectHtmlLang, + expectValidCountrySelector, + expectValidFooter, +} from './shared-e2e-tests'; + +const urlMap = { + it: '/', + en: '/en', +}; test('italian HP is well formed', async ({ page }) => { await page.goto('./'); + const lang = 'it'; + await expectHtmlLang(page, lang); + await expectValidFooter(page); + await expectValidCountrySelector(page, lang, urlMap); - const html = page.locator('html'); - const lang = await html.getAttribute('lang'); - - expect(lang).toBe('it'); // Expect a title "to contain" a substring. await expect(page).toHaveTitle(/La tech community di Javascript su Roma/); - const h1 = page.locator('h1'); - await h1.isVisible(); - await expect(h1).toHaveText(/La tech community di Javascript su Roma/); + await expectH1(page, /La tech community di Javascript su Roma/); }); test('english HP is well formed', async ({ page }) => { await page.goto('./en'); + const lang = 'en'; - const html = page.locator('html'); - const lang = await html.getAttribute('lang'); + await expectHtmlLang(page, lang); + await expectValidFooter(page); + await expectValidCountrySelector(page, lang, urlMap); - expect(lang).toBe('en'); await expect(page).toHaveTitle(/The Javascript community in Rome/); - const h1 = page.locator('h1'); - await h1.isVisible(); - await expect(h1).toHaveText(/The Javascript community in Rome/); + await expectH1(page, /The Javascript community in Rome/); }); diff --git a/e2e-tests/shared-e2e-tests.ts b/e2e-tests/shared-e2e-tests.ts new file mode 100644 index 0000000..e5332ee --- /dev/null +++ b/e2e-tests/shared-e2e-tests.ts @@ -0,0 +1,84 @@ +import { expect, type Page } from '@playwright/test'; + +const allLangs = ['it', 'en'] as const; + +type ExpectedLang = (typeof allLangs)[number]; + +/** + * Asserts that page uses expected lang + * @param page + * @param expectedLang + */ +export async function expectHtmlLang(page: Page, expectedLang: ExpectedLang) { + const html = page.locator('html'); + const lang = await html.getAttribute('lang'); + + expect(lang).toBe(expectedLang); +} + +/** + * Asserts that page has valid footer + * @param page + */ +export async function expectValidFooter(page: Page) { + const footer = page.locator('footer'); + + const socialLinks = [ + 'facebook', + 'discord', + 'youtube', + 'linkedin', + 'rss', + 'github', + ]; + + for (const socialLink of socialLinks) { + const link = await footer.getByRole('link', { name: socialLink }); + expect(link).toBeVisible(); + } +} + +/** + * Asserts that page has valid country selector + * @param page + * @param expectedLang + * @param urlMap + */ +export async function expectValidCountrySelector( + page: Page, + expectedLang: ExpectedLang, + urlMap: Record +) { + const currentPageLink = page.locator( + `nav a[aria-current="page"][hreflang="${expectedLang}"]` + ); + const currentPagehref = await currentPageLink.getAttribute('href'); + + expect(currentPageLink).toBeVisible(); + expect(await currentPageLink.textContent()).toBe(expectedLang); + expect(currentPagehref).toBe(urlMap[expectedLang]); + + const otherLangs = allLangs.filter((lang) => lang !== expectedLang); + + for (const lang of otherLangs) { + const pageLink = page.locator(`nav a[hreflang="${lang}"]`); + const ariaCurrent = await pageLink.getAttribute('aria-current'); + const href = await pageLink.getAttribute('href'); + + expect(href).toBe(urlMap[lang]); + expect(pageLink).toBeVisible(); + expect(await pageLink.textContent()).toBe(lang); + expect(ariaCurrent).toBe(null); + } +} + +/** + * Asserts that page has valid h1 wuth input text + * @param page + * @param text + */ +export async function expectH1(page: Page, text: string | RegExp) { + const h1 = page.locator('h1'); + await h1.isVisible(); + await expect(h1).toHaveText(text); +} diff --git a/playwright.config.ts b/playwright.config.ts index e6f47e9..6ec2a3c 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -41,12 +41,16 @@ export default defineConfig({ use: { ...devices['Desktop Chrome'] }, }, { - name: 'android(Pixel 7)', - use: { ...devices['Pixel 7'] }, + name: 'Mobile Chrome (android)', + use: { ...devices['Pixel 7'], isMobile: true }, }, { name: 'Mobile Safari(iPhone)', - use: { ...devices['iPhone 15 Pro'] }, + use: { ...devices['iPhone 15 Pro'], isMobile: true }, + }, + { + name: 'Firefox desktop', + use: { ...devices['Desktop Firefox'] }, }, /* Test against branded browsers. */ From e653323dbf8130d731445b54dc8cbae66904783c Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 22 Dec 2024 12:25:19 +0100 Subject: [PATCH 07/13] chore: force timezone and locale of the e2e browsers --- playwright.config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/playwright.config.ts b/playwright.config.ts index 6ec2a3c..9847d39 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -32,6 +32,8 @@ export default defineConfig({ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', + locale: 'it-IT', + timezoneId: 'Europe/Rome', }, /* Configure projects for major browsers */ From 89ecf83b12345892461068da683510c6e8ff5a6d Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 22 Dec 2024 13:13:38 +0100 Subject: [PATCH 08/13] chore(playwright): set timeout to 12s --- playwright.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/playwright.config.ts b/playwright.config.ts index 9847d39..aa442ae 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -15,6 +15,7 @@ const baseURL = 'http://localhost:4321/'; */ export default defineConfig({ testDir: './e2e-tests', + timeout: 12 * 1_000, /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ From b4e78717ddde300ecc4ab8528c78b09e0fea78ca Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 22 Dec 2024 13:14:10 +0100 Subject: [PATCH 09/13] chore(playwright): add meta elements tests --- e2e-tests/about.e2e.test.ts | 3 ++ e2e-tests/blog-index.e2e.test.ts | 2 + e2e-tests/hp.e2e.test.ts | 3 ++ e2e-tests/shared-e2e-tests.ts | 86 ++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+) diff --git a/e2e-tests/about.e2e.test.ts b/e2e-tests/about.e2e.test.ts index f3c32a1..a6395e2 100644 --- a/e2e-tests/about.e2e.test.ts +++ b/e2e-tests/about.e2e.test.ts @@ -4,6 +4,7 @@ import { expectHtmlLang, expectValidCountrySelector, expectValidFooter, + expectValidMeta, } from './shared-e2e-tests'; const urlMap = { @@ -17,6 +18,7 @@ test('italian about page is well formed', async ({ page }) => { await expectHtmlLang(page, lang); await expectValidFooter(page); await expectValidCountrySelector(page, lang, urlMap); + await expectValidMeta(page, lang); await expectH1(page, /About RomaJS/); }); @@ -27,6 +29,7 @@ test('english about page is well formed', async ({ page }) => { await expectHtmlLang(page, lang); await expectValidFooter(page); await expectValidCountrySelector(page, lang, urlMap); + await expectValidMeta(page, lang); await expectH1(page, /About RomaJS/); }); diff --git a/e2e-tests/blog-index.e2e.test.ts b/e2e-tests/blog-index.e2e.test.ts index 4206ad1..fe92fac 100644 --- a/e2e-tests/blog-index.e2e.test.ts +++ b/e2e-tests/blog-index.e2e.test.ts @@ -3,6 +3,7 @@ import { expectH1, expectHtmlLang, expectValidFooter, + expectValidMeta, } from './shared-e2e-tests'; test('blog index is well formed', async ({ page }) => { @@ -10,6 +11,7 @@ test('blog index is well formed', async ({ page }) => { const lang = 'it'; await expectHtmlLang(page, lang); await expectValidFooter(page); + await expectValidMeta(page, lang); await expectH1(page, /RomaJS Blog/); }); diff --git a/e2e-tests/hp.e2e.test.ts b/e2e-tests/hp.e2e.test.ts index 5c1d7d9..8be9523 100644 --- a/e2e-tests/hp.e2e.test.ts +++ b/e2e-tests/hp.e2e.test.ts @@ -4,6 +4,7 @@ import { expectHtmlLang, expectValidCountrySelector, expectValidFooter, + expectValidMeta, } from './shared-e2e-tests'; const urlMap = { @@ -17,6 +18,7 @@ test('italian HP is well formed', async ({ page }) => { await expectHtmlLang(page, lang); await expectValidFooter(page); await expectValidCountrySelector(page, lang, urlMap); + await expectValidMeta(page, lang); // Expect a title "to contain" a substring. await expect(page).toHaveTitle(/La tech community di Javascript su Roma/); @@ -31,6 +33,7 @@ test('english HP is well formed', async ({ page }) => { await expectHtmlLang(page, lang); await expectValidFooter(page); await expectValidCountrySelector(page, lang, urlMap); + await expectValidMeta(page, lang); await expect(page).toHaveTitle(/The Javascript community in Rome/); diff --git a/e2e-tests/shared-e2e-tests.ts b/e2e-tests/shared-e2e-tests.ts index e5332ee..275e7c8 100644 --- a/e2e-tests/shared-e2e-tests.ts +++ b/e2e-tests/shared-e2e-tests.ts @@ -82,3 +82,89 @@ export async function expectH1(page: Page, text: string | RegExp) { await h1.isVisible(); await expect(h1).toHaveText(text); } + +async function getOgAttributesByLang(page: Page, lang: ExpectedLang) { + const [title, description, localPageUrl] = await Promise.all([ + page.title(), + page.locator('head > meta[name="description"]').getAttribute('content'), + page.url(), + ]); + + const parsedUrl = new URL(localPageUrl); + parsedUrl.host = 'romajs.org'; + parsedUrl.protocol = 'https:'; + parsedUrl.port = ''; + const pageUrl = parsedUrl.href; + + switch (lang) { + case 'it': + return { + 'og:type': 'website', + 'og:url': pageUrl, + 'og:title': title, + 'og:description': description, + 'og:image': 'https://romajs.org/assets/og-img.png', + 'og:image:width': '200', + 'og:image:height': '200', + 'twitter:card': 'summary_large_image', + 'twitter:url': pageUrl, + 'twitter:title': title, + 'twitter:description': description, + 'twitter:image': 'https://romajs.org/assets/og-img.png', + 'twitter:image:alt': 'RomaJS, il meetup javascript di Roma', + }; + case 'en': + return { + 'og:type': 'website', + 'og:url': pageUrl, + 'og:title': title, + 'og:description': description, + 'og:image': 'https://romajs.org/assets/og-img.png', + 'og:image:width': '200', + 'og:image:height': '200', + 'twitter:card': 'summary_large_image', + 'twitter:url': pageUrl, + 'twitter:title': title, + 'twitter:description': description, + 'twitter:image': 'https://romajs.org/assets/og-img.png', + 'twitter:image:alt': 'RomaJS, il meetup javascript di Roma', + }; + default: { + throw new TypeError(`unsupported lang=${lang}`); + } + } +} + +/** + * Asserts that the meta attribute defined in are valid + * @param page + */ +export async function expectValidMeta(page: Page, lang: ExpectedLang) { + expect( + await page.locator('head > meta[charset]').getAttribute('charset') + ).toBe('utf-8'); + expect( + await page.locator('head > link[rel="icon"]').getAttribute('href') + ).toBe('/favicon.ico'); + + const ogMetaAttributesFound = Object.fromEntries( + ( + await Promise.all( + ( + await page.locator(`head > meta[property][content]`).all() + ).map((locator) => + Promise.all([ + locator.getAttribute('property'), + locator.getAttribute('content'), + ]) + ) + ) + ).filter((entry): entry is [string, string] => !!entry[0] && !!entry[1]) + ); + + const ogAttr = await getOgAttributesByLang(page, lang); + + for (const [property, content] of Object.entries(ogAttr)) { + expect(ogMetaAttributesFound[property]).toContain(content); + } +} From 3dbac7cf7b200fa32d87b584efff0020f0fe6e48 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 22 Dec 2024 14:30:22 +0100 Subject: [PATCH 10/13] fix(hp): link[rel="alternate"] missing in homepage --- src/i18n/config.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 2095111..7046feb 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -2,6 +2,7 @@ import type { I18nRouteParams, Lang } from './types'; import { t as i18nextTranslate } from 'i18next'; import type { TOptions } from 'i18next'; import type L10nMessages from '../../public/locales/it/translation.json'; +import { hpUrlMap } from 'utils/routing'; export const i18nLang = Object.freeze({ it: { @@ -48,6 +49,16 @@ export interface RelAlternateProps { export function generateLinkRelAlternateProps(url: URL): RelAlternateProps[] { const output: RelAlternateProps[] = []; + if (/^\/?$/.test(url.pathname)) { + // is index page ? + return [ + { + href: hpUrlMap.en, + hreflang: 'en', + }, + ]; + } + const result = routeParamsRegex.exec(url.pathname); if (!result || !result.groups) { From c9cc7423eb53567a95107f013b08a4003e025a96 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 22 Dec 2024 14:40:42 +0100 Subject: [PATCH 11/13] fix: broken favicon link --- src/components/BaseHead.astro | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/BaseHead.astro b/src/components/BaseHead.astro index 4e92a9d..8a03b2c 100644 --- a/src/components/BaseHead.astro +++ b/src/components/BaseHead.astro @@ -21,11 +21,10 @@ const linkRelAlternateProps = generateLinkRelAlternateProps(canonicalURL); - {title} - + From 6a3b702986b6bcc33467d0f5e7f47c31db98bbe1 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 22 Dec 2024 14:42:06 +0100 Subject: [PATCH 12/13] refactor(e2e): simplify tests --- e2e-tests/about.e2e.test.ts | 26 +++++------- e2e-tests/blog-index.e2e.test.ts | 15 +++---- e2e-tests/hp.e2e.test.ts | 34 ++++++---------- e2e-tests/shared-e2e-tests.ts | 57 +++++++++++++++++++++++++++ e2e-tests/upcoming-events.e2e.test.ts | 22 +++++++++++ 5 files changed, 104 insertions(+), 50 deletions(-) create mode 100644 e2e-tests/upcoming-events.e2e.test.ts diff --git a/e2e-tests/about.e2e.test.ts b/e2e-tests/about.e2e.test.ts index a6395e2..134fc5e 100644 --- a/e2e-tests/about.e2e.test.ts +++ b/e2e-tests/about.e2e.test.ts @@ -1,11 +1,5 @@ import { test } from '@playwright/test'; -import { - expectH1, - expectHtmlLang, - expectValidCountrySelector, - expectValidFooter, - expectValidMeta, -} from './shared-e2e-tests'; +import { expectH1, expectWellFormedPage } from './shared-e2e-tests'; const urlMap = { it: '/it/about', @@ -15,21 +9,19 @@ const urlMap = { test('italian about page is well formed', async ({ page }) => { await page.goto('./it/about'); const lang = 'it'; - await expectHtmlLang(page, lang); - await expectValidFooter(page); - await expectValidCountrySelector(page, lang, urlMap); - await expectValidMeta(page, lang); - await expectH1(page, /About RomaJS/); + await Promise.all([ + expectWellFormedPage(page, lang, urlMap), + expectH1(page, /About RomaJS/i), + ]); }); test('english about page is well formed', async ({ page }) => { await page.goto('./en/about'); const lang = 'en'; - await expectHtmlLang(page, lang); - await expectValidFooter(page); - await expectValidCountrySelector(page, lang, urlMap); - await expectValidMeta(page, lang); - await expectH1(page, /About RomaJS/); + await Promise.all([ + expectWellFormedPage(page, lang, urlMap), + expectH1(page, /About RomaJS/i), + ]); }); diff --git a/e2e-tests/blog-index.e2e.test.ts b/e2e-tests/blog-index.e2e.test.ts index fe92fac..5da2592 100644 --- a/e2e-tests/blog-index.e2e.test.ts +++ b/e2e-tests/blog-index.e2e.test.ts @@ -1,17 +1,12 @@ import { test } from '@playwright/test'; -import { - expectH1, - expectHtmlLang, - expectValidFooter, - expectValidMeta, -} from './shared-e2e-tests'; +import { expectH1, expectWellFormedPage } from './shared-e2e-tests'; test('blog index is well formed', async ({ page }) => { await page.goto('./blog'); const lang = 'it'; - await expectHtmlLang(page, lang); - await expectValidFooter(page); - await expectValidMeta(page, lang); - await expectH1(page, /RomaJS Blog/); + await Promise.all([ + expectWellFormedPage(page, lang, null), + expectH1(page, /RomaJS Blog/), + ]); }); diff --git a/e2e-tests/hp.e2e.test.ts b/e2e-tests/hp.e2e.test.ts index 8be9523..aef3af2 100644 --- a/e2e-tests/hp.e2e.test.ts +++ b/e2e-tests/hp.e2e.test.ts @@ -1,11 +1,5 @@ import { test, expect } from '@playwright/test'; -import { - expectH1, - expectHtmlLang, - expectValidCountrySelector, - expectValidFooter, - expectValidMeta, -} from './shared-e2e-tests'; +import { expectH1, expectWellFormedPage } from './shared-e2e-tests'; const urlMap = { it: '/', @@ -15,27 +9,21 @@ const urlMap = { test('italian HP is well formed', async ({ page }) => { await page.goto('./'); const lang = 'it'; - await expectHtmlLang(page, lang); - await expectValidFooter(page); - await expectValidCountrySelector(page, lang, urlMap); - await expectValidMeta(page, lang); - // Expect a title "to contain" a substring. - await expect(page).toHaveTitle(/La tech community di Javascript su Roma/); - - await expectH1(page, /La tech community di Javascript su Roma/); + await Promise.all([ + expectWellFormedPage(page, lang, urlMap), + expect(page).toHaveTitle(/La tech community di Javascript su Roma/i), + expectH1(page, /La tech community di Javascript su Roma/i), + ]); }); test('english HP is well formed', async ({ page }) => { await page.goto('./en'); const lang = 'en'; - await expectHtmlLang(page, lang); - await expectValidFooter(page); - await expectValidCountrySelector(page, lang, urlMap); - await expectValidMeta(page, lang); - - await expect(page).toHaveTitle(/The Javascript community in Rome/); - - await expectH1(page, /The Javascript community in Rome/); + await Promise.all([ + expectWellFormedPage(page, lang, urlMap), + expect(page).toHaveTitle(/The Javascript community in Rome/i), + expectH1(page, /The Javascript community in Rome/i), + ]); }); diff --git a/e2e-tests/shared-e2e-tests.ts b/e2e-tests/shared-e2e-tests.ts index 275e7c8..73ca41d 100644 --- a/e2e-tests/shared-e2e-tests.ts +++ b/e2e-tests/shared-e2e-tests.ts @@ -16,6 +16,38 @@ export async function expectHtmlLang(page: Page, expectedLang: ExpectedLang) { expect(lang).toBe(expectedLang); } +/** + * Asserts that page contains valid `link[rel="alternate"]` + * @param page + * @param expectedLang + * @param urlMap + */ +export async function expectValidLinkRelAlternate( + page: Page, + expectedLang: ExpectedLang, + urlMap: Record +) { + const alternateLinksByLang = Object.fromEntries( + ( + await Promise.all( + ( + await page.locator('head > link[rel="alternate"]').all() + ).map((loc) => + Promise.all([loc.getAttribute('hreflang'), loc.getAttribute('href')]) + ) + ) + ).filter((entry): entry is [string, string] => !!entry[0] && !!entry[1]) + ); + + for (const [lang, url] of Object.entries(urlMap)) { + if (lang === expectedLang) { + continue; + } + + expect(alternateLinksByLang[lang]).toContain(url); + } +} + /** * Asserts that page has valid footer * @param page @@ -146,6 +178,17 @@ export async function expectValidMeta(page: Page, lang: ExpectedLang) { expect( await page.locator('head > link[rel="icon"]').getAttribute('href') ).toBe('/favicon.ico'); + expect( + await page.locator('head > meta[name="viewport"]').getAttribute('content') + ).toBeTruthy(); + expect( + await page.locator('head > meta[name="title"]').getAttribute('content') + ).toBeTruthy(); + expect( + await page + .locator('head > meta[name="description"]') + .getAttribute('content') + ).toBeTruthy(); const ogMetaAttributesFound = Object.fromEntries( ( @@ -168,3 +211,17 @@ export async function expectValidMeta(page: Page, lang: ExpectedLang) { expect(ogMetaAttributesFound[property]).toContain(content); } } + +export async function expectWellFormedPage( + page: Page, + expectedLang: ExpectedLang, + urlMap: Record | null +) { + return Promise.all([ + expectHtmlLang(page, expectedLang), + expectValidFooter(page), + urlMap && expectValidCountrySelector(page, expectedLang, urlMap), + urlMap && expectValidLinkRelAlternate(page, expectedLang, urlMap), + expectValidMeta(page, expectedLang), + ]); +} diff --git a/e2e-tests/upcoming-events.e2e.test.ts b/e2e-tests/upcoming-events.e2e.test.ts new file mode 100644 index 0000000..8125f56 --- /dev/null +++ b/e2e-tests/upcoming-events.e2e.test.ts @@ -0,0 +1,22 @@ +import { test } from '@playwright/test'; +import { expectH1, expectWellFormedPage } from './shared-e2e-tests'; + +test('italian upcoming-events page is well formed', async ({ page }) => { + await page.goto('./it/prossimi-eventi/'); + const lang = 'it'; + + await Promise.all([ + expectWellFormedPage(page, lang, null), + expectH1(page, /Prossimi eventi/i), + ]); +}); + +test('english upcoming-events page is well formed', async ({ page }) => { + await page.goto('./en/upcoming-events/'); + const lang = 'en'; + + await Promise.all([ + expectWellFormedPage(page, lang, null), + expectH1(page, /upcoming events/i), + ]); +}); From ff5fa73892e37fbeb63bcb05b218c86bc00c5e53 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 22 Dec 2024 14:50:52 +0100 Subject: [PATCH 13/13] chore(e2e): add previous events page tests --- e2e-tests/previous-events.e2e.test.ts | 22 ++++++++++++++++++++++ e2e-tests/shared-e2e-tests.ts | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 e2e-tests/previous-events.e2e.test.ts diff --git a/e2e-tests/previous-events.e2e.test.ts b/e2e-tests/previous-events.e2e.test.ts new file mode 100644 index 0000000..6ff4dbb --- /dev/null +++ b/e2e-tests/previous-events.e2e.test.ts @@ -0,0 +1,22 @@ +import { test, expect } from '@playwright/test'; +import { expectH1, expectWellFormedPage } from './shared-e2e-tests'; + +test('italian previous-events page is well formed', async ({ page }) => { + await page.goto('./it/eventi-passati/1/'); + const lang = 'it'; + + await Promise.all([ + expectWellFormedPage(page, lang, null), + expectH1(page, /Eventi passati/i), + ]); +}); + +test('english previous-events page is well formed', async ({ page }) => { + await page.goto('./en/past-events/1/'); + const lang = 'en'; + + await Promise.all([ + expectWellFormedPage(page, lang, null), + expectH1(page, /Past Events/i), + ]); +}); diff --git a/e2e-tests/shared-e2e-tests.ts b/e2e-tests/shared-e2e-tests.ts index 73ca41d..5e798ef 100644 --- a/e2e-tests/shared-e2e-tests.ts +++ b/e2e-tests/shared-e2e-tests.ts @@ -177,7 +177,7 @@ export async function expectValidMeta(page: Page, lang: ExpectedLang) { ).toBe('utf-8'); expect( await page.locator('head > link[rel="icon"]').getAttribute('href') - ).toBe('/favicon.ico'); + ).toBe('/assets/favicon.ico'); expect( await page.locator('head > meta[name="viewport"]').getAttribute('content') ).toBeTruthy();