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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ From geckodriver 8 and edgedriver 8, `@wdio/logger` is a peer dependency, so the
- `edgedriver`'s `start()` resolves to a `ChildProcess` (it was typed `ChildProcessWithoutNullStreams`): its `stdout` and `stderr` are `null` when you pass `spawnOpts: { stdio: 'ignore' }`.
- `safaridriver` now pipes the driver output like the other drivers: read `stdout` and `stderr`, or pass `spawnOpts: { stdio: 'ignore' }`. Before, the output was buffered and the driver was killed after 1 MB of it.
- The `edgedriver` and `geckodriver` CLIs exit with code 1 when a signal kills the driver; they exited with 0.
- On Windows, `EDGEDRIVER_AUTO_INSTALL` and `GECKODRIVER_AUTO_INSTALL` now download the driver during the install; before, the install always skipped the download there.
- `findEdgePath()` on Windows returns `undefined` when Edge is not installed, as on macOS and Linux; it threw.
- `spawnOpts` is a new option of `edgedriver` and `safaridriver`. The rest of the API, the CLI and the options did not change.

Expand Down Expand Up @@ -84,6 +85,8 @@ By default, binaries download when initialized via CLI or API. To download them
* **Geckodriver:** `GECKODRIVER_AUTO_INSTALL=1 npm i`
* **Edgedriver:** `EDGEDRIVER_AUTO_INSTALL=1 npm i`

The download runs in each package's `postinstall` script. pnpm 10 or newer and Bun run it only for packages you approve (`pnpm approve-builds`, or `trustedDependencies` in Bun), and recent npm versions warn until you approve them with `npm install-scripts approve <package>`. If the download fails, the install does not fail: the driver then downloads on first use.

#### Version Pinning & Custom Sources

* **Custom Driver Version:**
Expand Down
2 changes: 2 additions & 0 deletions packages/node-edgedriver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ By default, this package downloads Edgedriver when used for the first time throu
EDGEDRIVER_AUTO_INSTALL=1 npm i
```

The download runs in the package's `postinstall` script. pnpm 10 or newer and Bun run it only for a package you approve (`pnpm approve-builds`, or `trustedDependencies` in Bun), and recent npm versions warn until you approve it with `npm install-scripts approve edgedriver`. If the download fails, the install does not fail: the driver then downloads on first use.

To get a list of available CLI options run `npx edgedriver --help`. By default this package tries to find the Microsoft Edge version installed on a given system. If you prefer to have it install a custom EdgeDriver version you can define the environment variable `EDGEDRIVER_VERSION` when running in CLI, e.g.:

```sh
Expand Down
2 changes: 1 addition & 1 deletion packages/node-edgedriver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"scripts": {
"build": "tsc -b . && node ../../scripts/copy-cjs-pkg.mjs",
"clean": "rimraf ./dist ./tsconfig.tsbuildinfo",
"postinstall": "test -f ./dist/install.js && node ./dist/install.js || echo \"Skipping install, project not built!\"",
"postinstall": "node -e \"if (require('fs').existsSync('./dist/install.js')) { process.exit(1) } console.log('Skipping install, project not built!')\" || node ./dist/install.js || echo Skipping install, the driver download failed.",
"test": "run-s test:*",
"test:lint": "oxlint .",
"test:typecheck": "tsc -p tsconfig.test.json",
Expand Down
54 changes: 54 additions & 0 deletions packages/node-edgedriver/tests/postinstall.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { execSync } from 'node:child_process'
import { afterEach, beforeEach, expect, test } from 'vitest'

import pkg from '../package.json' with { type: 'json' }

/**
* Runs the real `postinstall` script with npm's default shell: `sh`, or `cmd.exe` on Windows.
* PATH has only Node.js and the system tools: a Windows CI runner also has Git's Unix tools
* (`test.exe`), which a user's Windows usually has not.
*/
const PATH = [path.dirname(process.execPath), ...(process.platform === 'win32'
? [path.join(process.env.SystemRoot ?? 'C:\\Windows', 'System32')]
: ['/usr/bin', '/bin'])].join(path.delimiter)
let root = ''
const postinstall = () => execSync(pkg.scripts.postinstall, {
cwd: root,
encoding: 'utf8',
stdio: 'pipe',
// Windows reads `Path`; one key only, so the restricted value wins
env: { ...Object.fromEntries(Object.entries(process.env).filter(([key]) => key.toUpperCase() !== 'PATH')), PATH },
})
const writeInstallJs = (body: string) => {
fs.mkdirSync(path.join(root, 'dist'))
fs.writeFileSync(path.join(root, 'dist', 'install.js'), body)
}

beforeEach(() => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'postinstall-'))
// the stubs below are CommonJS
fs.writeFileSync(path.join(root, 'package.json'), '{"type":"commonjs"}')
})

