From 0b0de69ecc4b49e228865504d5474a4603a5c77d Mon Sep 17 00:00:00 2001 From: Lenny Peters Date: Wed, 18 Dec 2024 20:17:55 -0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adding=20playwright?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/githubactions.yml | 52 ++++++------ .github/workflows/playwright.yml | 27 +++++++ .gitignore | 5 ++ package-lock.json | 119 ++++++++++++++++++++++++++-- package.json | 9 ++- playwright.config.js | 78 ++++++++++++++++++ tests/example.spec.js | 22 +++++ 7 files changed, 276 insertions(+), 36 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 playwright.config.js create mode 100644 tests/example.spec.js diff --git a/.github/workflows/githubactions.yml b/.github/workflows/githubactions.yml index e3cd60b..668ba50 100644 --- a/.github/workflows/githubactions.yml +++ b/.github/workflows/githubactions.yml @@ -9,15 +9,15 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x] + node-version: [20] steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: "**/node_modules" key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }} - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "npm" @@ -27,16 +27,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x] + node-version: [20] needs: [build] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: '**/node_modules' key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} @@ -51,16 +51,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x] + node-version: [20] needs: [build] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: '**/node_modules' key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} @@ -73,16 +73,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x] + node-version: [20] needs: [build] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: '**/node_modules' key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} @@ -102,7 +102,7 @@ jobs: runs-on: ubuntu-latest needs: [build] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get main @@ -122,13 +122,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x] + node-version: [20] needs: [build] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20 - run: npm install && npm install -g @lhci/cli@0.12.x - run: npm run build - run: lhci autorun @@ -138,11 +138,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x] + node-version: [20] needs: [lint, test, e2e_visual_a11y, lighthouseci, sonarcloud] steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built. run: | diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..3eb1314 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +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 192148a..36369af 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,8 @@ yarn-error.log* # IDE's .vscode .idea +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/package-lock.json b/package-lock.json index a98ebb8..e67debf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "MIT", "dependencies": { "@analytics/google-analytics": "^1.0.7", + "@axe-core/playwright": "^4.10.1", + "@percy/playwright": "^1.0.7", "analytics": "^0.8.9", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -26,6 +28,7 @@ "@commitlint/config-conventional": "^17.7.0", "@percy/cli": "^1.27.2", "@percy/cypress": "^3.1.2", + "@playwright/test": "^1.49.1", "@storybook/addon-actions": "^7.4.6", "@storybook/addon-essentials": "^7.4.6", "@storybook/addon-links": "^7.4.6", @@ -34,6 +37,7 @@ "@testing-library/cypress": "^10.0.1", "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", + "@types/node": "^22.10.2", "autoprefixer": "^10.4.16", "axe-core": "^4.8.2", "babel-loader": "^9.1.3", @@ -173,6 +177,18 @@ "resolved": "https://registry.npmjs.org/@analytics/type-utils/-/type-utils-0.6.2.tgz", "integrity": "sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==" }, + "node_modules/@axe-core/playwright": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.10.1.tgz", + "integrity": "sha512-EV5t39VV68kuAfMKqb/RL+YjYKhfuGim9rgIaQ6Vntb2HgaCaau0h98Y3WEUqW1+PbdzxDtDNjFAipbtZuBmEA==", + "license": "MPL-2.0", + "dependencies": { + "axe-core": "~4.10.2" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -5322,6 +5338,18 @@ "node": ">=14" } }, + "node_modules/@percy/playwright": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@percy/playwright/-/playwright-1.0.7.tgz", + "integrity": "sha512-eHRnbMcBqNB9krGUoSCFjr9Su4Njk1NjDquEgZ4zYzOzjoeoNdnvnXX+EFN5iNwiyiBuXk9qLxF1Bfi86ccyiA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "playwright-core": ">=1" + } + }, "node_modules/@percy/sdk-utils": { "version": "1.27.2", "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.27.2.tgz", @@ -5404,6 +5432,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@playwright/test": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@radix-ui/number": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", @@ -8121,10 +8165,14 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.18.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.3.tgz", - "integrity": "sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==", - "dev": true + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/@types/node-fetch": { "version": "2.6.6", @@ -9306,10 +9354,10 @@ "dev": true }, "node_modules/axe-core": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.2.tgz", - "integrity": "sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==", - "dev": true, + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "license": "MPL-2.0", "engines": { "node": ">=4" } @@ -11076,6 +11124,16 @@ "cypress": "^10 || ^11 || ^12 || ^13" } }, + "node_modules/cypress/node_modules/@types/node": { + "version": "18.19.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz", + "integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/cypress/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -11188,6 +11246,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/cypress/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, + "license": "MIT" + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -19811,6 +19876,37 @@ "node": ">=10" } }, + "node_modules/playwright": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/polished": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz", @@ -23489,6 +23585,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index f1280c2..e99b17c 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,15 @@ "coverage": "jest --coverage --testResultsProcessor=jest-sonar-reporter", "browserupdate-coverage": "npx browserslist@latest && npm run coverage", "format": "npm run pretty -- --write", - "test": "jest", + "test": "jest --testPathPattern=src/*", "lint": "eslint --ignore-path .gitignore --ext .js --ext .jsx .", "pretty": "prettier --ignore-path .gitignore \"**/*.+(js|json|md)\"", "check-format": "npm run pretty -- --list-different", "precommit": "npm-run-all --parallel lint pretty", "validate": "npm-run-all --parallel check-format lint", "cy:run": "percy exec -- npx cypress run", - "e2e:test": "npx browserslist@latest && start-server-and-test start http://localhost:9000 cy:run", + "play:run": "npx playwright test", + "e2e:test": "npx browserslist@latest && start-server-and-test start http://localhost:9000 play:run", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "prepare": "husky install" @@ -28,6 +29,8 @@ "license": "MIT", "dependencies": { "@analytics/google-analytics": "^1.0.7", + "@axe-core/playwright": "^4.10.1", + "@percy/playwright": "^1.0.7", "analytics": "^0.8.9", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -44,6 +47,7 @@ "@commitlint/config-conventional": "^17.7.0", "@percy/cli": "^1.27.2", "@percy/cypress": "^3.1.2", + "@playwright/test": "^1.49.1", "@storybook/addon-actions": "^7.4.6", "@storybook/addon-essentials": "^7.4.6", "@storybook/addon-links": "^7.4.6", @@ -52,6 +56,7 @@ "@testing-library/cypress": "^10.0.1", "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", + "@types/node": "^22.10.2", "autoprefixer": "^10.4.16", "axe-core": "^4.8.2", "babel-loader": "^9.1.3", diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..f287aed --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,78 @@ +// @ts-check +const { defineConfig, devices } = require("@playwright/test"); + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config({ path: path.resolve(__dirname, '.env') }); + +/** + * @see https://playwright.dev/docs/test-configuration + */ +module.exports = 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://127.0.0.1:3000', + + /* 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: "firefox", + use: { ...devices["Desktop Firefox"] }, + }, + + { + name: "webkit", + use: { ...devices["Desktop Safari"] }, + }, + + /* 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: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/tests/example.spec.js b/tests/example.spec.js new file mode 100644 index 0000000..6af3fc9 --- /dev/null +++ b/tests/example.spec.js @@ -0,0 +1,22 @@ +import { test, expect } from "@playwright/test"; +const { AxeBuilder } = require("@axe-core/playwright"); +const percySnapshot = require("@percy/playwright"); + +test("test", async ({ page }) => { + await page.goto("http://localhost:9000/"); + await percySnapshot(page, "Setup Example ScreenShort for Percy"); + try { + const results = await new AxeBuilder({ page }).analyze(); + console.log(results); + } catch (e) { + // do something with the error + } + await expect( + page.getByRole("heading", { name: "Welcome to Setup Example" }), + ).toBeVisible(); + await expect(page.getByRole("paragraph")).toContainText("0"); + await page.getByRole("button", { name: "+" }).click(); + await page.getByText("1").click(); + await page.getByRole("button", { name: "-" }).click(); + await page.getByText("0").click(); +});