Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,60 @@ npx playwright install --with-deps chromium # once per machine: downloads the
npm run test:e2e
```

`--with-deps` runs `apt-get install`, so it needs root. Without the OS packages
the browser bundle is present but will not launch, and every test fails with
`browserType.launch: Target page, context or browser has been closed`, which
names neither the missing library nor the install step. On a machine where you
are not root (an unprivileged container, a shared dev host), unpack the same
packages into a private sysroot and point the loader at it instead. The browser
bundle itself installs fine as an ordinary user:

<!-- cspell:disable -->

```bash
npx playwright install chromium # the browser bundle only, no --with-deps
mkdir -p /tmp/sysroot/apt/{state,cache}/ /tmp/debs
printf 'Dir::State "/tmp/sysroot/apt/state";\nDir::State::status "/tmp/sysroot/apt/state/status";\nDir::Cache "/tmp/sysroot/apt/cache";\n' > /tmp/sysroot/apt.conf
touch /tmp/sysroot/apt/state/status
export APT_CONFIG=/tmp/sysroot/apt.conf
apt-get update
cd /tmp/debs && apt-get download \
libglib2.0-0t64 libnspr4 libnss3 libdbus-1-3 libatk1.0-0t64 \
libatk-bridge2.0-0t64 libatspi2.0-0t64 libx11-6 libxcomposite1 libxdamage1 \
libxext6 libxfixes3 libxrandr2 libgbm1 libxcb1 libxkbcommon0 libasound2t64 \
libpango-1.0-0 libcairo2 libcups2t64 libexpat1 libudev1 libdrm2 \
libwayland-client0 libxrender1 libxau6 libxdmcp6 libxi6 \
libavahi-common3 libavahi-client3 libpng16-16t64 libfontconfig1 libfreetype6 \
libxcb-render0 libxcb-shm0 libpixman-1-0 libfribidi0 libthai0 libharfbuzz0b \
libgraphite2-3 libthai-data libdatrie1 libbrotli1 libbsd0 libmd0
for d in *.deb; do dpkg-deb -x "$d" /tmp/sysroot/fs; done
cd - && export LD_LIBRARY_PATH=/tmp/sysroot/fs/usr/lib/x86_64-linux-gnu:/tmp/sysroot/fs/lib/x86_64-linux-gnu
```

<!-- cspell:enable -->

The first 28 names are the packages Playwright itself installs on Debian and
Ubuntu plus the four the `chrome-headless-shell` binary needs beyond them; the
last 17 are what the full `chrome` binary additionally loads, so a headed or
`channel: 'chromium'` run works too. Both binaries have to resolve, so check
both. Zero `not found` lines on each is the goal:

```bash
for b in ~/.cache/ms-playwright/chromium*/*/chrome \
~/.cache/ms-playwright/chromium*/*/chrome-headless-shell; do
echo "$b: $(ldd "$b" | grep -c 'not found') missing"
done
```

Two things that look like failures and are not: `apt-get download` may print
`rm: cannot remove '/var/cache/apt/archives/partial/*.deb': Permission denied`
on a host whose `/var/cache/apt` is root-owned (the download still succeeds and
exits 0), and the `LD_LIBRARY_PATH` export is per shell, so set it again in a
new terminal before `npm run test:e2e`. Do not set `XDG_CACHE_HOME` for the run:
Playwright resolves the downloaded browser under it, and overriding it makes
every test fail with "Executable doesn't exist at ...". On an `arm64` host
substitute `aarch64-linux-gnu` for `x86_64-linux-gnu` in the loader path.

The suite starts its own server on `localhost:3000` (override with `E2E_PORT`)
and serves the static output already built in `build/` via `docusaurus serve` —
it does not rebuild your changes, so re-run `npm run build:production` after
Expand Down
28 changes: 26 additions & 2 deletions tests/contributing-e2e-setup.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ function workflowRunSteps() {
// naming the real cause. ci.yml never hits that because it always installs the
// packages -- `--with-deps` on a cache miss, `install-deps` when the cached
// bundle is restored.
//
// Both of those need root. The one documented way to get the packages without
// it is to unpack the same .debs into a private sysroot (`dpkg-deb -x`) and
// hand the loader that directory through LD_LIBRARY_PATH, which is the only
// context in which a bare `npx playwright install chromium` is complete.
function documentsSystemPackages(source) {
if (/npx playwright install-deps\b/.test(source)) return true;
return /dpkg-deb -x\b/.test(source) && /LD_LIBRARY_PATH=/.test(source);
}

test('the docs install Playwright the way ci.yml does, system packages included', () => {
const workflowInstallsDeps = workflowRunSteps().some(
(run) =>
Expand All @@ -68,10 +78,10 @@ test('the docs install Playwright the way ci.yml does, system packages included'
// so it must not satisfy the browser-install match on its own.
const installs = source.match(/npx playwright install(?!-deps)\b[^\n`]*/g);
if (!installs) continue;
const mentionsStandaloneDeps = /npx playwright install-deps\b/.test(source);
const packagesDocumented = documentsSystemPackages(source);
for (const invocation of installs) {
if (invocation.includes('--with-deps')) continue;
if (mentionsStandaloneDeps) continue;
if (packagesDocumented) continue;
incomplete.push(`${doc}: ${invocation.trim()}`);
}
}
Expand All @@ -83,6 +93,20 @@ test('the docs install Playwright the way ci.yml does, system packages included'
);
});

test('a bare browser install is complete only beside a way to get the packages', () => {
assert.ok(documentsSystemPackages('npx playwright install-deps chromium'));
assert.ok(
documentsSystemPackages(
'for d in *.deb; do dpkg-deb -x "$d" /tmp/sysroot/fs; done\nexport LD_LIBRARY_PATH=/tmp/sysroot/fs/usr/lib',
),
);
// Unpacking without pointing the loader at the result launches nothing, and
// naming the variable without unpacking anything has nothing to point at.
assert.ok(!documentsSystemPackages('dpkg-deb -x pkg.deb /tmp/sysroot/fs'));
assert.ok(!documentsSystemPackages('export LD_LIBRARY_PATH=/tmp/sysroot'));
assert.ok(!documentsSystemPackages('npx playwright install chromium'));
});

// `report:e2e:coverage` is a gate, not a readout: the e2e-coverage job hands it
// --check-source, --check-source-regions and --require-source-files, and
// tests/e2e-coverage-gate.test.mjs holds floors under the first two. A recipe
Expand Down
Loading