diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..40fb2cd
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+pnpm run test:lint
diff --git a/.husky/pre-push b/.husky/pre-push
new file mode 100755
index 0000000..98475b5
--- /dev/null
+++ b/.husky/pre-push
@@ -0,0 +1 @@
+pnpm test
diff --git a/README.md b/README.md
index 110feec..701971c 100644
--- a/README.md
+++ b/README.md
@@ -106,7 +106,7 @@ const browser = await remote({
}
});
-await browser.url('[https://webdriver.io](https://webdriver.io)');
+await browser.url('https://webdriver.io');
console.log(await browser.getTitle());
// 4. Terminate process when finished
@@ -121,7 +121,7 @@ import safaridriver from 'safaridriver';
import { remote } from 'webdriverio';
// 1. Start Safaridriver server
-await safaridriver.start({ port: 4444 });
+safaridriver.start({ port: 4444 });
// 2. Connect WebdriverIO session
const browser = await remote({
@@ -130,7 +130,7 @@ const browser = await remote({
}
});
-await browser.url('[https://webdriver.io](https://webdriver.io)');
+await browser.url('https://webdriver.io');
console.log(await browser.getTitle());
// 3. Stop Safaridriver process
@@ -142,7 +142,7 @@ safaridriver.stop();
## Configuration & Options
-### Options for `geckodriver` & `edgedriver`
+### Options for `geckodriver`
Passed into the `start(options)` method:
@@ -150,12 +150,28 @@ Passed into the `start(options)` method:
| --- | --- | --- | --- |
| `port` | `number` | — | Port to listen on. |
| `host` | `string` | `0.0.0.0` | Host IP address to bind server. |
-| `customGeckoDriverPath` / `customEdgeDriverPath` | `string` | `process.env.GECKODRIVER_PATH` | Path to custom/cached driver binary. |
-| `cacheDir` | `string` | `os.tmpdir()` | Root directory for caching downloaded binaries. |
+| `customGeckoDriverPath` | `string` | `process.env.GECKODRIVER_PATH` | Path to custom/cached driver binary. |
+| `cacheDir` | `string` | `process.env.GECKODRIVER_CACHE_DIR \|\| os.tmpdir()` | Root directory for caching downloaded binaries. |
| `spawnOpts` | `object` | `undefined` | Spawn options passed directly to Node.js `child_process.spawn`. |
| `allowHosts` | `string[]` | `[]` | List of explicit host names allowed to connect. |
| `allowOrigins` | `string[]` | `[]` | List of allowed request origins (`scheme://host:port`). |
+See the [geckodriver README](packages/node-geckodriver/README.md) for the full list of options.
+
+### Options for `edgedriver`
+
+Passed into the `start(options)` method:
+
+| Option | Type | Default | Description |
+| --- | --- | --- | --- |
+| `port` | `number` | — | Port to listen on. |
+| `customEdgeDriverPath` | `string` | `process.env.EDGEDRIVER_PATH` | Path to custom/cached driver binary. |
+| `cacheDir` | `string` | `process.env.EDGEDRIVER_CACHE_DIR \|\| os.tmpdir()` | Root directory for caching downloaded binaries. |
+| `allowedIps` | `string[]` | `['']` | List of remote IP addresses allowed to connect. |
+| `allowedOrigins` | `string[]` | `['*']` | List of allowed request origins. Using `*` to allow any origin is dangerous! |
+
+See the [edgedriver README](packages/node-edgedriver/README.md) for the full list of options.
+
### Options for `safaridriver`
Passed into `safaridriver.start(options)`:
@@ -172,8 +188,4 @@ Passed into `safaridriver.start(options)`:
## License
-[MIT](https://www.google.com/search?q=LICENSE&utm_source=gemini)
-
-```
-
-```
+[MIT](LICENSE)
diff --git a/package.json b/package.json
index edefdba..11687da 100644
--- a/package.json
+++ b/package.json
@@ -17,9 +17,9 @@
"checks:all": "pnpm run test:lint && pnpm run test:unit && pnpm run test:e2e"
},
"devDependencies": {
+ "@stylistic/eslint-plugin": "^5.10.0",
"@types/node": "^25.0.3",
"@types/which": "^3.0.4",
- "@stylistic/eslint-plugin": "^5.10.0",
"@vitest/coverage-v8": "^4.1.11",
"husky": "^9.1.7",
"npm-run-all2": "^9.0.3",
diff --git a/packages/node-edgedriver/.npmignore b/packages/node-edgedriver/.npmignore
index 02c133b..c1b2ee7 100644
--- a/packages/node-edgedriver/.npmignore
+++ b/packages/node-edgedriver/.npmignore
@@ -2,7 +2,7 @@ src
tests
coverage
*.tgz
+*.tsbuildinfo
/.*
/*.mjs
/tsconfig.json
-vitest.config.ts
diff --git a/packages/node-edgedriver/README.md b/packages/node-edgedriver/README.md
index 112cae9..ca33df3 100644
--- a/packages/node-edgedriver/README.md
+++ b/packages/node-edgedriver/README.md
@@ -1,7 +1,7 @@
EdgeDriver [](https://github.com/webdriverio-community/node-edgedriver/actions/workflows/ci.yml) [](https://github.com/webdriverio-community/node-edgedriver/actions/workflows/audit.yml)
==========
-An NPM wrapper for Microsofts' [EdgeDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/). It manages to download various (or the latest) Edgedriver versions and provides a programmatic interface to start and stop it within Node.js. __Note:__ this is a wrapper module. If you discover any bugs with EdgeDriver, please report them in the [official repository](https://github.com/MicrosoftEdge/EdgeWebDriver).
+An NPM wrapper for Microsoft's [EdgeDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/). It manages to download various (or the latest) Edgedriver versions and provides a programmatic interface to start and stop it within Node.js. __Note:__ this is a wrapper module. If you discover any bugs with EdgeDriver, please report them in the [official repository](https://github.com/MicrosoftEdge/EdgeWebDriver).
# Installing
@@ -23,7 +23,7 @@ By default, this package downloads Edgedriver when used for the first time throu
EDGEDRIVER_AUTO_INSTALL=1 npm i
```
-To get a list of available CLI options run `npx edgedriver --help`. By default this package tries to find the Mircosoft 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.:
+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
$ npm i edgedriver
@@ -124,7 +124,7 @@ The `start` method offers the following options to be passed on to the actual Ed
### edgeDriverVersion
-The version of EdgeDriver to start. See [Egdedriver directory list](https://msedgewebdriverstorage.z22.web.core.windows.net/) for all available versions, platforms and architecture.
+The version of EdgeDriver to start. See [Edgedriver directory list](https://msedgewebdriverstorage.z22.web.core.windows.net/) for all available versions, platforms and architecture.
Type: `number`
Default: `latest`
diff --git a/packages/node-edgedriver/package.json b/packages/node-edgedriver/package.json
index 389f93f..df47674 100644
--- a/packages/node-edgedriver/package.json
+++ b/packages/node-edgedriver/package.json
@@ -1,7 +1,7 @@
{
"name": "edgedriver",
"version": "6.3.1",
- "description": "Microsofts' EdgeDriver for Node.js",
+ "description": "Microsoft's EdgeDriver for Node.js",
"homepage": "https://webdriver.io",
"license": "MIT",
"author": {
@@ -40,7 +40,7 @@
"scripts": {
"build": "tsc -b . && node ../../scripts/copy-cjs-pkg.mjs",
"clean": "rimraf ./dist",
- "postinstall": "test -f ./dist/install.js && node ./dist/install.js || echo \"Skipping install, project not build!\"",
+ "postinstall": "test -f ./dist/install.js && node ./dist/install.js || echo \"Skipping install, project not built!\"",
"test": "run-s test:*",
"test:lint": "oxlint .",
"test:unit": "vitest --run",
diff --git a/packages/node-edgedriver/src/install.ts b/packages/node-edgedriver/src/install.ts
index 25f4c28..097c779 100644
--- a/packages/node-edgedriver/src/install.ts
+++ b/packages/node-edgedriver/src/install.ts
@@ -205,11 +205,20 @@ export async function fetchVersion (edgeVersion: string) {
async function downloadZip(res: Awaited>, cacheDir: string) {
const zipBlob = await res.blob()
const zip = new ZipReader(new BlobReader(zipBlob))
+ const resolvedCacheDir = path.resolve(cacheDir)
for (const entry of await zip.getEntries()) {
const unzippedFilePath = path.join(cacheDir, entry.filename)
if (entry.directory) {
continue
}
+ /**
+ * guard against Zip Slip: a malicious archive could contain entries
+ * with `../` or absolute paths that escape the cache directory
+ */
+ const resolvedPath = path.resolve(unzippedFilePath)
+ if (resolvedPath !== resolvedCacheDir && !resolvedPath.startsWith(resolvedCacheDir + path.sep)) {
+ throw new Error(`Zip entry "${entry.filename}" resolves outside the cache directory`)
+ }
const fileEntry = entry as FileEntry
if (!await hasAccess(path.dirname(unzippedFilePath))) {
await fsp.mkdir(path.dirname(unzippedFilePath), { recursive: true })
@@ -227,10 +236,22 @@ function sanitizeVersion (version: string) {
return version.replace(/\0/g, '').slice(2).trim()
}
+/**
+ * True when this module was invoked directly as the package's postinstall
+ * entrypoint (`dist/install.js`), as opposed to being imported as a dependency.
+ */
+export function isAutoInstallEntrypoint (argv1: string | undefined) {
+ if (!argv1) {
+ return false
+ }
+ const installJsPath = path.join('dist', 'install.js')
+ return path.normalize(argv1).endsWith(path.sep + installJsPath)
+}
+
/**
* download on install
*/
-if (process.argv[1] && process.argv[1].endsWith('/dist/install.js') && Boolean(process.env.EDGEDRIVER_AUTO_INSTALL)) {
+if (isAutoInstallEntrypoint(process.argv[1]) && Boolean(process.env.EDGEDRIVER_AUTO_INSTALL)) {
await download().then(
() => log.info('Success!'),
(err) => log.error(`Failed to install Edgedriver: ${err.stack}`)
diff --git a/packages/node-edgedriver/src/types.ts b/packages/node-edgedriver/src/types.ts
index e924678..dfeca66 100644
--- a/packages/node-edgedriver/src/types.ts
+++ b/packages/node-edgedriver/src/types.ts
@@ -68,7 +68,7 @@ export interface EdgedriverParameters {
allowedOrigins?: string[]
/**
* The path to the root of the cache directory.
- * @default process.env.GECKODRIVER_CACHE_DIR || os.tmpdir()
+ * @default process.env.EDGEDRIVER_CACHE_DIR || os.tmpdir()
*/
cacheDir?: string
}
diff --git a/packages/node-edgedriver/tests/__snapshots__/unit.test.ts.snap b/packages/node-edgedriver/tests/__snapshots__/utils.test.ts.snap
similarity index 100%
rename from packages/node-edgedriver/tests/__snapshots__/unit.test.ts.snap
rename to packages/node-edgedriver/tests/__snapshots__/utils.test.ts.snap
diff --git a/packages/node-edgedriver/tests/index.test.ts b/packages/node-edgedriver/tests/index.test.ts
new file mode 100644
index 0000000..a2ff226
--- /dev/null
+++ b/packages/node-edgedriver/tests/index.test.ts
@@ -0,0 +1,9 @@
+import { test, expect } from 'vitest'
+
+import * as pkgExports from '../src/index.js'
+
+test('exports', () => {
+ expect(typeof pkgExports.download).toBe('function')
+ expect(typeof pkgExports.findEdgePath).toBe('function')
+ expect(typeof pkgExports.start).toBe('function')
+})
diff --git a/packages/node-edgedriver/tests/unit.test.ts b/packages/node-edgedriver/tests/install.test.ts
similarity index 59%
rename from packages/node-edgedriver/tests/unit.test.ts
rename to packages/node-edgedriver/tests/install.test.ts
index bda0f60..309bcdd 100644
--- a/packages/node-edgedriver/tests/unit.test.ts
+++ b/packages/node-edgedriver/tests/install.test.ts
@@ -1,18 +1,47 @@
import os from 'node:os'
-import { vi, test, expect, describe } from 'vitest'
+import path from 'node:path'
+import fsp from 'node:fs/promises'
+import { vi, test, expect, describe, beforeEach } from 'vitest'
-import * as pkgExports from '../src/index.js'
-import { fetchVersion } from '../src/install.js'
-import { getNameByArchitecture, parseParams, extractBasicAuthFromUrl } from '../src/utils.js'
+import { fetchVersion, download, isAutoInstallEntrypoint } from '../src/install.js'
import { EDGE_PRODUCTS_API } from '../src/constants.js'
+// All vi.mock calls must be at module scope so Vitest hoists them before any
+// imports — mocks inside test() bodies are not hoisted.
vi.mock('node:os', () => ({
default: {
arch: vi.fn(),
- platform: vi.fn()
+ platform: vi.fn(),
+ tmpdir: vi.fn(() => '/tmp')
}
}))
+vi.mock('node:fs/promises', () => ({
+ default: {
+ access: vi.fn(),
+ mkdir: vi.fn().mockResolvedValue(undefined),
+ chmod: vi.fn().mockResolvedValue(undefined),
+ },
+ writeFile: vi.fn().mockResolvedValue(undefined),
+}))
+
+vi.mock('../src/utils.js', async (original) => {
+ const actual: any = await original()
+ return {
+ ...actual,
+ hasAccess: vi.fn(),
+ }
+})
+
+const zipState = vi.hoisted(() => ({ entries: [] as any[] }))
+vi.mock('@zip.js/zip.js', () => ({
+ BlobReader: class { },
+ BlobWriter: class { },
+ ZipReader: class {
+ getEntries() { return Promise.resolve(zipState.entries) }
+ },
+}))
+
// Mock the global fetch function
const mockFetch = vi.fn()
vi.stubGlobal('fetch', mockFetch)
@@ -127,59 +156,50 @@ describe('fetchVersion', () => {
})
})
-test('getNameByArchitecture', () => {
- vi.mocked(os.arch).mockReturnValue('arm')
- vi.mocked(os.platform).mockReturnValue('linux')
- expect(getNameByArchitecture()).toBe('edgedriver_linux32')
- vi.mocked(os.arch).mockReturnValue('arm64')
- vi.mocked(os.platform).mockReturnValue('linux')
- expect(getNameByArchitecture()).toBe('edgedriver_linux64')
- vi.mocked(os.arch).mockReturnValue('arm')
- vi.mocked(os.platform).mockReturnValue('win32')
- expect(getNameByArchitecture()).toBe('edgedriver_win32')
- vi.mocked(os.arch).mockReturnValue('arm64')
- vi.mocked(os.platform).mockReturnValue('win32')
- expect(getNameByArchitecture()).toBe('edgedriver_win64')
- vi.mocked(os.arch).mockReturnValue('x64')
- vi.mocked(os.platform).mockReturnValue('darwin')
- expect(getNameByArchitecture()).toBe('edgedriver_mac64')
- vi.mocked(os.arch).mockReturnValue('arm64')
- vi.mocked(os.platform).mockReturnValue('darwin')
- expect(getNameByArchitecture()).toBe('edgedriver_mac64_m1')
-})
-
-test('parseParams', () => {
- expect(parseParams({ baseUrl: 'foobar', silent: true, verbose: false, allowedIps: ['123', '321'] }))
- .toMatchSnapshot()
-})
-
-test('exports', () => {
- expect(typeof pkgExports.download).toBe('function')
- expect(typeof pkgExports.findEdgePath).toBe('function')
- expect(typeof pkgExports.start).toBe('function')
-})
+describe('isAutoInstallEntrypoint', () => {
+ // uses the native `path` module (matching the implementation), so this
+ // only proves correctness for the host OS running the test — real
+ // cross-platform coverage (including Windows) comes from CI running this
+ // same file on the macOS/Ubuntu/Windows matrix, not from simulating
+ // another platform's paths in-process.
+ test('matches when argv[1] points at dist/install.js', () => {
+ const installJsPath = path.join('dist', 'install.js')
+ expect(isAutoInstallEntrypoint(path.join('/home/user/node_modules/edgedriver', installJsPath))).toBe(true)
+ })
-test('extractBasicAuthFromUrl with credentials', () => {
- const result = extractBasicAuthFromUrl('https://myuser:mypassword@cdn.example.com/path/file.zip')
- expect(result.url).toBe('https://cdn.example.com/path/file.zip')
- expect(result.authHeader).toBe('Basic ' + Buffer.from('myuser:mypassword').toString('base64'))
-})
+ test('does not match when the package is only imported as a dependency', () => {
+ expect(isAutoInstallEntrypoint(path.join('/home/user/my-project/index.js'))).toBe(false)
+ })
-test('extractBasicAuthFromUrl without credentials', () => {
- const result = extractBasicAuthFromUrl('https://cdn.example.com/path/file.zip')
- expect(result.url).toBe('https://cdn.example.com/path/file.zip')
- expect(result.authHeader).toBeUndefined()
+ test('does not match when argv[1] is undefined', () => {
+ expect(isAutoInstallEntrypoint(undefined)).toBe(false)
+ })
})
-test('extractBasicAuthFromUrl with only username', () => {
- const result = extractBasicAuthFromUrl('https://myuser@cdn.example.com/path/file.zip')
- expect(result.url).toBe('https://cdn.example.com/path/file.zip')
- expect(result.authHeader).toBe('Basic ' + Buffer.from('myuser:').toString('base64'))
-})
+describe('download', () => {
+ const CACHE_DIR = path.resolve(os.tmpdir(), 'test-cache')
+ let hasAccess: ReturnType
+
+ beforeEach(async () => {
+ const utils = await import('../src/utils.js')
+ hasAccess = vi.mocked(utils.hasAccess)
+ hasAccess.mockResolvedValue(false) // cache miss
+ vi.mocked(fsp.mkdir).mockClear()
+ vi.mocked(fsp.chmod).mockClear()
+ mockFetch.mockReset()
+ })
-test('extractBasicAuthFromUrl with invalid URL returns original', () => {
- const result = extractBasicAuthFromUrl('not-a-valid-url')
- expect(result.url).toBe('not-a-valid-url')
- expect(result.authHeader).toBeUndefined()
+ test('rejects zip entries that escape the cache directory (Zip Slip)', async () => {
+ mockFetch.mockResolvedValue({
+ ok: true,
+ status: 200,
+ body: {},
+ blob: vi.fn().mockResolvedValue(new Blob([])),
+ })
+ zipState.entries = [
+ { filename: '../../evil.exe', directory: false, getData: vi.fn() },
+ ]
+
+ await expect(download('123.456.789.0', CACHE_DIR)).rejects.toThrow('resolves outside the cache directory')
+ })
})
-
diff --git a/packages/node-edgedriver/tests/utils.test.ts b/packages/node-edgedriver/tests/utils.test.ts
new file mode 100644
index 0000000..8b1dab8
--- /dev/null
+++ b/packages/node-edgedriver/tests/utils.test.ts
@@ -0,0 +1,63 @@
+import os from 'node:os'
+import { vi, test, expect, describe } from 'vitest'
+
+import { getNameByArchitecture, parseParams, extractBasicAuthFromUrl } from '../src/utils.js'
+
+vi.mock('node:os', () => ({
+ default: {
+ arch: vi.fn(),
+ platform: vi.fn()
+ }
+}))
+
+test('getNameByArchitecture', () => {
+ vi.mocked(os.arch).mockReturnValue('arm')
+ vi.mocked(os.platform).mockReturnValue('linux')
+ expect(getNameByArchitecture()).toBe('edgedriver_linux32')
+ vi.mocked(os.arch).mockReturnValue('arm64')
+ vi.mocked(os.platform).mockReturnValue('linux')
+ expect(getNameByArchitecture()).toBe('edgedriver_linux64')
+ vi.mocked(os.arch).mockReturnValue('arm')
+ vi.mocked(os.platform).mockReturnValue('win32')
+ expect(getNameByArchitecture()).toBe('edgedriver_win32')
+ vi.mocked(os.arch).mockReturnValue('arm64')
+ vi.mocked(os.platform).mockReturnValue('win32')
+ expect(getNameByArchitecture()).toBe('edgedriver_win64')
+ vi.mocked(os.arch).mockReturnValue('x64')
+ vi.mocked(os.platform).mockReturnValue('darwin')
+ expect(getNameByArchitecture()).toBe('edgedriver_mac64')
+ vi.mocked(os.arch).mockReturnValue('arm64')
+ vi.mocked(os.platform).mockReturnValue('darwin')
+ expect(getNameByArchitecture()).toBe('edgedriver_mac64_m1')
+})
+
+test('parseParams', () => {
+ expect(parseParams({ baseUrl: 'foobar', silent: true, verbose: false, allowedIps: ['123', '321'] }))
+ .toMatchSnapshot()
+})
+
+describe('extractBasicAuthFromUrl', () => {
+ test('with credentials', () => {
+ const result = extractBasicAuthFromUrl('https://myuser:mypassword@cdn.example.com/path/file.zip')
+ expect(result.url).toBe('https://cdn.example.com/path/file.zip')
+ expect(result.authHeader).toBe('Basic ' + Buffer.from('myuser:mypassword').toString('base64'))
+ })
+
+ test('without credentials', () => {
+ const result = extractBasicAuthFromUrl('https://cdn.example.com/path/file.zip')
+ expect(result.url).toBe('https://cdn.example.com/path/file.zip')
+ expect(result.authHeader).toBeUndefined()
+ })
+
+ test('with only username', () => {
+ const result = extractBasicAuthFromUrl('https://myuser@cdn.example.com/path/file.zip')
+ expect(result.url).toBe('https://cdn.example.com/path/file.zip')
+ expect(result.authHeader).toBe('Basic ' + Buffer.from('myuser:').toString('base64'))
+ })
+
+ test('with invalid URL returns original', () => {
+ const result = extractBasicAuthFromUrl('not-a-valid-url')
+ expect(result.url).toBe('not-a-valid-url')
+ expect(result.authHeader).toBeUndefined()
+ })
+})
diff --git a/packages/node-geckodriver/.npmignore b/packages/node-geckodriver/.npmignore
index b8be233..c1b2ee7 100644
--- a/packages/node-geckodriver/.npmignore
+++ b/packages/node-geckodriver/.npmignore
@@ -2,8 +2,7 @@ src
tests
coverage
*.tgz
+*.tsbuildinfo
/.*
/*.mjs
/tsconfig.json
-vitest.config.ts
-CHANGELOG.md
diff --git a/packages/node-geckodriver/README.md b/packages/node-geckodriver/README.md
index f1cc841..2f1ea96 100644
--- a/packages/node-geckodriver/README.md
+++ b/packages/node-geckodriver/README.md
@@ -173,15 +173,15 @@ Set Gecko log level [possible values: `fatal`, `error`, `warn`, `info`, `config`
Type: `string`
-### `logNoTruncated`
-Write server log to file instead of stderr, increases log level to `INFO`.
+### `logNoTruncate`
+Log verbosely and don't truncate long strings so that the log can be replayed.
Type: `boolean`
### `marionetteHost`
Host to use to connect to Gecko.
-Type: `boolean`
+Type: `string`
Default: `127.0.0.1`
### `marionettePort`
diff --git a/packages/node-geckodriver/src/types.ts b/packages/node-geckodriver/src/types.ts
index a1076b4..d3d4d8a 100644
--- a/packages/node-geckodriver/src/types.ts
+++ b/packages/node-geckodriver/src/types.ts
@@ -29,7 +29,7 @@ export interface GeckodriverParameters {
connectExisting?: boolean
/**
* Host IP to use for WebDriver server
- * @default 127.0.0.1
+ * @default 0.0.0.0
*/
host?: string
/**
@@ -42,7 +42,7 @@ export interface GeckodriverParameters {
*/
log?: LogLevel
/**
- * write server log to file instead of stderr, increases log level to INFO
+ * log verbosely and don't truncate long strings so that the log can be replayed
*/
logNoTruncate?: boolean
/**
diff --git a/packages/node-safaridriver/.github/FUNDING.yml b/packages/node-safaridriver/.github/FUNDING.yml
deleted file mode 100644
index 5ec869a..0000000
--- a/packages/node-safaridriver/.github/FUNDING.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-tidelift: "npm/safaridriver"
-open_collective: webdriverio
-github: [christian-bromann,webdriverio]
diff --git a/packages/node-safaridriver/.github/dependabot.yml b/packages/node-safaridriver/.github/dependabot.yml
deleted file mode 100644
index 9a8fcd2..0000000
--- a/packages/node-safaridriver/.github/dependabot.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-version: 2
-updates:
-- package-ecosystem: npm
- directory: "/"
- schedule:
- interval: weekly
- open-pull-requests-limit: 10
-
- groups:
- patch-deps-updates-main:
- update-types:
- - "patch"
- minor-deps-updates-main:
- update-types:
- - "minor"
- major-deps-updates-main:
- update-types:
- - "major"
-- package-ecosystem: github-actions
- directory: "/"
- schedule:
- interval: weekly
- time: "11:00"
- open-pull-requests-limit: 10
- groups:
- patch-deps-updates:
- update-types:
- - "patch"
- minor-deps-updates:
- update-types:
- - "minor"
- major-deps-updates:
- update-types:
- - "major"
diff --git a/packages/node-safaridriver/.github/workflows/audit.yml b/packages/node-safaridriver/.github/workflows/audit.yml
deleted file mode 100644
index 22ebbc0..0000000
--- a/packages/node-safaridriver/.github/workflows/audit.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: Audit
-on:
- push:
- branches:
- - main
- paths-ignore:
- - '**.md'
- pull_request:
- paths-ignore:
- - '**.md'
-
-jobs:
-
- build:
- name: Audit
-
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
-
- - name: Set Node.js 20.x
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
- with:
- node-version: 20.x
-
- - name: npm install
- run: npm install
-
- - name: Build
- run: npm run build
-
- - name: Audit
- run: npm audit --audit-level=moderate --omit=dev
diff --git a/packages/node-safaridriver/.github/workflows/expense.yml b/packages/node-safaridriver/.github/workflows/expense.yml
deleted file mode 100644
index dae02b4..0000000
--- a/packages/node-safaridriver/.github/workflows/expense.yml
+++ /dev/null
@@ -1,65 +0,0 @@
-name: Expense Contribution
-
-on:
- workflow_dispatch:
- inputs:
- prNumber:
- description: "Number of the PR (without #)"
- required: true
- amount:
- description: "The expense amount you like to grant for the contribution in $"
- required: true
- type: choice
- options:
- - "5"
- - "10"
- - "15"
- - "25"
- - "35"
- - "50"
- - "100"
- - "150"
- - "200"
- - "250"
- - "300"
- - "350"
- - "400"
- - "450"
- - "500"
- - "550"
- - "600"
- - "650"
- - "700"
- - "750"
- - "800"
- - "850"
- - "900"
- - "950"
- - "1000"
-
-jobs:
- authorize:
- runs-on: ubuntu-latest
- steps:
- - uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d # v2.4.0
- with:
- route: GET /orgs/:organisation/teams/:team/memberships/${{ github.actor }}
- team: technical-steering-committee
- organisation: webdriverio
- env:
- GITHUB_TOKEN: ${{ secrets.WDIO_BOT_GITHUB_TOKEN }}
- expense:
- permissions:
- contents: write
- id-token: write
- needs: [authorize]
- runs-on: ubuntu-latest
- steps:
- - name: Run Expense Flow
- uses: webdriverio/expense-action@fbcf7b225dfc474104d0830fe50a9b356bcec810 # v1
- with:
- prNumber: ${{ github.event.inputs.prNumber }}
- amount: ${{ github.event.inputs.amount }}
- env:
- RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
- GH_TOKEN: ${{ secrets.WDIO_BOT_GITHUB_TOKEN }}
diff --git a/packages/node-safaridriver/.npmignore b/packages/node-safaridriver/.npmignore
index e52ae25..c1b2ee7 100644
--- a/packages/node-safaridriver/.npmignore
+++ b/packages/node-safaridriver/.npmignore
@@ -2,6 +2,7 @@ src
tests
coverage
*.tgz
+*.tsbuildinfo
/.*
/*.mjs
/tsconfig.json
diff --git a/packages/node-safaridriver/README.md b/packages/node-safaridriver/README.md
index 013c3c5..a68672b 100644
--- a/packages/node-safaridriver/README.md
+++ b/packages/node-safaridriver/README.md
@@ -1,6 +1,6 @@
# Safaridriver for Node.js [](https://github.com/webdriverio-community/node-safaridriver/actions/workflows/ci.yml) [](https://github.com/webdriverio-community/node-safaridriver/actions/workflows/audit.yml)
-> A Node.js untility to manage Safaridriver sessions.
+> A Node.js utility to manage Safaridriver sessions.
The Safaridriver utility is used to launch an HTTP server that implements the [WebDriver](https://w3c.github.io/webdriver/) REST API. When launched, Safaridriver allows for automated testing of web content using the version of Safari that is installed with macOS.
@@ -19,7 +19,7 @@ To start a Safaridriver server, import the package and run:
```js
import safaridriver from 'safaridriver'
-await safaridriver.start()
+safaridriver.start()
// run some automation...
@@ -45,7 +45,7 @@ __Default:__ `/usr/bin/safaridriver`
### `useTechnologyPreview`
-If set to `true`, it will start the Safaridriver for Safari Technology Preview. Make sure you have it installed by installing Safari Technology Preview on Apple's [offical website](https://developer.apple.com/safari/technology-preview/).
+If set to `true`, it will start the Safaridriver for Safari Technology Preview. Make sure you have it installed by installing Safari Technology Preview on Apple's [official website](https://developer.apple.com/safari/technology-preview/).
__Type:__ `boolean`
__Default:__ `false`
@@ -68,4 +68,4 @@ __Default:__ `false`
----
-For more information on WebdriverIO see the [homepage](http://webdriver.io).
+For more information on WebdriverIO see the [homepage](https://webdriver.io).
diff --git a/packages/node-safaridriver/package.json b/packages/node-safaridriver/package.json
index 1ef2bc2..8c054f9 100644
--- a/packages/node-safaridriver/package.json
+++ b/packages/node-safaridriver/package.json
@@ -39,7 +39,6 @@
"typeScriptVersion": "3.8.3",
"scripts": {
"build": "tsc -b . && node ../../scripts/copy-cjs-pkg.mjs",
- "ci": "run-s lint build test",
"clean": "rimraf ./dist",
"test": "run-s test:*",
"test:lint": "oxlint .",
diff --git a/packages/node-safaridriver/tests/mod.test.ts b/packages/node-safaridriver/tests/mod.test.ts
index f79eefa..1780ef3 100644
--- a/packages/node-safaridriver/tests/mod.test.ts
+++ b/packages/node-safaridriver/tests/mod.test.ts
@@ -5,7 +5,9 @@ import safaridriver from '../src/index.js'
vi.mock('node:child_process', () => ({
default: {
- execFile: vi.fn().mockReturnValue({ kill: vi.fn() })
+ // fresh mock per call, so each test's process handle has its own
+ // independent kill() call count rather than sharing one across the file
+ execFile: vi.fn().mockImplementation(() => ({ kill: vi.fn() }))
}
}))
@@ -65,7 +67,7 @@ test('can start with options', () => {
})
test('can stop server', () => {
- const intance = safaridriver.start()
+ const instance = safaridriver.start()
safaridriver.stop()
- expect(intance.kill).toBeCalledTimes(5)
+ expect(instance.kill).toBeCalledTimes(1)
})