afterEach(() => {
fs.rmSync(root, { recursive: true, force: true })
})

test('runs dist/install.js when it is built, on every OS', () => {
writeInstallJs('require("fs").writeFileSync("ran", process.argv[1])')
postinstall()
// install.js only downloads when it is the entry point
expect(fs.readFileSync(path.join(root, 'ran'), 'utf8')).toMatch(/dist[\\/]install\.js$/)
})

test('skips without failing when the package is not built', () => {
expect(postinstall()).toContain('Skipping install, project not built!')
})

test('a failing download does not fail the install', () => {
writeInstallJs('throw new Error("no network")')
expect(() => postinstall()).not.toThrow()
})
2 changes: 2 additions & 0 deletions packages/node-geckodriver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ By default, this package downloads Geckodriver when used for the first time thro
GECKODRIVER_AUTO_INSTALL=1 npm i
```

The download runs in the package's `postinstall` script. pnpm 10 or newer and Bun run it only for a package you approve (`pnpm approve-builds`, or `trustedDependencies` in Bun), and recent npm versions warn until you approve it with `npm install-scripts approve geckodriver`. If the download fails, the install does not fail: the driver then downloads on first use.

To get a list of available CLI options run `npx geckodriver --help`. By default, this package downloads the latest version of the driver. If you prefer to have it install a custom Geckodriver version you can define the environment variable `GECKODRIVER_VERSION` when running in CLI, e.g.:

```sh
Expand Down
2 changes: 1 addition & 1 deletion packages/node-geckodriver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"scripts": {
"build": "tsc -b . && node ../../scripts/copy-cjs-pkg.mjs",
"clean": "rimraf ./dist ./tsconfig.tsbuildinfo",
"postinstall": "test -f ./dist/install.js && node ./dist/install.js || echo \"Skipping install, project not built!\"",
"postinstall": "node -e \"if (require('fs').existsSync('./dist/install.js')) { process.exit(1) } console.log('Skipping install, project not built!')\" || node ./dist/install.js || echo Skipping install, the driver download failed.",
"test": "run-s build test:*",
"test:lint": "oxlint .",
"test:typecheck": "tsc -p tsconfig.test.json",
Expand Down
54 changes: 54 additions & 0 deletions packages/node-geckodriver/tests/postinstall.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { execSync } from 'node:child_process'
import { afterEach, beforeEach, expect, test } from 'vitest'

import pkg from '../package.json' with { type: 'json' }

/**
* Runs the real `postinstall` script with npm's default shell: `sh`, or `cmd.exe` on Windows.
* PATH has only Node.js and the system tools: a Windows CI runner also has Git's Unix tools
* (`test.exe`), which a user's Windows usually has not.
*/
const PATH = [path.dirname(process.execPath), ...(process.platform === 'win32'
? [path.join(process.env.SystemRoot ?? 'C:\\Windows', 'System32')]
: ['/usr/bin', '/bin'])].join(path.delimiter)
let root = ''
const postinstall = () => execSync(pkg.scripts.postinstall, {
cwd: root,
encoding: 'utf8',
stdio: 'pipe',
// Windows reads `Path`; one key only, so the restricted value wins
env: { ...Object.fromEntries(Object.entries(process.env).filter(([key]) => key.toUpperCase() !== 'PATH')), PATH },
})
const writeInstallJs = (body: string) => {
fs.mkdirSync(path.join(root, 'dist'))
fs.writeFileSync(path.join(root, 'dist', 'install.js'), body)
}

beforeEach(() => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'postinstall-'))
// the stubs below are CommonJS
fs.writeFileSync(path.join(root, 'package.json'), '{"type":"commonjs"}')
})

afterEach(() => {
fs.rmSync(root, { recursive: true, force: true })
})

test('runs dist/install.js when it is built, on every OS', () => {
writeInstallJs('require("fs").writeFileSync("ran", process.argv[1])')
postinstall()
// install.js only downloads when it is the entry point
expect(fs.readFileSync(path.join(root, 'ran'), 'utf8')).toMatch(/dist[\\/]install\.js$/)
})

test('skips without failing when the package is not built', () => {
expect(postinstall()).toContain('Skipping install, project not built!')
})

test('a failing download does not fail the install', () => {
writeInstallJs('throw new Error("no network")')
expect(() => postinstall()).not.toThrow()
})
Loading