diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..467190b --- /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: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 3f7859f..bc4e557 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,7 @@ dist/ /test-results/ /playwright-report/ /playwright/.cache/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..301801e --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,77 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * 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://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/logger.spec.ts b/tests/logger.spec.ts new file mode 100644 index 0000000..08e1d8e --- /dev/null +++ b/tests/logger.spec.ts @@ -0,0 +1,152 @@ +import { expect, test } from "@playwright/test"; +import { Logger } from "../src/Logger"; + +// Can't use tampermonkey since the tampermonkey functions are not defined in the testing suite +test("Constructors", () => { + const _logger0 = new Logger(); + const _logger1 = new Logger({}); + + const _logger2 = new Logger({ + outputs: { + tampermonkey: { + enabled: false, + maxBuckets: 0, + bucketIndexKey: "", + }, + }, + }); + const _logger3 = new Logger({ + outputs: { + console: { + enabled: false, + }, + }, + }); + const _logger4 = new Logger({ outputs: { console: { enabled: true } } }); + const _logger5 = new Logger({ + outputs: { console: { enabled: false }, tampermonkey: { enabled: false } }, + }); + const _logger6 = new Logger({ outputs: { callback: () => {} } }); + const _logger7 = new Logger({ + outputs: { + console: { enabled: true }, + tampermonkey: { enabled: false }, + callback: () => {}, + }, + }); +}); + +test("Log Messages", () => { + let logs = ""; + const logger = new Logger({ + outputs: { + tampermonkey: { enabled: false }, + console: { enabled: false }, + callback: (message) => { + logs += message + "\n"; + }, + }, + }); + logger.log("logging a"); + + logger.trace("logging b"); + logger.debug("logging c"); + logger.info("logging d"); + logger.warn("logging e"); + logger.fatal("logging f"); + + const lines = logs.split("\n"); + expect(lines[0]).toContain("fatal"); + expect(lines[0]).toContain("logging a"); + + expect(lines[1]).toContain("trace"); + expect(lines[1]).toContain("logging b"); + + expect(lines[2]).toContain("debug"); + expect(lines[2]).toContain("logging c"); + + expect(lines[3]).toContain("info"); + expect(lines[3]).toContain("logging d"); + + expect(lines[4]).toContain("warn"); + expect(lines[4]).toContain("logging e"); + + expect(lines[5]).toContain("fatal"); + expect(lines[5]).toContain("logging f"); + + expect(lines.length).toBe(7); +}); + +test("Log messages with context", () => { + let logs = ""; + const logger = new Logger({ + outputs: { + console: { enabled: false }, + callback: (message) => { + logs += message + "\n"; + }, + }, + }); + + logger.debug("test context", { data: "hello world" }); + const req = new Request("https://www.github.com"); + + logger.info("test context", { + request: req, + }); + + const lines = logs.split("\n"); + expect(lines[0]).toContain('{"level":20,"data":"hello world"}'); + expect(lines[1]).toContain("https://www.github.com"); +}); + +test("Logs contain timestamps", () => { + let logs = ""; + const logger = new Logger({ + outputs: { + console: { enabled: false }, + callback: (message) => { + logs += message + "\n"; + }, + }, + }); + + logger.log("test"); + + expect(logs).toMatch(/^[0-9]+-[0-9]+-[0-9]+T[0-9]+:[0-9]+:[0-9]+.[0-9]+Z/gm); +}); + +test("Overflow logs buffer", () => { + const logger = new Logger({ + bufferCapacity: 100, + outputs: { console: { enabled: false } }, + }); + + for (let i = 0; i < 100; i++) { + logger.log(`Iteration: ${i}`); + } + + logger.log("done"); +}); + +test("Export logs without tampermonkey", async () => { + const logger = new Logger({ outputs: { console: { enabled: false } } }); + for (let i = 0; i < 10; i++) { + logger.log(`Iteration: ${i}`); + } + + const export_one = await logger.export(1); + + expect(export_one.length).toBe(1); + expect(export_one[0]).toContain("Iteration: 9"); + + const export_five = await logger.export(5); + + expect(export_five.length).toBe(5); + expect(export_five[3]).toContain("Iteration: 8"); + + const export_all = await logger.export(100); + + expect(export_all.length).toBe(10); + expect(export_all[9]).toContain("Iteration: 9"); +});