diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..28d2f41 --- /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 install -g pnpm && pnpm install + - name: Install Playwright Browsers + run: pnpm exec playwright install --with-deps + - name: Run Playwright E2E tests + run: pnpm e2e + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 69dc02e..d8dbbad 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,10 @@ auto-imports.d.ts # Produced when building or packing manifest.json -web-ext-artifacts \ No newline at end of file +web-ext-artifacts + +# Test +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/e2e/diff.spec.ts b/e2e/diff.spec.ts new file mode 100644 index 0000000..786ac73 --- /dev/null +++ b/e2e/diff.spec.ts @@ -0,0 +1,24 @@ +import { test, expect } from "./fixtures"; + +test("Goto github PR files changed and check diff", async ({ page }) => { + await page.goto( + "https://github.com/bancorprotocol/carbon-app/pull/1464/files" + ); + const firstIframe = page.frameLocator("iframe").first(); + const iframeBody = firstIframe.locator("body"); + + await expect(iframeBody).toContainText("Difference"); + await expect(iframeBody).toContainText("Overlay"); +}); + +test("Check settings page contains right header and labels", async ({ + page, + extensionId, +}) => { + await page.goto(`chrome-extension://${extensionId}/src/popup/index.html`); + await expect(page.locator("h1")).toContainText("Settings"); + expect(page.locator("form > label")).toHaveText([ + "Diff Color", + "Default Algo", + ]); +}); diff --git a/e2e/fixtures.ts b/e2e/fixtures.ts new file mode 100644 index 0000000..28c04c0 --- /dev/null +++ b/e2e/fixtures.ts @@ -0,0 +1,34 @@ +import { test as base, chromium, type BrowserContext } from "@playwright/test"; +import path from "path"; +import { fileURLToPath } from "url"; +import { resolve } from "node:path"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const rootPath = resolve(__dirname, ".."); + +export const test = base.extend<{ + context: BrowserContext; + extensionId: string; +}>({ + context: async ({}, use) => { + const pathToExtension = path.join(rootPath, "dist"); + const context = await chromium.launchPersistentContext("", { + headless: false, + args: [ + `--disable-extensions-except=${pathToExtension}`, + `--load-extension=${pathToExtension}`, + ], + }); + await use(context); + await context.close(); + }, + extensionId: async ({ context }, use) => { + let [background] = context.serviceWorkers(); + if (!background) background = await context.waitForEvent("serviceworker"); + + const extensionId = background.url().split("/")[2]; + await use(extensionId); + }, +}); +export const expect = test.expect; diff --git a/package.json b/package.json index bf78473..a21395d 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,13 @@ "dev:chromium": "concurrently \"pnpm build:chromium -- -- --watch\" \"pnpm start:chromium\"", "pack:firefox": "web-ext build -s dist --overwrite-dest", "lint": "web-ext lint -s dist", - "clear": "rimraf --glob dist/src dist/manifest.json dist.*" + "clear": "rimraf --glob dist/src dist/manifest.json dist.*", + "e2e": "pnpm build:chromium && pnpm playwright test" }, "devDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", + "@playwright/test": "^1.47.2", "@types/fs-extra": "^11.0.4", "@types/node": "^20.12.7", "@types/pixelmatch": "^5.2.6", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..6720c8a --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,52 @@ +/** + * @see {@link https://playwright.dev/docs/chrome-extensions Chrome extensions | Playwright} + */ + +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: "./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"] }, + }, + ], + // webServer: { + // command: "pnpm dev", + // // start e2e test after the Vite server is fully prepared + // url: "http://localhost:3303/popup/main.ts", + // reuseExistingServer: true, + // }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 709d940..7b4c66f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,9 @@ importers: '@commitlint/config-conventional': specifier: ^19.2.2 version: 19.2.2 + '@playwright/test': + specifier: ^1.47.2 + version: 1.47.2 '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 @@ -514,6 +517,11 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@playwright/test@1.47.2': + resolution: {integrity: sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==} + engines: {node: '>=18'} + hasBin: true + '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} @@ -1244,6 +1252,11 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1878,6 +1891,16 @@ packages: pkg-types@1.2.0: resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} + playwright-core@1.47.2: + resolution: {integrity: sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.47.2: + resolution: {integrity: sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==} + engines: {node: '>=18'} + hasBin: true + pngjs@6.0.0: resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} engines: {node: '>=12.13.0'} @@ -2788,6 +2811,10 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@playwright/test@1.47.2': + dependencies: + playwright: 1.47.2 + '@pnpm/config.env-replace@1.1.0': {} '@pnpm/network.ca-file@1.0.2': @@ -3616,6 +3643,9 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -4200,6 +4230,14 @@ snapshots: mlly: 1.7.1 pathe: 1.1.2 + playwright-core@1.47.2: {} + + playwright@1.47.2: + dependencies: + playwright-core: 1.47.2 + optionalDependencies: + fsevents: 2.3.2 + pngjs@6.0.0: {} postcss@8.4.38: diff --git a/tsconfig.json b/tsconfig.json index 8c9f20b..5c37474 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,5 +21,5 @@ "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true }, - "include": ["src"] + "include": ["src", "e2e"] }