Skip to content

Commit 196c365

Browse files
committed
feat(webpage-observer): add workspace project
1 parent e65e586 commit 196c365

16 files changed

Lines changed: 281 additions & 1 deletion

.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ pnpm-lock.yaml
77
# coverage
88
**/.nyc_output/**
99
**/coverage/**
10+
11+
# Playwright
12+
**/playwright-html-report/**
13+
**/test-results/**
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
extends: ['@patricktree/eslint-config/eslint-ecma.cjs'],
3+
parserOptions: {
4+
tsconfigRootDir: __dirname,
5+
},
6+
ignorePatterns: ['**/playwright-html-report/**'],
7+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
playwright-html-report
2+
test-results
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Agent Guide for `@patricktree/webpage-observer`
2+
3+
- Source lives in `src/` (TypeScript) and compiles to `dist/`; edit TS only and run `pnpm run build` (tsc via `tsconfig.project.json`) to refresh the JS Playwright uses.
4+
- Use `pnpm` (workspace root has the lockfile). Package scripts: `pnpm run build`, `pnpm run dev` (watch), `pnpm run lint` / `pnpm run lint:fix`, `pnpm run nuke`/`nuke:artifacts`.
5+
- Playwright tests run from compiled JS: `pnpm start` pulls `mcr.microsoft.com/playwright:v1.56.0-noble` and executes `playwright test --config ./dist/playwright.config.js`. Build first if `dist/` is stale. Run e.g. `pnpm start './src/sentry.spec.ts'` to run tests just for this file (seems like Playwright uses the source file names to filter by file name).
6+
- Playwright file filters are regexes against test file paths; use file-based patterns like `jsnation` instead of test titles or URLs like `jsnation.com`.
7+
- Default test browser runs inside a Docker “Playwright Server” container (host networking, X11 forwarded when not CI). Docker must be available; tests need outbound internet to reach the target sites.
8+
- Path alias `#pkg/*` maps to `./src/*`; keep snapshots under `snapshots/` (see `snapshotPathTemplate` in `src/playwright.config.ts`). Update screenshots intentionally (`--update-snapshots`) and expect EU/Vienna locale/timezone defaults.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "@patricktree/webpage-observer",
3+
"private": true,
4+
"type": "module",
5+
"imports": {
6+
"#pkg/*": "./dist/*"
7+
},
8+
"scripts": {
9+
"build": "superturbo run turbo:build",
10+
"dev": "pnpm run build --watch --preserveWatchOutput",
11+
"internal:compile": "tsc --build ./tsconfig.json",
12+
"lint": "superturbo run turbo:lint",
13+
"lint:file": "eslint --max-warnings 0",
14+
"lint:fix": "superturbo run turbo:lint:fix",
15+
"nuke": "pnpm run nuke:artifacts && del-cli node_modules",
16+
"nuke:artifacts": "del-cli dist \"*.tsbuildinfo\"",
17+
"start": "docker pull mcr.microsoft.com/playwright:v1.57.0-noble && playwright test --config ./dist/playwright.config.js",
18+
"turbo:build": "pnpm run internal:compile",
19+
"turbo:lint": "pnpm run lint:file .",
20+
"turbo:lint:fix": "pnpm run lint:file . --fix"
21+
},
22+
"dependencies": {
23+
"@patricktree/commons-node": "workspace:^",
24+
"@playwright/test": "1.57.0",
25+
"type-fest": "^5.0.0"
26+
},
27+
"devDependencies": {
28+
"@types/node": "^20"
29+
}
30+
}
73.2 KB
Loading
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* eslint-disable n/no-process-env -- configs are the only place where reading from process.env is allowed */
2+
3+
export const config = {
4+
CI: process.env['CI'],
5+
6+
/**
7+
* if `--inspect-publish-uid` is in env variable `NODE_OPTIONS` - which is a Node.js feature to
8+
* allow easier debugging - it indicates that the current `playwright test` run is debugged by an
9+
* engineer (e.g. via VS Code Debug Terminal)
10+
*/
11+
isDebuggingSession: process.env['NODE_OPTIONS']?.includes('--inspect-publish-uid'),
12+
13+
isPlaywrightStartedWithDebug: process.env['PWDEBUG'] === '1',
14+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { config } from '#pkg/config.js';
2+
3+
export const playwrightBrowserConfig =
4+
/*
5+
* `--debug` does not correctly work when Playwright is running browsers in the Playwright Server Docker container --> run Playwright browsers locally instead
6+
*/
7+
config.isPlaywrightStartedWithDebug
8+
? ({ browser: 'local' } as const)
9+
: ({ browser: 'docker', port: 36_719 } as const);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
test('jsnation.com tickets', async ({ page }) => {
4+
const baseURL = new URL('https://jsnation.com/');
5+
await page.goto(baseURL.href, { waitUntil: 'domcontentloaded' });
6+
await page.pause();
7+
8+
const ticketsPrices = page.locator('#tickets > div > div.prices');
9+
await expect(ticketsPrices).toBeVisible({ timeout: 60_000 });
10+
await expect.soft(ticketsPrices).toHaveScreenshot();
11+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import '#pkg/set-env.js';
2+
3+
import { devices, type PlaywrightTestConfig, type ReporterDescription } from '@playwright/test';
4+
import os from 'node:os';
5+
6+
import { config } from '#pkg/config.js';
7+
import { playwrightBrowserConfig } from '#pkg/constants.js';
8+
9+
const countOfCpus = os.cpus().length;
10+
const workers = countOfCpus !== 0 ? (countOfCpus <= 4 ? countOfCpus : 4) : undefined;
11+
12+
const htmlReporter: ReporterDescription = [
13+
'html',
14+
{ open: 'never', outputFolder: '../playwright-html-report' },
15+
];
16+
17+
const playwrightConfig: PlaywrightTestConfig = {
18+
fullyParallel: true,
19+
reporter: config.CI ? [htmlReporter, ['github']] : [htmlReporter],
20+
testMatch: ['*.spec.js'],
21+
globalTimeout: 1000 * 8 * 60, // 8 minutes
22+
projects: [
23+
{
24+
name: 'chromium',
25+
use: {
26+
...devices['Desktop Chrome'],
27+
// opt into "New Headless" chromium (https://playwright.dev/docs/browsers#chromium-new-headless-mode, https://developer.chrome.com/docs/chromium/headless)
28+
channel: 'chromium',
29+
},
30+
},
31+
],
32+
33+
/**
34+
* increase timeout to 30 minutes and set workers count to 1 if we are in a debugging session
35+
*/
36+
timeout: config.isDebuggingSession ? 1000 * 60 * 30 : undefined,
37+
workers: config.isDebuggingSession ? 1 : workers,
38+
39+
// fail a Playwright run in CI if some test.only is in the source code
40+
forbidOnly: !!config.CI,
41+
42+
snapshotPathTemplate: `{testDir}/../snapshots/{testFilePath}/{arg}-{projectName}-${playwrightBrowserConfig.browser === 'docker' ? 'docker' : '{platform}'}{ext}`,
43+
44+
expect: {
45+
toHaveScreenshot: {
46+
maxDiffPixelRatio: 0.03,
47+
},
48+
},
49+
50+
use: {
51+
/* have consistent timezone and locale */
52+
timezoneId: 'Europe/Vienna',
53+
locale: 'de-AT',
54+
55+
// always capture trace (seems to not have any performance impact)
56+
trace: 'on',
57+
58+
// always capture video (seems to not have any performance impact)
59+
video: 'on',
60+
},
61+
62+
webServer:
63+
playwrightBrowserConfig.browser === 'docker'
64+
? {
65+
// start the Playwright server in a docker container
66+
command: createDockerRunCommand(playwrightBrowserConfig.port),
67+
url: `http://127.0.0.1:${playwrightBrowserConfig.port}/`,
68+
stdout: 'pipe',
69+
stderr: 'pipe',
70+
timeout: 30_000,
71+
gracefulShutdown: {
72+
signal: 'SIGTERM',
73+
timeout: 10_000,
74+
},
75+
reuseExistingServer: !config.CI,
76+
}
77+
: undefined,
78+
};
79+
80+
// eslint-disable-next-line import/no-default-export -- needs to be default export for Playwright
81+
export default playwrightConfig;
82+
83+
function createDockerRunCommand(port: number) {
84+
let dockerRunCommand = `docker run --rm --init --workdir /home/pwuser --user pwuser --network host`;
85+
if (!config.CI) {
86+
// on development machines, we forward the X11 socket to the host system to allow GUI applications to run from within the container
87+
dockerRunCommand += ` -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix`;
88+
}
89+
dockerRunCommand += ` mcr.microsoft.com/playwright:v1.57.0-noble /bin/sh -c "npx -y playwright@1.57.0 run-server --port ${port} --host 0.0.0.0"`;
90+
91+
return dockerRunCommand;
92+
}

0 commit comments

Comments
 (0)