diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000..921d7179 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,33 @@ +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: Start Projects + run: | + npm run build -- --no-cache + npm start + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Sleep For 30 Seconds + run: sleep 30 + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index c5dc7b37..34d1c43f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,9 @@ apps/stock-price-checker-proxy/cache/ apps/twitch-proxy/.data/ apps/twitch-proxy/.logs/ Caddyfile -.eslintcache \ No newline at end of file +.eslintcache + +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/e2e/cash-register.test.js b/e2e/cash-register.test.js new file mode 100644 index 00000000..89372bfa --- /dev/null +++ b/e2e/cash-register.test.js @@ -0,0 +1,16 @@ +const { test, expect } = require('@playwright/test'); +const fs = require('fs'); +const path = require('path'); + +const portMap = require('../port-map.json'); + +const name = fs + .readdirSync(path.join(__dirname, '../apps')) + .filter(name => name === 'cash-register')[0]; + +const portNum = portMap[name]; + +test('has title', async ({ page }) => { + await page.goto(`http://localhost:${portNum}`); + await expect(page).toHaveTitle(/Cash Register/); +}); diff --git a/package-lock.json b/package-lock.json index 3f3ec92c..77a0253e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,9 @@ "dotenv": "^16.0.3" }, "devDependencies": { + "@playwright/test": "^1.46.1", "@types/jest": "^29.5.10", + "@types/node": "^22.5.2", "axios": "1.7.4", "eslint-config-prettier": "8.7.0", "eslint-plugin-react": "7.34.0", @@ -1243,6 +1245,21 @@ "node": ">= 8" } }, + "node_modules/@playwright/test": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz", + "integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==", + "dev": true, + "dependencies": { + "playwright": "1.46.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1352,10 +1369,13 @@ } }, "node_modules/@types/node": { - "version": "18.15.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", - "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", - "dev": true + "version": "22.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.2.tgz", + "integrity": "sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } }, "node_modules/@types/stack-utils": { "version": "2.0.1", @@ -5544,6 +5564,50 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz", + "integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==", + "dev": true, + "dependencies": { + "playwright-core": "1.46.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz", + "integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==", + "dev": true, + "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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -6454,6 +6518,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", diff --git a/package.json b/package.json index 92a0471b..b25c0fc9 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,14 @@ "start": "docker compose up -d", "stop": "docker compose down", "prepare": "husky install", - "test": "jest" + "test": "jest", + "playwright:run": "playwright test", + "playwright:watch": "playwright test --ui" }, "devDependencies": { + "@playwright/test": "^1.46.1", "@types/jest": "^29.5.10", + "@types/node": "^22.5.2", "axios": "1.7.4", "eslint-config-prettier": "8.7.0", "eslint-plugin-react": "7.34.0", diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 00000000..49b63604 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,77 @@ +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: './e2e', + /* 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, + // }, +});