From 227685c497e0dffd839d406f90f7295f78c05a9c Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 04:09:09 +0000 Subject: [PATCH 1/9] install playwright for automated browser-based testing npm init playwright@latest answers to questions: ts or js? ts dir: "tests/" CI? yes Install browsers? yes --- .github/workflows/playwright.yml | 27 ++ .gitignore | 7 + package-lock.json | 559 ++++--------------------------- package.json | 1 + playwright.config.ts | 79 +++++ tests/example-recorded.spec.ts | 17 + tests/example.spec.ts | 18 + 7 files changed, 215 insertions(+), 493 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 playwright.config.ts create mode 100644 tests/example-recorded.spec.ts create mode 100644 tests/example.spec.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000000..3eb13143c3 --- /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 c76c5355a3..fab667d302 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,10 @@ public/p5.min.js # optional local preferences for vscode local.code-workspace .zed + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/package-lock.json b/package-lock.json index 8f4304b4c5..011de71836 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ }, "devDependencies": { "@codemirror/lang-javascript": "^6.2.2", + "@playwright/test": "^1.58.2", "@preact/preset-vite": "^2.8.2", "@swc/html": "^1.10.9", "@testing-library/preact": "^3.2.3", @@ -2020,15 +2021,6 @@ "node": ">=16" } }, - "node_modules/@cloudflare/workers-types": { - "version": "4.20260316.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260316.1.tgz", - "integrity": "sha512-HUZ+vQD8/1A4Fz/8WAlzYWcS5W5u3Nu7Dv9adkIkmLfeKqMIRn01vc4nSUBar60KkmohyQHkPi8jtWV/zazvAg==", - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "peer": true - }, "node_modules/@codemirror/autocomplete": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", @@ -4226,6 +4218,22 @@ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@poppinss/colors": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", @@ -18879,6 +18887,53 @@ "pathe": "^2.0.1" } }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "dev": true, + "license": "Apache-2.0", + "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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/points-on-curve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", @@ -23714,6 +23769,7 @@ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "pathe": "^2.0.3" } @@ -26272,6 +26328,7 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "bin": { "workerd": "bin/workerd" }, @@ -26321,490 +26378,6 @@ } } }, - "node_modules/wrangler/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/wrangler/node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" - } - }, "node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", diff --git a/package.json b/package.json index 05da1278ba..72c544ea33 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ }, "devDependencies": { "@codemirror/lang-javascript": "^6.2.2", + "@playwright/test": "^1.58.2", "@preact/preset-vite": "^2.8.2", "@swc/html": "^1.10.9", "@testing-library/preact": "^3.2.3", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000000..6dfc0d9bc6 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,79 @@ +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: './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://localhost: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://localhost:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/tests/example-recorded.spec.ts b/tests/example-recorded.spec.ts new file mode 100644 index 0000000000..26c3d39fd1 --- /dev/null +++ b/tests/example-recorded.spec.ts @@ -0,0 +1,17 @@ +import { test } from '@playwright/test'; + +test('test', async ({ page }) => { + await page.goto('https://demo.playwright.dev/todomvc/#/'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).click(); + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('buy food'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('wash car'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('read book'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + await page.getByRole('listitem').filter({ hasText: 'wash car' }).getByLabel('Toggle Todo').check(); + await page.getByRole('link', { name: 'Active' }).click(); + await page.getByRole('link', { name: 'Completed' }).click(); + await page.getByRole('link', { name: 'All' }).click(); + await page.getByRole('button', { name: 'Clear completed' }).click(); +}); \ No newline at end of file diff --git a/tests/example.spec.ts b/tests/example.spec.ts new file mode 100644 index 0000000000..54a906a4e8 --- /dev/null +++ b/tests/example.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); +}); + +test('get started link', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Click the get started link. + await page.getByRole('link', { name: 'Get started' }).click(); + + // Expects page to have a heading with the name of Installation. + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); +}); From 073b5313d0f91dd56435f34d73eece0a0e450e61 Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 04:10:19 +0000 Subject: [PATCH 2/9] add v rough playwright test to look for console errors in ref pages (e.g. from sketches) --- tests/find-broken-sketches-on-beta.spec.ts | 144 +++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 tests/find-broken-sketches-on-beta.spec.ts diff --git a/tests/find-broken-sketches-on-beta.spec.ts b/tests/find-broken-sketches-on-beta.spec.ts new file mode 100644 index 0000000000..d0ca491918 --- /dev/null +++ b/tests/find-broken-sketches-on-beta.spec.ts @@ -0,0 +1,144 @@ +// A playwright test to load various reference page URLs, scroll through for any +// sketches, and monitor the console, failing if errors are reported in the console +// (with some allowed). +// +//How to run: with UI: npx playwright test --project=chromium --ui +//How to run: no UI: npx playwright test --project=chromium +// +//TODO: rate-limit so we don't hassle the server or more likely get ip-banned by cloudflare CDN +//TODO: prefetching gets in the way, when links don't have a trailing slash! +//TODO: which test browsers will have a problem with webgl or webgpu? "Error creating webgl context at ..." +// It's not enough to LOAD the containing page, the browser has to scroll so the sketch is in viewport to get loaded. (and we have to wait a little, especially in the case of shader compilation) +// TODO: can we cause the browser to load and start all the sketches, without needing them to be in view? +// TODO: can we detect and scroll directly to each sketch in turn (waiting 1sec once there), rather than just scrolling through? +// TODO: test that a bad initial link is correctly reported as such (this could also legitimately be that a ref page has been renamed/removed) + +import { test, type Page, type ConsoleMessage, expect } from "@playwright/test"; + +type LogSeverity = "exception" | "error" | "other"; +type LogMsg = { url: string, text: string, severity: LogSeverity }; +type LogPolicy = "skipSilently" | "skipButMention" | "log"; +type Config = { + shouldLogNonErrorLogsInline: boolean, + on404ConsoleMsg: LogPolicy +} + +const config: Config = { + shouldLogNonErrorLogsInline: false, + on404ConsoleMsg: "skipButMention" +} + +/** + * Attaches listeners to capture console errors, uncaught exceptions. (also non-error console msgs) + * @param page + * @param errorLog An array to store captured error messages from the console + * @param nonErrorLog An array to store captured non-error messages from the console + * @todo do we need to explicitly drop this event handler when we nav away from the page? + */ +function setupErrorTracking(page: Page, nonErrorLog: LogMsg[], errorLog: LogMsg[]): void { + //1. get the console error and others (but I think this _doesn't_ get exceptions - later) + page.on("console", (msg: ConsoleMessage) => { + if (msg.type() === "error") { + //TODO: generalise to allow filtering of error types we don't care about + //is this an error we probably don't care about? + if (msg.text().includes("Failed to load resource: the server responded with a status of 404") && config.on404ConsoleMsg !== "log") { + if (config.on404ConsoleMsg === "skipButMention") { + console.log("skipping (maybe) expected 404 - prefetch with no trailing slash?") + } + } else { + //log error normally + errorLog.push({ severity: "error", text: msg.text(), url: page.url() }); + } + } else { + //non error console log + nonErrorLog.push({ severity: "other", url: page.url(), text: msg.text() }); + if (config.shouldLogNonErrorLogsInline) { + console.log(`[Console non-error] ${msg.text()} at ${page.url()}`); + } + } + } + ); + //2. also register for exceptions + page.on("pageerror", (err: Error) => { + errorLog.push({ severity: "exception", text: err.message, url: page.url() }); + }); +} + +test("look for sketch console errors in ref pages", async ({ page }) => { + test.setTimeout(120_000); + const pathsToCheck: string[] = [ + "/reference/p5/filterColor/", + "/reference/p5/createCanvas/", + "/reference/p5/fill/", + "/reference/p5/circle/", + "/reference/p5/colorMode/", + "/reference/p5/rectMode/", + "/reference/p5/imageMode/", + "/reference/p5/sphere/", + "/reference/p5/box/", + "/reference/p5/buildFilterShader/", + "/reference/p5/p5.Shader/", + "/reference/p5.Shader/setUniform/", + "/reference/p5/createFilterShader/", + "/reference/p5/emissiveMaterial/", + "/reference/p5/loadImage/", + "/reference/p5/p5.Image/", + "/reference/p5/imageMode/", + ]; + + const allErrors: LogMsg[] = []; + const nonErrors: LogMsg[] = []; + + setupErrorTracking(page, nonErrors, allErrors); + + for (const path of pathsToCheck) { + const baseURL = "http://localhost:4321"; //"https://beta.p5js.org" + const url = `${baseURL}${path}`; + console.log("going to url: ", url) + await page.goto(url); + await page.waitForTimeout(500); + await scrollToBottom(page); + // Give p5.js setup() and draw() time to execute, and shader compilation! + console.log("done scrolling to bottom. waiting for 1500") + + //TODO: this is a weak point. no idea how long it could take for everything to load (e.g. compile shader). can we detect iframe's p5 setup completion? maybe with a small instrumentation change to p5? + await page.waitForTimeout(1500); + + if (allErrors.length > 0) { + console.error(`Errors on ${url}:`, allErrors); + allErrors.length = 0; // Clear for next page + } else { + console.log(`no errors on ${url}`); + } + } + + //TODO: perhaps provide non-error console outputs for the url(s) which had errors, for more context. + //Also take screenshots of them? + + expect(allErrors).toEqual([]); +}); + +// We need to scroll slowly or otherwise make sure all sketches come into viewport to start loading. THEN we need to wait a bit! +// Modified (debugged) from damaon's solution at https://github.com/microsoft/playwright/issues/4302#issuecomment-1882853669 +// There are other solutions we might consider at the same url https://github.com/microsoft/playwright/issues/4302 +async function scrollToBottom(page: Page) { + const [scrollY, scrollHeight] = await page.evaluate(() => [ + window.scrollY, + window.document.documentElement.scrollHeight, + ]) + console.log(`window.scrollY at start: ${scrollY} window scrollHeight: ${scrollHeight}`) + + //TODO: are we going to pay attn to initial scrollY or not? + for (let i = 0; scrollY + i < scrollHeight; i += 300) { + await page.evaluate((i) => { + console.log("scrolling to: ", i) + //should be scrolling to i + scrollY if we're paying attn to the latter + window.scrollTo({ top: i, left: 0, behavior: 'smooth' }) + }, i) + await sleep(0.1) + } +} + +function sleep(seconds: number) { + return new Promise((resolve) => setTimeout(resolve, seconds * 1000)) +} \ No newline at end of file From dcd33b6d568e15ec2ee3aed255c553ef5d92ff2c Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 04:28:44 +0000 Subject: [PATCH 3/9] move from tests/ to playwright-tests/ --- {tests => playwright-tests}/example-recorded.spec.ts | 0 {tests => playwright-tests}/example.spec.ts | 0 .../find-broken-sketches-on-beta.spec.ts | 0 playwright.config.ts | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename {tests => playwright-tests}/example-recorded.spec.ts (100%) rename {tests => playwright-tests}/example.spec.ts (100%) rename {tests => playwright-tests}/find-broken-sketches-on-beta.spec.ts (100%) diff --git a/tests/example-recorded.spec.ts b/playwright-tests/example-recorded.spec.ts similarity index 100% rename from tests/example-recorded.spec.ts rename to playwright-tests/example-recorded.spec.ts diff --git a/tests/example.spec.ts b/playwright-tests/example.spec.ts similarity index 100% rename from tests/example.spec.ts rename to playwright-tests/example.spec.ts diff --git a/tests/find-broken-sketches-on-beta.spec.ts b/playwright-tests/find-broken-sketches-on-beta.spec.ts similarity index 100% rename from tests/find-broken-sketches-on-beta.spec.ts rename to playwright-tests/find-broken-sketches-on-beta.spec.ts diff --git a/playwright.config.ts b/playwright.config.ts index 6dfc0d9bc6..af52468b2b 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -12,7 +12,7 @@ import { defineConfig, devices } from '@playwright/test'; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './tests', + testDir: './playwright-tests', /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ From 200f868754b21dc83ad1b28998f4c8b37d5fceca Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 04:31:29 +0000 Subject: [PATCH 4/9] playwrightl: silence logs from page-scrolling code --- playwright-tests/find-broken-sketches-on-beta.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/playwright-tests/find-broken-sketches-on-beta.spec.ts b/playwright-tests/find-broken-sketches-on-beta.spec.ts index d0ca491918..01fd805513 100644 --- a/playwright-tests/find-broken-sketches-on-beta.spec.ts +++ b/playwright-tests/find-broken-sketches-on-beta.spec.ts @@ -43,7 +43,7 @@ function setupErrorTracking(page: Page, nonErrorLog: LogMsg[], errorLog: LogMsg[ //is this an error we probably don't care about? if (msg.text().includes("Failed to load resource: the server responded with a status of 404") && config.on404ConsoleMsg !== "log") { if (config.on404ConsoleMsg === "skipButMention") { - console.log("skipping (maybe) expected 404 - prefetch with no trailing slash?") + console.log("Skipping a (maybe) expected 404 - often a prefetch with no trailing slash?") } } else { //log error normally @@ -126,13 +126,13 @@ async function scrollToBottom(page: Page) { window.scrollY, window.document.documentElement.scrollHeight, ]) - console.log(`window.scrollY at start: ${scrollY} window scrollHeight: ${scrollHeight}`) + // console.log(`window.scrollY at start: ${scrollY} window scrollHeight: ${scrollHeight}`) //TODO: are we going to pay attn to initial scrollY or not? for (let i = 0; scrollY + i < scrollHeight; i += 300) { await page.evaluate((i) => { - console.log("scrolling to: ", i) - //should be scrolling to i + scrollY if we're paying attn to the latter + // console.log("scrolling to: ", i) + // TODO: check. this should be scrolling to i + scrollY if we're going to pay attn to the latter window.scrollTo({ top: i, left: 0, behavior: 'smooth' }) }, i) await sleep(0.1) From 6297af8c26b9556803f4e9d454ce551c5f5e98ed Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 05:23:23 +0000 Subject: [PATCH 5/9] remove playwright examples --- playwright-tests/example-recorded.spec.ts | 17 ----------------- playwright-tests/example.spec.ts | 18 ------------------ 2 files changed, 35 deletions(-) delete mode 100644 playwright-tests/example-recorded.spec.ts delete mode 100644 playwright-tests/example.spec.ts diff --git a/playwright-tests/example-recorded.spec.ts b/playwright-tests/example-recorded.spec.ts deleted file mode 100644 index 26c3d39fd1..0000000000 --- a/playwright-tests/example-recorded.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { test } from '@playwright/test'; - -test('test', async ({ page }) => { - await page.goto('https://demo.playwright.dev/todomvc/#/'); - await page.getByRole('textbox', { name: 'What needs to be done?' }).click(); - await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('buy food'); - await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); - await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('wash car'); - await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); - await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('read book'); - await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); - await page.getByRole('listitem').filter({ hasText: 'wash car' }).getByLabel('Toggle Todo').check(); - await page.getByRole('link', { name: 'Active' }).click(); - await page.getByRole('link', { name: 'Completed' }).click(); - await page.getByRole('link', { name: 'All' }).click(); - await page.getByRole('button', { name: 'Clear completed' }).click(); -}); \ No newline at end of file diff --git a/playwright-tests/example.spec.ts b/playwright-tests/example.spec.ts deleted file mode 100644 index 54a906a4e8..0000000000 --- a/playwright-tests/example.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { test, expect } from '@playwright/test'; - -test('has title', async ({ page }) => { - await page.goto('https://playwright.dev/'); - - // Expect a title "to contain" a substring. - await expect(page).toHaveTitle(/Playwright/); -}); - -test('get started link', async ({ page }) => { - await page.goto('https://playwright.dev/'); - - // Click the get started link. - await page.getByRole('link', { name: 'Get started' }).click(); - - // Expects page to have a heading with the name of Installation. - await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); -}); From 81fcb3eadb4f1c84c551a2d88c633dd4f40042b0 Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 05:25:25 +0000 Subject: [PATCH 6/9] externalise url paths to check, and finally "fail" if non-zero errors. --- .../find-broken-sketches-on-beta.spec.ts | 63 +- playwright-tests/reference-links.ts | 877 ++++++++++++++++++ 2 files changed, 911 insertions(+), 29 deletions(-) create mode 100644 playwright-tests/reference-links.ts diff --git a/playwright-tests/find-broken-sketches-on-beta.spec.ts b/playwright-tests/find-broken-sketches-on-beta.spec.ts index 01fd805513..27716a34e8 100644 --- a/playwright-tests/find-broken-sketches-on-beta.spec.ts +++ b/playwright-tests/find-broken-sketches-on-beta.spec.ts @@ -2,18 +2,22 @@ // sketches, and monitor the console, failing if errors are reported in the console // (with some allowed). // -//How to run: with UI: npx playwright test --project=chromium --ui -//How to run: no UI: npx playwright test --project=chromium +//How to run: headed: npx playwright test --project=chromium --headed +//How to run: with UI*: npx playwright test --project=chromium --ui +//How to run: no UI*: npx playwright test --project=chromium +//\* these headless modes still have issues with webgl. // //TODO: rate-limit so we don't hassle the server or more likely get ip-banned by cloudflare CDN //TODO: prefetching gets in the way, when links don't have a trailing slash! -//TODO: which test browsers will have a problem with webgl or webgpu? "Error creating webgl context at ..." +//TODO: consider turning off browser prefetching to lessen request rates. +//TODO: fix webgl for headless (chromium). "Error creating webgl context at ..." // It's not enough to LOAD the containing page, the browser has to scroll so the sketch is in viewport to get loaded. (and we have to wait a little, especially in the case of shader compilation) // TODO: can we cause the browser to load and start all the sketches, without needing them to be in view? // TODO: can we detect and scroll directly to each sketch in turn (waiting 1sec once there), rather than just scrolling through? // TODO: test that a bad initial link is correctly reported as such (this could also legitimately be that a ref page has been renamed/removed) import { test, type Page, type ConsoleMessage, expect } from "@playwright/test"; +import { referencePageURLPaths } from "./reference-links.ts"; type LogSeverity = "exception" | "error" | "other"; type LogMsg = { url: string, text: string, severity: LogSeverity }; @@ -28,6 +32,20 @@ const config: Config = { on404ConsoleMsg: "skipButMention" } + //TODO: extract this from astro build or data.json + function getURLPathsToTest(): string[] { + return [ + "/reference/p5/intentionallyBadURL/", + //on local this will also 404 - no trailing slash + "/reference/p5/square", + //locally modded up to have some errors + "/reference/p5/filterColor/", + //the full set + ...referencePageURLPaths + ] + } + + /** * Attaches listeners to capture console errors, uncaught exceptions. (also non-error console msgs) * @param page @@ -65,31 +83,16 @@ function setupErrorTracking(page: Page, nonErrorLog: LogMsg[], errorLog: LogMsg[ } test("look for sketch console errors in ref pages", async ({ page }) => { - test.setTimeout(120_000); - const pathsToCheck: string[] = [ - "/reference/p5/filterColor/", - "/reference/p5/createCanvas/", - "/reference/p5/fill/", - "/reference/p5/circle/", - "/reference/p5/colorMode/", - "/reference/p5/rectMode/", - "/reference/p5/imageMode/", - "/reference/p5/sphere/", - "/reference/p5/box/", - "/reference/p5/buildFilterShader/", - "/reference/p5/p5.Shader/", - "/reference/p5.Shader/setUniform/", - "/reference/p5/createFilterShader/", - "/reference/p5/emissiveMaterial/", - "/reference/p5/loadImage/", - "/reference/p5/p5.Image/", - "/reference/p5/imageMode/", - ]; + test.setTimeout(60 * 60 * 1000);//60 minutes + const pathsToCheck: string[] = getURLPathsToTest(); + const allErrors: LogMsg[] = []; - const nonErrors: LogMsg[] = []; - setupErrorTracking(page, nonErrors, allErrors); + const errorsInPage: LogMsg[] = []; + const nonErrorsInPage: LogMsg[] = []; + + setupErrorTracking(page, nonErrorsInPage, errorsInPage); for (const path of pathsToCheck) { const baseURL = "http://localhost:4321"; //"https://beta.p5js.org" @@ -104,9 +107,11 @@ test("look for sketch console errors in ref pages", async ({ page }) => { //TODO: this is a weak point. no idea how long it could take for everything to load (e.g. compile shader). can we detect iframe's p5 setup completion? maybe with a small instrumentation change to p5? await page.waitForTimeout(1500); - if (allErrors.length > 0) { - console.error(`Errors on ${url}:`, allErrors); - allErrors.length = 0; // Clear for next page + if (errorsInPage.length > 0) { + console.error(`Errors on ${url}:`, errorsInPage); + + allErrors.push(...errorsInPage); + errorsInPage.length = 0; // Clear for next page } else { console.log(`no errors on ${url}`); } @@ -115,7 +120,7 @@ test("look for sketch console errors in ref pages", async ({ page }) => { //TODO: perhaps provide non-error console outputs for the url(s) which had errors, for more context. //Also take screenshots of them? - expect(allErrors).toEqual([]); + expect(allErrors).toHaveLength(0); }); // We need to scroll slowly or otherwise make sure all sketches come into viewport to start loading. THEN we need to wait a bit! diff --git a/playwright-tests/reference-links.ts b/playwright-tests/reference-links.ts new file mode 100644 index 0000000000..4a2ff5baaf --- /dev/null +++ b/playwright-tests/reference-links.ts @@ -0,0 +1,877 @@ +// This is a temporary file for early testing of playwright to find broken sketches in reference pages +// eventually these urls would come from the build process (astro's or /docs/reference/data.json) +export const referencePageURLPaths = [ + "/reference/p5.Amplitude/getLevel/", + "/reference/p5.Amplitude/setInput/", + "/reference/p5.Amplitude/smooth/", + "/reference/p5.AudioIn/start/", + "/reference/p5.AudioIn/stop/", + "/reference/p5.Biquad/freq/", + "/reference/p5.Biquad/gain/", + "/reference/p5.Biquad/res/", + "/reference/p5.Biquad/setType/", + "/reference/p5.Camera/camera/", + "/reference/p5.Camera/centerX/", + "/reference/p5.Camera/centerY/", + "/reference/p5.Camera/centerZ/", + "/reference/p5.Camera/eyeX/", + "/reference/p5.Camera/eyeY/", + "/reference/p5.Camera/eyeZ/", + "/reference/p5.Camera/frustum/", + "/reference/p5.Camera/lookAt/", + "/reference/p5.Camera/move/", + "/reference/p5.Camera/ortho/", + "/reference/p5.Camera/pan/", + "/reference/p5.Camera/perspective/", + "/reference/p5.Camera/set/", + "/reference/p5.Camera/setPosition/", + "/reference/p5.Camera/slerp/", + "/reference/p5.Camera/tilt/", + "/reference/p5.Camera/upX/", + "/reference/p5.Camera/upY/", + "/reference/p5.Camera/upZ/", + "/reference/p5.Color/contrast/", + "/reference/p5.Color/setAlpha/", + "/reference/p5.Color/setBlue/", + "/reference/p5.Color/setGreen/", + "/reference/p5.Color/setRed/", + "/reference/p5.Color/toString/", + "/reference/p5.Delay/delayTime/", + "/reference/p5.Delay/feedback/", + "/reference/p5.Delay/process/", + "/reference/p5.Element/addClass/", + "/reference/p5.Element/attribute/", + "/reference/p5.Element/center/", + "/reference/p5.Element/changed/", + "/reference/p5.Element/child/", + "/reference/p5.Element/class/", + "/reference/p5.Element/doubleClicked/", + "/reference/p5.Element/dragLeave/", + "/reference/p5.Element/dragOver/", + "/reference/p5.Element/draggable/", + "/reference/p5.Element/drop/", + "/reference/p5.Element/elt/", + "/reference/p5.Element/hasClass/", + "/reference/p5.Element/height/", + "/reference/p5.Element/hide/", + "/reference/p5.Element/html/", + "/reference/p5.Element/id/", + "/reference/p5.Element/input/", + "/reference/p5.Element/mouseClicked/", + "/reference/p5.Element/mouseMoved/", + "/reference/p5.Element/mouseOut/", + "/reference/p5.Element/mouseOver/", + "/reference/p5.Element/mousePressed/", + "/reference/p5.Element/mouseReleased/", + "/reference/p5.Element/mouseWheel/", + "/reference/p5.Element/parent/", + "/reference/p5.Element/position/", + "/reference/p5.Element/remove/", + "/reference/p5.Element/removeAttribute/", + "/reference/p5.Element/removeClass/", + "/reference/p5.Element/show/", + "/reference/p5.Element/size/", + "/reference/p5.Element/style/", + "/reference/p5.Element/toggleClass/", + "/reference/p5.Element/value/", + "/reference/p5.Element/width/", + "/reference/p5.Envelope/attackTime/", + "/reference/p5.Envelope/play/", + "/reference/p5.Envelope/releaseTime/", + "/reference/p5.Envelope/setADSR/", + "/reference/p5.Envelope/setInput/", + "/reference/p5.Envelope/triggerAttack/", + "/reference/p5.Envelope/triggerRelease/", + "/reference/p5.FFT/analyze/", + "/reference/p5.FFT/waveform/", + "/reference/p5.File/data/", + "/reference/p5.File/file/", + "/reference/p5.File/name/", + "/reference/p5.File/size/", + "/reference/p5.File/subtype/", + "/reference/p5.File/type/", + "/reference/p5.Font/textToContours/", + "/reference/p5.Font/textToModel/", + "/reference/p5.Font/textToPaths/", + "/reference/p5.Font/textToPoints/", + "/reference/p5.Framebuffer/autoSized/", + "/reference/p5.Framebuffer/begin/", + "/reference/p5.Framebuffer/color/", + "/reference/p5.Framebuffer/createCamera/", + "/reference/p5.Framebuffer/depth/", + "/reference/p5.Framebuffer/draw/", + "/reference/p5.Framebuffer/end/", + "/reference/p5.Framebuffer/get/", + "/reference/p5.Framebuffer/height/", + "/reference/p5.Framebuffer/loadPixels/", + "/reference/p5.Framebuffer/pixelDensity/", + "/reference/p5.Framebuffer/pixels/", + "/reference/p5.Framebuffer/remove/", + "/reference/p5.Framebuffer/resize/", + "/reference/p5.Framebuffer/width/", + "/reference/p5.Geometry/calculateBoundingBox/", + "/reference/p5.Geometry/clearColors/", + "/reference/p5.Geometry/computeFaces/", + "/reference/p5.Geometry/computeNormals/", + "/reference/p5.Geometry/faces/", + "/reference/p5.Geometry/flipU/", + "/reference/p5.Geometry/flipV/", + "/reference/p5.Geometry/gid/", + "/reference/p5.Geometry/makeEdgesFromFaces/", + "/reference/p5.Geometry/normalize/", + "/reference/p5.Geometry/uvs/", + "/reference/p5.Geometry/vertexNormals/", + "/reference/p5.Geometry/vertexProperty/", + "/reference/p5.Geometry/vertices/", + "/reference/p5.Graphics/createFramebuffer/", + "/reference/p5.Graphics/remove/", + "/reference/p5.Graphics/reset/", + "/reference/p5.Image/blend/", + "/reference/p5.Image/copy/", + "/reference/p5.Image/delay/", + "/reference/p5.Image/filter/", + "/reference/p5.Image/get/", + "/reference/p5.Image/getCurrentFrame/", + "/reference/p5.Image/height/", + "/reference/p5.Image/loadPixels/", + "/reference/p5.Image/mask/", + "/reference/p5.Image/numFrames/", + "/reference/p5.Image/pause/", + "/reference/p5.Image/pixelDensity/", + "/reference/p5.Image/pixels/", + "/reference/p5.Image/play/", + "/reference/p5.Image/reset/", + "/reference/p5.Image/resize/", + "/reference/p5.Image/save/", + "/reference/p5.Image/set/", + "/reference/p5.Image/setFrame/", + "/reference/p5.Image/updatePixels/", + "/reference/p5.Image/width/", + "/reference/p5.MediaElement/addCue/", + "/reference/p5.MediaElement/autoplay/", + "/reference/p5.MediaElement/clearCues/", + "/reference/p5.MediaElement/connect/", + "/reference/p5.MediaElement/disconnect/", + "/reference/p5.MediaElement/duration/", + "/reference/p5.MediaElement/hideControls/", + "/reference/p5.MediaElement/loop/", + "/reference/p5.MediaElement/noLoop/", + "/reference/p5.MediaElement/onended/", + "/reference/p5.MediaElement/pause/", + "/reference/p5.MediaElement/play/", + "/reference/p5.MediaElement/removeCue/", + "/reference/p5.MediaElement/showControls/", + "/reference/p5.MediaElement/speed/", + "/reference/p5.MediaElement/src/", + "/reference/p5.MediaElement/stop/", + "/reference/p5.MediaElement/time/", + "/reference/p5.MediaElement/volume/", + "/reference/p5.Noise/type/", + "/reference/p5.Oscillator/freq/", + "/reference/p5.Oscillator/phase/", + "/reference/p5.Oscillator/setType/", + "/reference/p5.Panner/pan/", + "/reference/p5.Panner3D/maxDist/", + "/reference/p5.Panner3D/positionX/", + "/reference/p5.Panner3D/positionY/", + "/reference/p5.Panner3D/positionZ/", + "/reference/p5.Panner3D/process/", + "/reference/p5.Panner3D/rolloff/", + "/reference/p5.Panner3D/set/", + "/reference/p5.Panner3D/setFalloff/", + "/reference/p5.PitchShifter/shift/", + "/reference/p5.Reverb/set/", + "/reference/p5.Shader/copyToContext/", + "/reference/p5.Shader/inspectHooks/", + "/reference/p5.Shader/modify/", + "/reference/p5.Shader/setUniform/", + "/reference/p5.Shader/version/", + "/reference/p5.SoundFile/channels/", + "/reference/p5.SoundFile/duration/", + "/reference/p5.SoundFile/frames/", + "/reference/p5.SoundFile/isLooping/", + "/reference/p5.SoundFile/isPlaying/", + "/reference/p5.SoundFile/jump/", + "/reference/p5.SoundFile/loop/", + "/reference/p5.SoundFile/onended/", + "/reference/p5.SoundFile/pause/", + "/reference/p5.SoundFile/play/", + "/reference/p5.SoundFile/rate/", + "/reference/p5.SoundFile/sampleRate/", + "/reference/p5.SoundFile/setLoop/", + "/reference/p5.SoundFile/setPath/", + "/reference/p5.SoundFile/start/", + "/reference/p5.SoundFile/stop/", + "/reference/p5.Table/addColumn/", + "/reference/p5.Table/addRow/", + "/reference/p5.Table/clearRows/", + "/reference/p5.Table/columns/", + "/reference/p5.Table/findRow/", + "/reference/p5.Table/findRows/", + "/reference/p5.Table/get/", + "/reference/p5.Table/getArray/", + "/reference/p5.Table/getColumn/", + "/reference/p5.Table/getColumnCount/", + "/reference/p5.Table/getNum/", + "/reference/p5.Table/getObject/", + "/reference/p5.Table/getRow/", + "/reference/p5.Table/getRowCount/", + "/reference/p5.Table/getRows/", + "/reference/p5.Table/getString/", + "/reference/p5.Table/matchRow/", + "/reference/p5.Table/matchRows/", + "/reference/p5.Table/removeColumn/", + "/reference/p5.Table/removeRow/", + "/reference/p5.Table/removeTokens/", + "/reference/p5.Table/rows/", + "/reference/p5.Table/set/", + "/reference/p5.Table/setNum/", + "/reference/p5.Table/setString/", + "/reference/p5.Table/trim/", + "/reference/p5.TableRow/get/", + "/reference/p5.TableRow/getNum/", + "/reference/p5.TableRow/getString/", + "/reference/p5.TableRow/set/", + "/reference/p5.TableRow/setNum/", + "/reference/p5.TableRow/setString/", + "/reference/p5.Vector/add/", + "/reference/p5.Vector/angleBetween/", + "/reference/p5.Vector/array/", + "/reference/p5.Vector/clampToZero/", + "/reference/p5.Vector/copy/", + "/reference/p5.Vector/cross/", + "/reference/p5.Vector/dist/", + "/reference/p5.Vector/div/", + "/reference/p5.Vector/dot/", + "/reference/p5.Vector/equals/", + "/reference/p5.Vector/fromAngle/", + "/reference/p5.Vector/fromAngles/", + "/reference/p5.Vector/getValue/", + "/reference/p5.Vector/heading/", + "/reference/p5.Vector/lerp/", + "/reference/p5.Vector/limit/", + "/reference/p5.Vector/mag/", + "/reference/p5.Vector/magSq/", + "/reference/p5.Vector/mult/", + "/reference/p5.Vector/normalize/", + "/reference/p5.Vector/random2D/", + "/reference/p5.Vector/random3D/", + "/reference/p5.Vector/reflect/", + "/reference/p5.Vector/rem/", + "/reference/p5.Vector/rotate/", + "/reference/p5.Vector/set/", + "/reference/p5.Vector/setHeading/", + "/reference/p5.Vector/setMag/", + "/reference/p5.Vector/setValue/", + "/reference/p5.Vector/slerp/", + "/reference/p5.Vector/sub/", + "/reference/p5.Vector/toString/", + "/reference/p5.Vector/x/", + "/reference/p5.Vector/y/", + "/reference/p5.Vector/z/", + "/reference/p5.XML/addChild/", + "/reference/p5.XML/getAttributeCount/", + "/reference/p5.XML/getChild/", + "/reference/p5.XML/getChildren/", + "/reference/p5.XML/getContent/", + "/reference/p5.XML/getName/", + "/reference/p5.XML/getNum/", + "/reference/p5.XML/getParent/", + "/reference/p5.XML/getString/", + "/reference/p5.XML/hasAttribute/", + "/reference/p5.XML/hasChildren/", + "/reference/p5.XML/listAttributes/", + "/reference/p5.XML/listChildren/", + "/reference/p5.XML/removeChild/", + "/reference/p5.XML/serialize/", + "/reference/p5.XML/setAttribute/", + "/reference/p5.XML/setName/", + "/reference/p5.p5soundMixEffect/wet/", + "/reference/p5.p5soundNode/amp/", + "/reference/p5.p5soundSource/start/", + "/reference/p5.p5soundSource/stop/", + "/reference/p5.sound/p5.Amplitude/", + "/reference/p5.sound/p5.AudioIn/", + "/reference/p5.sound/p5.BandPass/", + "/reference/p5.sound/p5.Biquad/", + "/reference/p5.sound/p5.Delay/", + "/reference/p5.sound/p5.Envelope/", + "/reference/p5.sound/p5.FFT/", + "/reference/p5.sound/p5.Gain/", + "/reference/p5.sound/p5.HighPass/", + "/reference/p5.sound/p5.LowPass/", + "/reference/p5.sound/p5.Noise/", + "/reference/p5.sound/p5.Oscillator/", + "/reference/p5.sound/p5.Panner/", + "/reference/p5.sound/p5.Panner3D/", + "/reference/p5.sound/p5.PitchShifter/", + "/reference/p5.sound/p5.Reverb/", + "/reference/p5.sound/p5.SawOsc/", + "/reference/p5.sound/p5.SinOsc/", + "/reference/p5.sound/p5.SoundFile/", + "/reference/p5.sound/p5.SqrOsc/", + "/reference/p5.sound/p5.TriOsc/", + "/reference/p5.sound/p5.p5soundMixEffect/", + "/reference/p5.sound/p5.p5soundNode/", + "/reference/p5.sound/p5.p5soundSource/", + "/reference/p5/abs/", + "/reference/p5/accelerationX/", + "/reference/p5/accelerationY/", + "/reference/p5/accelerationZ/", + "/reference/p5/acos/", + "/reference/p5/addElement/", + "/reference/p5/alpha/", + "/reference/p5/ambientLight/", + "/reference/p5/ambientMaterial/", + "/reference/p5/angleMode/", + "/reference/p5/applyMatrix/", + "/reference/p5/arc/", + "/reference/p5/asin/", + "/reference/p5/async_await/", + "/reference/p5/atan/", + "/reference/p5/atan2/", + "/reference/p5/background/", + "/reference/p5/baseColorShader/", + "/reference/p5/baseFilterShader/", + "/reference/p5/baseMaterialShader/", + "/reference/p5/baseNormalShader/", + "/reference/p5/baseStrokeShader/", + "/reference/p5/beginClip/", + "/reference/p5/beginContour/", + "/reference/p5/beginShape/", + "/reference/p5/bezier/", + "/reference/p5/bezierOrder/", + "/reference/p5/bezierPoint/", + "/reference/p5/bezierTangent/", + "/reference/p5/bezierVertex/", + "/reference/p5/blend/", + "/reference/p5/blendMode/", + "/reference/p5/blue/", + "/reference/p5/boolean/", + "/reference/p5/box/", + "/reference/p5/brightness/", + "/reference/p5/buildColorShader/", + "/reference/p5/buildFilterShader/", + "/reference/p5/buildGeometry/", + "/reference/p5/buildMaterialShader/", + "/reference/p5/buildNormalShader/", + "/reference/p5/buildStrokeShader/", + "/reference/p5/byte/", + "/reference/p5/camera/", + "/reference/p5/cameraInputs/", + "/reference/p5/ceil/", + "/reference/p5/char/", + "/reference/p5/circle/", + "/reference/p5/class/", + "/reference/p5/clear/", + "/reference/p5/clearDepth/", + "/reference/p5/clearStorage/", + "/reference/p5/clip/", + "/reference/p5/close/", + "/reference/p5/code/", + "/reference/p5/color/", + "/reference/p5/colorMode/", + "/reference/p5/combineColors/", + "/reference/p5/cone/", + "/reference/p5/console/", + "/reference/p5/constants/ADD/", + "/reference/p5/constants/ALT/", + "/reference/p5/constants/ARROW/", + "/reference/p5/constants/AUDIO/", + "/reference/p5/constants/AUTO/", + "/reference/p5/constants/AXES/", + "/reference/p5/constants/BACKSPACE/", + "/reference/p5/constants/BASELINE/", + "/reference/p5/constants/BEVEL/", + "/reference/p5/constants/BEZIER/", + "/reference/p5/constants/BLEND/", + "/reference/p5/constants/BLUR/", + "/reference/p5/constants/BOLD/", + "/reference/p5/constants/BOLDITALIC/", + "/reference/p5/constants/BOTTOM/", + "/reference/p5/constants/BURN/", + "/reference/p5/constants/CENTER/", + "/reference/p5/constants/CHAR/", + "/reference/p5/constants/CHORD/", + "/reference/p5/constants/CLAMP/", + "/reference/p5/constants/CLOSE/", + "/reference/p5/constants/CONTAIN/", + "/reference/p5/constants/CONTROL/", + "/reference/p5/constants/CORNER/", + "/reference/p5/constants/CORNERS/", + "/reference/p5/constants/COVER/", + "/reference/p5/constants/CROSS/", + "/reference/p5/constants/CURVE/", + "/reference/p5/constants/DARKEST/", + "/reference/p5/constants/DEGREES/", + "/reference/p5/constants/DEG_TO_RAD/", + "/reference/p5/constants/DELETE/", + "/reference/p5/constants/DIFFERENCE/", + "/reference/p5/constants/DILATE/", + "/reference/p5/constants/DODGE/", + "/reference/p5/constants/DOWN_ARROW/", + "/reference/p5/constants/EMPTY_PATH/", + "/reference/p5/constants/ENTER/", + "/reference/p5/constants/ERODE/", + "/reference/p5/constants/ESCAPE/", + "/reference/p5/constants/EXCLUDE/", + "/reference/p5/constants/EXCLUSION/", + "/reference/p5/constants/FALLBACK/", + "/reference/p5/constants/FILL/", + "/reference/p5/constants/FLAT/", + "/reference/p5/constants/FLOAT/", + "/reference/p5/constants/FULL/", + "/reference/p5/constants/GRAY/", + "/reference/p5/constants/GRID/", + "/reference/p5/constants/HALF_FLOAT/", + "/reference/p5/constants/HALF_PI/", + "/reference/p5/constants/HAND/", + "/reference/p5/constants/HARD_LIGHT/", + "/reference/p5/constants/HSB/", + "/reference/p5/constants/HSL/", + "/reference/p5/constants/HWB/", + "/reference/p5/constants/IMAGE/", + "/reference/p5/constants/IMMEDIATE/", + "/reference/p5/constants/INCLUDE/", + "/reference/p5/constants/INVERT/", + "/reference/p5/constants/ITALIC/", + "/reference/p5/constants/LAB/", + "/reference/p5/constants/LABEL/", + "/reference/p5/constants/LANDSCAPE/", + "/reference/p5/constants/LCH/", + "/reference/p5/constants/LEFT/", + "/reference/p5/constants/LEFT_ARROW/", + "/reference/p5/constants/LIGHTEST/", + "/reference/p5/constants/LINEAR/", + "/reference/p5/constants/LINES/", + "/reference/p5/constants/LINE_LOOP/", + "/reference/p5/constants/LINE_STRIP/", + "/reference/p5/constants/MIRROR/", + "/reference/p5/constants/MITER/", + "/reference/p5/constants/MOVE/", + "/reference/p5/constants/MULTIPLY/", + "/reference/p5/constants/NEAREST/", + "/reference/p5/constants/NORMAL/", + "/reference/p5/constants/OKLAB/", + "/reference/p5/constants/OKLCH/", + "/reference/p5/constants/OPAQUE/", + "/reference/p5/constants/OPEN/", + "/reference/p5/constants/OPTION/", + "/reference/p5/constants/OVERLAY/", + "/reference/p5/constants/P2D/", + "/reference/p5/constants/P2DHDR/", + "/reference/p5/constants/PATH/", + "/reference/p5/constants/PI/", + "/reference/p5/constants/PIE/", + "/reference/p5/constants/POINTS/", + "/reference/p5/constants/PORTRAIT/", + "/reference/p5/constants/POSTERIZE/", + "/reference/p5/constants/PROJECT/", + "/reference/p5/constants/QUADRATIC/", + "/reference/p5/constants/QUADS/", + "/reference/p5/constants/QUAD_STRIP/", + "/reference/p5/constants/QUARTER_PI/", + "/reference/p5/constants/RADIANS/", + "/reference/p5/constants/RADIUS/", + "/reference/p5/constants/RAD_TO_DEG/", + "/reference/p5/constants/REMOVE/", + "/reference/p5/constants/REPEAT/", + "/reference/p5/constants/REPLACE/", + "/reference/p5/constants/RETURN/", + "/reference/p5/constants/RGB/", + "/reference/p5/constants/RGBA/", + "/reference/p5/constants/RGBHDR/", + "/reference/p5/constants/RIGHT/", + "/reference/p5/constants/RIGHT_ARROW/", + "/reference/p5/constants/ROUND/", + "/reference/p5/constants/SCREEN/", + "/reference/p5/constants/SHIFT/", + "/reference/p5/constants/SIMPLE/", + "/reference/p5/constants/SMOOTH/", + "/reference/p5/constants/SOFT_LIGHT/", + "/reference/p5/constants/SQUARE/", + "/reference/p5/constants/STROKE/", + "/reference/p5/constants/SUBTRACT/", + "/reference/p5/constants/TAB/", + "/reference/p5/constants/TAU/", + "/reference/p5/constants/TESS/", + "/reference/p5/constants/TEXT/", + "/reference/p5/constants/TEXTURE/", + "/reference/p5/constants/THRESHOLD/", + "/reference/p5/constants/TOP/", + "/reference/p5/constants/TRIANGLES/", + "/reference/p5/constants/TRIANGLE_FAN/", + "/reference/p5/constants/TRIANGLE_STRIP/", + "/reference/p5/constants/TWO_PI/", + "/reference/p5/constants/UNSIGNED_BYTE/", + "/reference/p5/constants/UNSIGNED_INT/", + "/reference/p5/constants/UP_ARROW/", + "/reference/p5/constants/VERSION/", + "/reference/p5/constants/VIDEO/", + "/reference/p5/constants/WAIT/", + "/reference/p5/constants/WEBGL/", + "/reference/p5/constants/WEBGL2/", + "/reference/p5/constants/WEBGPU/", + "/reference/p5/constants/WORD/", + "/reference/p5/constrain/", + "/reference/p5/copy/", + "/reference/p5/cos/", + "/reference/p5/createA/", + "/reference/p5/createAudio/", + "/reference/p5/createButton/", + "/reference/p5/createCamera/", + "/reference/p5/createCanvas/", + "/reference/p5/createCapture/", + "/reference/p5/createCheckbox/", + "/reference/p5/createColorPicker/", + "/reference/p5/createDiv/", + "/reference/p5/createElement/", + "/reference/p5/createFileInput/", + "/reference/p5/createFilterShader/", + "/reference/p5/createFramebuffer/", + "/reference/p5/createGraphics/", + "/reference/p5/createImage/", + "/reference/p5/createImg/", + "/reference/p5/createInput/", + "/reference/p5/createModel/", + "/reference/p5/createP/", + "/reference/p5/createRadio/", + "/reference/p5/createSelect/", + "/reference/p5/createShader/", + "/reference/p5/createSlider/", + "/reference/p5/createSpan/", + "/reference/p5/createVector/", + "/reference/p5/createVideo/", + "/reference/p5/createWriter/", + "/reference/p5/cursor/", + "/reference/p5/curveDetail/", + "/reference/p5/cylinder/", + "/reference/p5/day/", + "/reference/p5/debugMode/", + "/reference/p5/degrees/", + "/reference/p5/deltaTime/", + "/reference/p5/describe/", + "/reference/p5/describeElement/", + "/reference/p5/deviceMoved/", + "/reference/p5/deviceOrientation/", + "/reference/p5/deviceShaken/", + "/reference/p5/deviceTurned/", + "/reference/p5/directionalLight/", + "/reference/p5/disableFriendlyErrors/", + "/reference/p5/displayDensity/", + "/reference/p5/displayHeight/", + "/reference/p5/displayWidth/", + "/reference/p5/dist/", + "/reference/p5/doubleClicked/", + "/reference/p5/draw/", + "/reference/p5/drawingContext/", + "/reference/p5/ellipse/", + "/reference/p5/ellipseMode/", + "/reference/p5/ellipsoid/", + "/reference/p5/emissiveMaterial/", + "/reference/p5/endClip/", + "/reference/p5/endContour/", + "/reference/p5/endShape/", + "/reference/p5/erase/", + "/reference/p5/exitPointerLock/", + "/reference/p5/exp/", + "/reference/p5/fill/", + "/reference/p5/filter/", + "/reference/p5/filterColor/", + "/reference/p5/finalColor/", + "/reference/p5/float/", + "/reference/p5/floor/", + "/reference/p5/focused/", + "/reference/p5/fontAscent/", + "/reference/p5/fontBounds/", + "/reference/p5/fontDescent/", + "/reference/p5/fontWidth/", + "/reference/p5/for/", + "/reference/p5/fract/", + "/reference/p5/frameCount/", + "/reference/p5/frameRate/", + "/reference/p5/freeGeometry/", + "/reference/p5/fromAxisAngle/", + "/reference/p5/frustum/", + "/reference/p5/fullscreen/", + "/reference/p5/function/", + "/reference/p5/get/", + "/reference/p5/getAudioContext/", + "/reference/p5/getCameraInputs/", + "/reference/p5/getColor/", + "/reference/p5/getFinalColor/", + "/reference/p5/getItem/", + "/reference/p5/getObjectInputs/", + "/reference/p5/getPixelInputs/", + "/reference/p5/getTargetFrameRate/", + "/reference/p5/getTexture/", + "/reference/p5/getURL/", + "/reference/p5/getURLParams/", + "/reference/p5/getURLPath/", + "/reference/p5/getWorldInputs/", + "/reference/p5/green/", + "/reference/p5/gridOutput/", + "/reference/p5/height/", + "/reference/p5/hex/", + "/reference/p5/hour/", + "/reference/p5/httpDo/", + "/reference/p5/httpGet/", + "/reference/p5/httpPost/", + "/reference/p5/hue/", + "/reference/p5/if/", + "/reference/p5/image/", + "/reference/p5/imageLight/", + "/reference/p5/imageMode/", + "/reference/p5/imageShader/", + "/reference/p5/int/", + "/reference/p5/isLooping/", + "/reference/p5/key/", + "/reference/p5/keyCode/", + "/reference/p5/keyIsDown/", + "/reference/p5/keyIsPressed/", + "/reference/p5/keyPressed/", + "/reference/p5/keyReleased/", + "/reference/p5/keyTyped/", + "/reference/p5/lerp/", + "/reference/p5/lerpColor/", + "/reference/p5/let/", + "/reference/p5/lightFalloff/", + "/reference/p5/lightness/", + "/reference/p5/lights/", + "/reference/p5/line/", + "/reference/p5/linePerspective/", + "/reference/p5/loadBlob/", + "/reference/p5/loadBytes/", + "/reference/p5/loadColorShader/", + "/reference/p5/loadFilterShader/", + "/reference/p5/loadFont/", + "/reference/p5/loadImage/", + "/reference/p5/loadJSON/", + "/reference/p5/loadMaterialShader/", + "/reference/p5/loadModel/", + "/reference/p5/loadNormalShader/", + "/reference/p5/loadPixels/", + "/reference/p5/loadShader/", + "/reference/p5/loadSound/", + "/reference/p5/loadStrings/", + "/reference/p5/loadStrokeShader/", + "/reference/p5/loadTable/", + "/reference/p5/loadXML/", + "/reference/p5/log/", + "/reference/p5/loop/", + "/reference/p5/mag/", + "/reference/p5/map/", + "/reference/p5/max/", + "/reference/p5/metalness/", + "/reference/p5/millis/", + "/reference/p5/min/", + "/reference/p5/minute/", + "/reference/p5/model/", + "/reference/p5/month/", + "/reference/p5/mouseButton/", + "/reference/p5/mouseClicked/", + "/reference/p5/mouseDragged/", + "/reference/p5/mouseIsPressed/", + "/reference/p5/mouseMoved/", + "/reference/p5/mousePressed/", + "/reference/p5/mouseReleased/", + "/reference/p5/mouseWheel/", + "/reference/p5/mouseX/", + "/reference/p5/mouseY/", + "/reference/p5/movedX/", + "/reference/p5/movedY/", + "/reference/p5/mult/", + "/reference/p5/nf/", + "/reference/p5/nfc/", + "/reference/p5/nfp/", + "/reference/p5/nfs/", + "/reference/p5/noCanvas/", + "/reference/p5/noCursor/", + "/reference/p5/noDebugMode/", + "/reference/p5/noErase/", + "/reference/p5/noFill/", + "/reference/p5/noLights/", + "/reference/p5/noLoop/", + "/reference/p5/noSmooth/", + "/reference/p5/noStroke/", + "/reference/p5/noTint/", + "/reference/p5/noise/", + "/reference/p5/noiseDetail/", + "/reference/p5/noiseSeed/", + "/reference/p5/norm/", + "/reference/p5/normal/", + "/reference/p5/normalMaterial/", + "/reference/p5/objectInputs/", + "/reference/p5/orbitControl/", + "/reference/p5/ortho/", + "/reference/p5/p5.Camera/", + "/reference/p5/p5.Color/", + "/reference/p5/p5.Element/", + "/reference/p5/p5.File/", + "/reference/p5/p5.Font/", + "/reference/p5/p5.Framebuffer/", + "/reference/p5/p5.Geometry/", + "/reference/p5/p5.Graphics/", + "/reference/p5/p5.Image/", + "/reference/p5/p5.MediaElement/", + "/reference/p5/p5.PrintWriter/", + "/reference/p5/p5.Shader/", + "/reference/p5/p5.Table/", + "/reference/p5/p5.TableRow/", + "/reference/p5/p5.Vector/", + "/reference/p5/p5.XML/", + "/reference/p5/p5/", + "/reference/p5/pAccelerationX/", + "/reference/p5/pAccelerationY/", + "/reference/p5/pAccelerationZ/", + "/reference/p5/pRotationX/", + "/reference/p5/pRotationY/", + "/reference/p5/pRotationZ/", + "/reference/p5/paletteLerp/", + "/reference/p5/panorama/", + "/reference/p5/perspective/", + "/reference/p5/pixelDensity/", + "/reference/p5/pixelInputs/", + "/reference/p5/pixels/", + "/reference/p5/plane/", + "/reference/p5/pmouseX/", + "/reference/p5/pmouseY/", + "/reference/p5/point/", + "/reference/p5/pointLight/", + "/reference/p5/pop/", + "/reference/p5/pow/", + "/reference/p5/print/", + "/reference/p5/push/", + "/reference/p5/pwinMouseX/", + "/reference/p5/pwinMouseY/", + "/reference/p5/quad/", + "/reference/p5/radians/", + "/reference/p5/random/", + "/reference/p5/randomGaussian/", + "/reference/p5/randomSeed/", + "/reference/p5/rect/", + "/reference/p5/rectMode/", + "/reference/p5/red/", + "/reference/p5/redraw/", + "/reference/p5/registerAddon/", + "/reference/p5/remove/", + "/reference/p5/removeElements/", + "/reference/p5/removeItem/", + "/reference/p5/requestPointerLock/", + "/reference/p5/resetMatrix/", + "/reference/p5/resetShader/", + "/reference/p5/resizeCanvas/", + "/reference/p5/roll/", + "/reference/p5/rotate/", + "/reference/p5/rotateBy/", + "/reference/p5/rotateX/", + "/reference/p5/rotateY/", + "/reference/p5/rotateZ/", + "/reference/p5/rotationX/", + "/reference/p5/rotationY/", + "/reference/p5/rotationZ/", + "/reference/p5/round/", + "/reference/p5/saturation/", + "/reference/p5/save/", + "/reference/p5/saveCanvas/", + "/reference/p5/saveFrames/", + "/reference/p5/saveGif/", + "/reference/p5/saveJSON/", + "/reference/p5/saveObj/", + "/reference/p5/saveStl/", + "/reference/p5/saveStrings/", + "/reference/p5/saveTable/", + "/reference/p5/scale/", + "/reference/p5/screenToWorld/", + "/reference/p5/second/", + "/reference/p5/select/", + "/reference/p5/selectAll/", + "/reference/p5/set/", + "/reference/p5/setAttributes/", + "/reference/p5/setAudioContext/", + "/reference/p5/setCamera/", + "/reference/p5/setContent/", + "/reference/p5/setMoveThreshold/", + "/reference/p5/setShakeThreshold/", + "/reference/p5/setup/", + "/reference/p5/shader/", + "/reference/p5/shearX/", + "/reference/p5/shearY/", + "/reference/p5/shininess/", + "/reference/p5/shuffle/", + "/reference/p5/sin/", + "/reference/p5/smooth/", + "/reference/p5/smoothstep/", + "/reference/p5/specularColor/", + "/reference/p5/specularMaterial/", + "/reference/p5/sphere/", + "/reference/p5/spline/", + "/reference/p5/splinePoint/", + "/reference/p5/splineProperties/", + "/reference/p5/splineProperty/", + "/reference/p5/splineTangent/", + "/reference/p5/splineVertex/", + "/reference/p5/splitTokens/", + "/reference/p5/spotLight/", + "/reference/p5/sq/", + "/reference/p5/sqrt/", + "/reference/p5/square/", + "/reference/p5/storeItem/", + "/reference/p5/str/", + "/reference/p5/stroke/", + "/reference/p5/strokeCap/", + "/reference/p5/strokeJoin/", + "/reference/p5/strokeMode/", + "/reference/p5/strokeShader/", + "/reference/p5/strokeWeight/", + "/reference/p5/tan/", + "/reference/p5/text/", + "/reference/p5/textAlign/", + "/reference/p5/textAscent/", + "/reference/p5/textBounds/", + "/reference/p5/textDescent/", + "/reference/p5/textDirection/", + "/reference/p5/textFont/", + "/reference/p5/textLeading/", + "/reference/p5/textOutput/", + "/reference/p5/textProperties/", + "/reference/p5/textProperty/", + "/reference/p5/textSize/", + "/reference/p5/textStyle/", + "/reference/p5/textWeight/", + "/reference/p5/textWidth/", + "/reference/p5/textWrap/", + "/reference/p5/texture/", + "/reference/p5/textureMode/", + "/reference/p5/textureWrap/", + "/reference/p5/tint/", + "/reference/p5/torus/", + "/reference/p5/touches/", + "/reference/p5/translate/", + "/reference/p5/triangle/", + "/reference/p5/turnAxis/", + "/reference/p5/types/Array/", + "/reference/p5/types/Boolean/", + "/reference/p5/types/Number/", + "/reference/p5/types/Object/", + "/reference/p5/types/String/", + "/reference/p5/unchar/", + "/reference/p5/unhex/", + "/reference/p5/updatePixels/", + "/reference/p5/userStartAudio/", + "/reference/p5/userStopAudio/", + "/reference/p5/vertex/", + "/reference/p5/vertexProperty/", + "/reference/p5/webglVersion/", + "/reference/p5/while/", + "/reference/p5/width/", + "/reference/p5/winMouseX/", + "/reference/p5/winMouseY/", + "/reference/p5/windowHeight/", + "/reference/p5/windowResized/", + "/reference/p5/windowWidth/", + "/reference/p5/worldInputs/", + "/reference/p5/worldToScreen/", + "/reference/p5/write/", + "/reference/p5/year/", +]; From a84f674ae84989fb4fa3412fd12e15c35f0d82b6 Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 05:29:33 +0000 Subject: [PATCH 7/9] disable playwright github ci workflow --- .github/workflows/{playwright.yml => playwright.yml.disabled} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{playwright.yml => playwright.yml.disabled} (100%) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml.disabled similarity index 100% rename from .github/workflows/playwright.yml rename to .github/workflows/playwright.yml.disabled From 37e31c130833993ecd468ffe85b1e9490b6957c3 Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 16:42:00 +0000 Subject: [PATCH 8/9] playwright: add note on 404 ref pages --- playwright-tests/reference-links.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/playwright-tests/reference-links.ts b/playwright-tests/reference-links.ts index 4a2ff5baaf..cfb883c537 100644 --- a/playwright-tests/reference-links.ts +++ b/playwright-tests/reference-links.ts @@ -1,5 +1,11 @@ // This is a temporary file for early testing of playwright to find broken sketches in reference pages // eventually these urls would come from the build process (astro's or /docs/reference/data.json) +// There are some paths here which do not end up having a page generated. Including: +// "/reference/p5/types/Array/", +// "/reference/p5/types/Boolean/", +// "/reference/p5/types/Number/", +// "/reference/p5/types/Object/", +// "/reference/p5/types/String/", export const referencePageURLPaths = [ "/reference/p5.Amplitude/getLevel/", "/reference/p5.Amplitude/setInput/", From 674793896c6763e4b7a31bbac217220cc64cb018 Mon Sep 17 00:00:00 2001 From: nbogie Date: Wed, 25 Mar 2026 21:32:56 +0000 Subject: [PATCH 9/9] add playwright script used to test web editor color changes unrelated, but perhaps informative --- ...t-proposed-colour-changes.spec.ts.disabled | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 playwright-tests/screenshot-proposed-colour-changes.spec.ts.disabled diff --git a/playwright-tests/screenshot-proposed-colour-changes.spec.ts.disabled b/playwright-tests/screenshot-proposed-colour-changes.spec.ts.disabled new file mode 100644 index 0000000000..19f5caa266 --- /dev/null +++ b/playwright-tests/screenshot-proposed-colour-changes.spec.ts.disabled @@ -0,0 +1,108 @@ +// Note: how to record test +// 1. in vscode, `View: Show Testing` +// 2. from the Playwright sidebar choose "Record New" (or "Record at cursor") +// 3. wait for it to open GUI tool +// 4. click around +// 5. stop recording in top toolbar of test tool (not vscode) +// 6. script will be written into new editor tab or at cursor (depending on choice in step 2) + + +import { test, type Page } from "@playwright/test"; + +test("screenshot alt colours", async ({ page }) => { + + await page.goto('https://editor.p5js.org/'); + + //accept /dismiss cookie banner + await page.getByRole('button', { name: 'Allow Essential' }).click(); + + //switch on dark mode once for editor + //set dark mode + await page.getByRole('button', { name: 'Open Preferences' }).click(); + await page.getByText('Dark').click(); + await page.getByRole('button', { name: 'Close Settings overlay' }).click(); + + //1. do for settings + await page.getByRole('button', { name: 'p5.js' }).click(); + + await runAllStyleTrials(page, "in-settings"); + + await page.getByRole('button', { name: 'Close Settings overlay' }).click(); + + //2. do for about page + await page.getByRole('menuitem', { name: 'Help' }).click(); + await page.getByRole('menuitem', { name: 'About' }).click(); + await page.getByRole('link', { name: 'Report a Bug' }).scrollIntoViewIfNeeded(); + //alternative: + // await page.evaluate(() => { + // window.scrollTo(0, 500); //some arbitrary y position + // }) + + await runAllStyleTrials(page, "in-about"); + + //3. do for file dialog + await page.getByRole('link', { name: 'Back to Editor' }).click(); + await page.getByRole('menuitem', { name: 'File' }).click(); + await page.getByRole('menuitem', { name: 'Examples' }).click(); + + await runAllStyleTrials(page, "in-file-list"); + + +}); + +async function runAllStyleTrials(page: Page, locationLabel: string) { + const changesets = [ + //Note this automation doesn't tidy up (doesn't reload) after each change, + // so for now these changesets MUST all completely replace each other. + { name: "2-middle-light", style: { color: "#a6a6a6" } }, + { name: "2-medium-light", style: { color: "#d9d9d9" } }, + { name: "2-contrast-pink", style: { color: "#ffa9d9" } }, + { name: "3-p5-pink-ish", style: { color: "#FF6691" } }, + // { name: "1-changed-bg-and-link", style: { background: "#000000", color: "#eb285f" } }, + ] + + + for (const changeset of changesets) { + //not needed + await page.waitForTimeout(500); + //TODO: how much to scroll? + // perhaps until the first relevant is in middle of viewport, ideally? + // await scrollToBottom(page); + + await applyThemeOverride(page, "a", changeset.style); + + await page.screenshot({ path: `${locationLabel}-${changeset.name}.png` }); + } +} + + + + + +/** + * Injects a temporary CSS override into the page. + */ +async function applyThemeOverride(page: Page, selector: string, style: { color: string }): Promise { + + await page.addStyleTag({ + + content: ` + ${selector} { + color: ${style.color} !important; + } +` + }); + + const cssForBackgroundOverridesUNUSED = ` +.preference {background: #000000} + +tr.sketches-table__row { + background: #000000 !important; +} + +.RootPage-sc-1nw49or-0 { + background:#000000; +} +`; + +}