From bc65bfc3ddbdb9264cd18623fdb39ad868fbda86 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Fri, 6 Jun 2025 10:59:34 +0800 Subject: [PATCH] Simple e2e tests for logged-out cases --- .github/workflows/playwright.yml | 29 +++++++++++ .gitignore | 8 ++- package-lock.json | 83 ++++++++++++++++++++++++++++++-- package.json | 2 + playwright.config.js | 50 +++++++++++++++++++ tests/logged-out-view.spec.js | 37 ++++++++++++++ 6 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 playwright.config.js create mode 100644 tests/logged-out-view.spec.js diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000..9b1d12c2 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,29 @@ +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 ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx 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 0e0c6565..e5b0710b 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,10 @@ phanpy-dist.tar.gz sonda-report.html # Compiled locale files -src/locales/*.js \ No newline at end of file +src/locales/*.js + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/package-lock.json b/package-lock.json index b2a6a7b5..5f67faa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,7 +54,9 @@ "@lingui/babel-plugin-lingui-macro": "~5.3.2", "@lingui/cli": "~5.3.2", "@lingui/vite-plugin": "~5.3.2", + "@playwright/test": "~1.52.0", "@preact/preset-vite": "~2.10.1", + "@types/node": "~22.15.30", "postcss": "~8.5.4", "postcss-dark-theme-class": "~1.3.0", "postcss-preset-env": "~10.2.0", @@ -3682,6 +3684,22 @@ "moo": "^0.5.1" } }, + "node_modules/@playwright/test": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", + "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@preact/preset-vite": { "version": "2.10.1", "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.1.tgz", @@ -4160,11 +4178,14 @@ } }, "node_modules/@types/node": { - "version": "18.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", - "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", + "version": "22.15.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", + "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", "devOptional": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } }, "node_modules/@types/parse-json": { "version": "4.0.2", @@ -7752,6 +7773,53 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", + "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", + "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/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/pofile": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.4.tgz", @@ -9868,6 +9936,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", diff --git a/package.json b/package.json index c17623a5..dc4b1077 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,9 @@ "@lingui/babel-plugin-lingui-macro": "~5.3.2", "@lingui/cli": "~5.3.2", "@lingui/vite-plugin": "~5.3.2", + "@playwright/test": "~1.52.0", "@preact/preset-vite": "~2.10.1", + "@types/node": "~22.15.30", "postcss": "~8.5.4", "postcss-dark-theme-class": "~1.3.0", "postcss-preset-env": "~10.2.0", diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 00000000..c3b43a9b --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,50 @@ +// @ts-check +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') }); + +/** + * @see https://playwright.dev/docs/test-configuration + */ +export default defineConfig({ + testDir: './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: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* 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', + /* 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://localhost:5173', + + /* 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: 'Mobile Safari', + use: { ...devices['iPhone 13 Mini'] }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'npm run dev', + url: 'http://localhost:5173', + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/tests/logged-out-view.spec.js b/tests/logged-out-view.spec.js new file mode 100644 index 00000000..8b040583 --- /dev/null +++ b/tests/logged-out-view.spec.js @@ -0,0 +1,37 @@ +// @ts-check +import { expect, test } from '@playwright/test'; + +test('has welcome page', async ({ page }) => { + await page.goto('/'); + await expect(page.locator('#welcome')).toBeVisible(); +}); + +test('loads post page and works', async ({ page }) => { + await page.route('**/api/v1/statuses/123', async (route) => { + await route.fulfill({ + json: { + id: '123', + created_at: '2024-01-01T12:00:00.000Z', + account: { + id: '1', + username: 'testuser', + display_name: 'Test User', + acct: 'testuser@test.social', + }, + content: '

This is a test post

', + }, + }); + }); + + await page.route('**/api/v1/statuses/123/context', async (route) => { + await route.fulfill({ + json: { + ancestors: [], + descendants: [], + }, + }); + }); + + await page.goto('/#/test.social/s/123'); + await expect(page.locator('text=This is a test post')).toBeVisible(); +});