From e90f9a9d46b24cbe1ae4bc82575ab4be1c1cb193 Mon Sep 17 00:00:00 2001 From: Kohei Watanabe Date: Sat, 16 Mar 2024 16:32:53 +0900 Subject: [PATCH] add test workflow (#38) --- .github/workflows/test.yml | 31 ++++++++++++++++ .gitignore | 2 + package-lock.json | 76 ++++++++++++++++++++++++++++++++++++++ package.json | 3 ++ playwright.config.ts | 39 +++++++++++++++++++ tests/jwk.test.ts | 43 +++++++++++++++++++++ 6 files changed, 194 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 playwright.config.ts create mode 100644 tests/jwk.test.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b93b220 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: test +on: pull_request +jobs: + test: + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + 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 tests + shell: bash + run: BASE_URL="https://$(echo "${HEAD_REF}" | sed s/[^_0-9a-z]/-/gi).jwk.pages.dev/" npm test + env: + HEAD_REF: ${{ github.head_ref }} + - uses: actions/upload-artifact@v4 + if: always() + with: + name: "playwright-report-${{ matrix.os }}" + path: playwright-report diff --git a/.gitignore b/.gitignore index f06235c..53fce2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules dist +playwright-report +test-results diff --git a/package-lock.json b/package-lock.json index 1cbf0d4..ba1934d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "MIT", "devDependencies": { "@exampledev/new.css": "^1.1.3", + "@playwright/test": "^1.42.1", + "@types/node": "^20.11.28", "esbuild": "^0.20.0", "jose": "^5.0.0" } @@ -388,6 +390,30 @@ "integrity": "sha512-qhbGfqBRwUlM6MCSaJdUfjq86opNCMvM+6kVvs6S0kYhy0V8dKbe4rDMIklEJGuMc5QH5OuPjdCReu9I0tim2w==", "dev": true }, + "node_modules/@playwright/test": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", + "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "dev": true, + "dependencies": { + "playwright": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@types/node": { + "version": "20.11.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", + "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/esbuild": { "version": "0.20.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", @@ -426,6 +452,20 @@ "@esbuild/win32-x64": "0.20.1" } }, + "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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/jose": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.3.tgz", @@ -434,6 +474,42 @@ "funding": { "url": "https://github.com/sponsors/panva" } + }, + "node_modules/playwright": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", + "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "dev": true, + "dependencies": { + "playwright-core": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", + "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true } } } diff --git a/package.json b/package.json index ec84bb7..8ba3ef2 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,14 @@ "author": "Kohei Watanabe ", "license": "MIT", "scripts": { + "test": "playwright test", "build": "esbuild --bundle --loader:.html=copy --outdir=dist src/*", "start": "esbuild --bundle --loader:.html=copy --outdir=dist --servedir=dist src/*" }, "devDependencies": { "@exampledev/new.css": "^1.1.3", + "@playwright/test": "^1.42.1", + "@types/node": "^20.11.28", "esbuild": "^0.20.0", "jose": "^5.0.0" } diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..9e37448 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,39 @@ +import { defineConfig, devices } from "@playwright/test"; + +const baseURL = process.env.BASE_URL ?? "http://127.0.0.1:8000"; + +export default defineConfig({ + testDir: "tests", + fullyParallel: true, + forbidOnly: Boolean(process.env.CI), + timeout: 10_000, + retries: 2, + workers: process.env.CI ? 1 : undefined, + reporter: "html", + use: { + baseURL, + trace: "on-first-retry", + video: "on-first-retry", + }, + projects: [ + { + name: "chromium", + use: devices["Desktop Chrome"], + }, + { + name: "firefox", + use: devices["Desktop Firefox"], + }, + { + name: "webkit", + use: devices["Desktop Safari"], + }, + ], + webServer: { + url: baseURL, + command: "npm start", + stdout: "ignore", + stderr: "ignore", + reuseExistingServer: true, + }, +}); diff --git a/tests/jwk.test.ts b/tests/jwk.test.ts new file mode 100644 index 0000000..33c118a --- /dev/null +++ b/tests/jwk.test.ts @@ -0,0 +1,43 @@ +import { test, expect } from "@playwright/test"; + +// prettier-ignore +const properties = { + "ECDSA (ES256)": ["alg", "use", "kid", "kty", "crv", "x", "y", "d"], + "ECDSA (ES384)": ["alg", "use", "kid", "kty", "crv", "x", "y", "d"], + "ECDSA (ES512)": ["alg", "use", "kid", "kty", "crv", "x", "y", "d"], + "RSASSA-PKCS1-v1_5 (RS256)": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "RSASSA-PKCS1-v1_5 (RS384)": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "RSASSA-PKCS1-v1_5 (RS512)": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "RSASSA-PSS (PS256)": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "RSASSA-PSS (PS384)": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "RSASSA-PSS (PS512)": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "ECDH-ES": ["alg", "use", "kid", "kty", "crv", "x", "y", "d"], + "ECDH-ES+A128KW": ["alg", "use", "kid", "kty", "crv", "x", "y", "d"], + "ECDH-ES+A192KW": ["alg", "use", "kid", "kty", "crv", "x", "y", "d"], + "ECDH-ES+A256KW": ["alg", "use", "kid", "kty", "crv", "x", "y", "d"], + "RSA-OAEP": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "RSA-OAEP-256": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "RSA-OAEP-384": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], + "RSA-OAEP-512": ["alg", "use", "kid", "kty", "e", "n", "d", "p", "q", "dp", "dq", "qi"], +}; +// prettier-ignore-end + +for (const [algorithm, expected] of Object.entries(properties)) { + test(`${algorithm} has ${expected}`, async ({ page }) => { + await page.goto("/"); + await page.getByLabel("Algorithm").selectOption({ label: algorithm }); + await page.getByLabel("Generate").click(); + + const jwk = await page + .getByLabel("Private Key") + .getByText(/./) + .inputValue() + .then(JSON.parse); + + for (const property of expected) { + expect(jwk).toHaveProperty(property, expect.stringMatching(/./)); + } + + expect(Object.keys(jwk)).toHaveLength(expected.length); + }); +}