diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6ba59bb..d081e332 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,3 +33,25 @@ jobs: - name: Validate generated site run: bundle exec htmlproofer ./_site --disable-external --no-enforce-https --allow-missing-href --ignore-urls '/^\/\//' + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install Playwright dependencies + run: npm ci + + - name: Install Playwright browser + run: npx playwright install --with-deps chromium + + - name: Run Playwright smoke tests + run: npm run test:e2e + + - name: Upload Playwright report + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: playwright-report/ diff --git a/.gitignore b/.gitignore index f0851f0a..fff49932 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ vendor # Script output tmp/pending_updates.yml +node_modules +playwright-report +test-results diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..f3922a8a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,76 @@ +{ + "name": "testingconferences.github.io", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "testingconferences.github.io", + "devDependencies": { + "@playwright/test": "1.62.1" + } + }, + "node_modules/@playwright/test": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..d732e97f --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "testingconferences.github.io", + "private": true, + "scripts": { + "test:e2e": "playwright test" + }, + "devDependencies": { + "@playwright/test": "1.62.1" + } +} diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 00000000..63d87373 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,14 @@ +const { defineConfig } = require('@playwright/test'); + +module.exports = defineConfig({ + testDir: './test/playwright', + reporter: process.env.CI ? [['html', { open: 'never' }], ['github']] : 'list', + use: { + baseURL: 'http://127.0.0.1:4173' + }, + webServer: { + command: 'python3 -m http.server 4173 --bind 127.0.0.1 --directory _site', + port: 4173, + reuseExistingServer: !process.env.CI + } +}); diff --git a/test/playwright/site-smoke.spec.js b/test/playwright/site-smoke.spec.js new file mode 100644 index 00000000..31b9a49d --- /dev/null +++ b/test/playwright/site-smoke.spec.js @@ -0,0 +1,26 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('site smoke checks', () => { + test('API: key pages return success responses', async ({ request, baseURL }) => { + for (const path of ['/', '/past/', '/subscribe/']) { + const response = await request.get(`${baseURL}${path}`); + expect(response.ok(), `${path} should return success`).toBeTruthy(); + } + }); + + test('UI: homepage renders nav links', async ({ page }) => { + await page.goto('/'); + + await expect(page).toHaveTitle(/Software Testing Conferences/); + await expect(page.getByRole('link', { name: 'Upcoming' })).toBeVisible(); + await expect(page.getByRole('link', { name: 'Past' })).toBeVisible(); + await expect(page.getByRole('link', { name: 'Newsletter', exact: true })).toBeVisible(); + }); + + test('UI: past page lists conferences', async ({ page }) => { + await page.goto('/past/'); + + await expect(page).toHaveURL(/\/past\/$/); + await expect(page.locator('ul.post-list li').first()).toBeVisible(); + }); +});