diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebd493b6..198fa3f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,5 +26,13 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' + - name: Install pnpm (for the pnpm example project) + shell: bash + run: | + case "$(node -p 'process.versions.node.split(".")[0]')" in + 16) npm install --global pnpm@8 ;; + 18) npm install --global pnpm@9 ;; + *) npm install --global pnpm ;; + esac - run: npm ci - run: npm test diff --git a/examples/babel/package-lock.json b/examples/babel/package-lock.json index 1a3d4135..a9344b46 100644 --- a/examples/babel/package-lock.json +++ b/examples/babel/package-lock.json @@ -20,7 +20,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, @@ -4329,7 +4329,7 @@ "mkdirp": "^3.0.1", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "rimraf": "^5.0.5", "standard": "^17.1.0", "stringify-object-es5": "^2.5.0", diff --git a/examples/jest-broken/package-lock.json b/examples/jest-broken/package-lock.json index 21a471cc..bd76699a 100644 --- a/examples/jest-broken/package-lock.json +++ b/examples/jest-broken/package-lock.json @@ -21,7 +21,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, @@ -5837,7 +5837,7 @@ "mkdirp": "^3.0.1", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "rimraf": "^5.0.5", "standard": "^17.1.0", "stringify-object-es5": "^2.5.0", diff --git a/examples/jest/package-lock.json b/examples/jest/package-lock.json index 4fa5c49e..042d101e 100644 --- a/examples/jest/package-lock.json +++ b/examples/jest/package-lock.json @@ -21,7 +21,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, @@ -5837,7 +5837,7 @@ "mkdirp": "^3.0.1", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "rimraf": "^5.0.5", "standard": "^17.1.0", "stringify-object-es5": "^2.5.0", diff --git a/examples/node-ava/package-lock.json b/examples/node-ava/package-lock.json index 23856c8d..8e9bc43f 100644 --- a/examples/node-ava/package-lock.json +++ b/examples/node-ava/package-lock.json @@ -19,7 +19,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, @@ -8647,7 +8647,7 @@ "mkdirp": "^3.0.1", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "rimraf": "^5.0.5", "standard": "^17.1.0", "stringify-object-es5": "^2.5.0", diff --git a/examples/node-esm-auto-loader/package-lock.json b/examples/node-esm-auto-loader/package-lock.json index 33205436..3180409e 100644 --- a/examples/node-esm-auto-loader/package-lock.json +++ b/examples/node-esm-auto-loader/package-lock.json @@ -18,7 +18,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, @@ -6175,7 +6175,7 @@ "mkdirp": "^3.0.1", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "rimraf": "^5.0.5", "standard": "^17.1.0", "stringify-object-es5": "^2.5.0", diff --git a/examples/node-esm-pnpm/.gitignore b/examples/node-esm-pnpm/.gitignore new file mode 100644 index 00000000..c07ea0f3 --- /dev/null +++ b/examples/node-esm-pnpm/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +pnpm-lock.yaml diff --git a/examples/node-esm-pnpm/.npmrc b/examples/node-esm-pnpm/.npmrc new file mode 100644 index 00000000..84ff0791 --- /dev/null +++ b/examples/node-esm-pnpm/.npmrc @@ -0,0 +1 @@ +hoist=false diff --git a/examples/node-esm-pnpm/package.json b/examples/node-esm-pnpm/package.json new file mode 100644 index 00000000..66c61087 --- /dev/null +++ b/examples/node-esm-pnpm/package.json @@ -0,0 +1,7 @@ +{ + "name": "testdouble-node-esm-pnpm-example", + "private": true, + "scripts": { + "test": "pnpm --recursive test" + } +} diff --git a/examples/node-esm-pnpm/packages/app/lib/numbers-only.mjs b/examples/node-esm-pnpm/packages/app/lib/numbers-only.mjs new file mode 100644 index 00000000..e75cdce7 --- /dev/null +++ b/examples/node-esm-pnpm/packages/app/lib/numbers-only.mjs @@ -0,0 +1,8 @@ +import isNumber from 'is-number' + +export default function (thing) { + if (!isNumber(thing)) { + throw new Error('numbers only!') + } + return true +} diff --git a/examples/node-esm-pnpm/packages/app/package.json b/examples/node-esm-pnpm/packages/app/package.json new file mode 100644 index 00000000..175559b0 --- /dev/null +++ b/examples/node-esm-pnpm/packages/app/package.json @@ -0,0 +1,11 @@ +{ + "name": "testdouble-node-esm-pnpm-example-app", + "private": true, + "scripts": { + "test": "node --loader=testdouble --test ./test/numbers-only-test.mjs" + }, + "devDependencies": { + "is-number": "^7.0.0", + "testdouble": "file:../../../.." + } +} diff --git a/examples/node-esm-pnpm/packages/app/test/numbers-only-test.mjs b/examples/node-esm-pnpm/packages/app/test/numbers-only-test.mjs new file mode 100644 index 00000000..ead31fe7 --- /dev/null +++ b/examples/node-esm-pnpm/packages/app/test/numbers-only-test.mjs @@ -0,0 +1,20 @@ +import assert from 'node:assert/strict' +import { describe, it, afterEach } from 'node:test' +import * as td from 'testdouble' + +// is-number is a dependency of this package only, so pnpm installs it where +// this file can see it but testdouble (in the workspace root's node_modules) +// can't. td.replaceEsm() has to resolve it the way this file would. +describe('numbers-only', function () { + afterEach(function () { + td.reset() + }) + + it('replaces a third-party module only this package depends on', async function () { + const isNumber = (await td.replaceEsm('is-number')).default + const numbersOnly = (await import('../lib/numbers-only.mjs')).default + td.when(isNumber('seven')).thenReturn(true) + + assert.strictEqual(numbersOnly('seven'), true) + }) +}) diff --git a/examples/node-esm-pnpm/pnpm-workspace.yaml b/examples/node-esm-pnpm/pnpm-workspace.yaml new file mode 100644 index 00000000..384c668a --- /dev/null +++ b/examples/node-esm-pnpm/pnpm-workspace.yaml @@ -0,0 +1,11 @@ +packages: + - packages/* + +# Keep each package's dependencies out of reach of the packages installed in +# the workspace root, so td.replaceEsm() has to resolve bare specifiers from +# the test file (pnpm 10+ reads this here, pnpm 8 and 9 from .npmrc) +hoist: false + +# Install testdouble's dependencies even when they were just released, +# instead of pnpm adding exclusions to this file for them +minimumReleaseAge: 0 diff --git a/examples/node-esm/package-lock.json b/examples/node-esm/package-lock.json index 0cb3ecdd..f2b26a64 100644 --- a/examples/node-esm/package-lock.json +++ b/examples/node-esm/package-lock.json @@ -18,7 +18,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, diff --git a/examples/node/package-lock.json b/examples/node/package-lock.json index 9e883ddc..0b26ff53 100644 --- a/examples/node/package-lock.json +++ b/examples/node/package-lock.json @@ -20,7 +20,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, @@ -6464,7 +6464,7 @@ "mkdirp": "^3.0.1", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "rimraf": "^5.0.5", "standard": "^17.1.0", "stringify-object-es5": "^2.5.0", diff --git a/package-lock.json b/package-lock.json index 19af6944..20648272 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, @@ -6010,9 +6010,9 @@ ] }, "node_modules/quibble": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/quibble/-/quibble-0.11.0.tgz", - "integrity": "sha512-DNoMAchxhKyKAbJhVVlA+oOgdicluabD/CRuBQrH3c3Cf9Veie2Zl1TRWVuaqMmq+DcXqZIx02uCUzz0+CCtQg==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/quibble/-/quibble-0.12.1.tgz", + "integrity": "sha512-XyqltQYTTG+U5zLuvQusmc/F3Yes9DoqvgkrcY0pB1YmMaScqW0mdW00GYderJ+u8+geUv88Xapx+8FMp2Ro3Q==", "license": "MIT", "dependencies": { "lodash": "^4.18.1", @@ -12415,9 +12415,9 @@ "dev": true }, "quibble": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/quibble/-/quibble-0.11.0.tgz", - "integrity": "sha512-DNoMAchxhKyKAbJhVVlA+oOgdicluabD/CRuBQrH3c3Cf9Veie2Zl1TRWVuaqMmq+DcXqZIx02uCUzz0+CCtQg==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/quibble/-/quibble-0.12.1.tgz", + "integrity": "sha512-XyqltQYTTG+U5zLuvQusmc/F3Yes9DoqvgkrcY0pB1YmMaScqW0mdW00GYderJ+u8+geUv88Xapx+8FMp2Ro3Q==", "requires": { "lodash": "^4.18.1", "resolve": "^1.22.8" diff --git a/package.json b/package.json index d3fade82..139dff54 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "test:esm-auto-loader": "node test/support/supports-auto-load.js not || cross-env TS_NODE_IGNORE=\"node_modules,notypescript\" ./test/safe-esm/teenytest-proxy.js --helper test/helper.js test/safe-esm/replace.test.js", "test:no-loader-esm": "node test/support/supports-auto-load.js || cross-env TS_NODE_IGNORE=\"node_modules,notypescript\" teenytest test/helper.js './test/safe-esm/*.no-loader-test.{mjs,js}'", "test:ci": "npm run compile && run-p style test && echo \"All done!\"", - "test:example": "run-s test:example:babel test:example:jest test:example:jest-broken test:example:node test:example:node-ava test:example:node-esm test:example-esm-auto-loader", + "test:example": "run-s test:example:babel test:example:jest test:example:jest-broken test:example:node test:example:node-ava test:example:node-esm test:example:node-esm-pnpm test:example-esm-auto-loader", "test:example-esm-auto-loader": "node test/support/supports-auto-load.js not || npm run test:example:node-esm-auto-loader", "test:example:babel": "bash ./script/run-examples babel", "test:example:jest": "bash ./script/run-examples jest", @@ -55,6 +55,7 @@ "test:example:node": "bash ./script/run-examples node", "test:example:node-ava": "bash ./script/run-examples node-ava", "test:example:node-esm": "bash ./script/run-examples node-esm", + "test:example:node-esm-pnpm": "bash ./script/run-examples node-esm-pnpm", "test:example:node-esm-auto-loader": "bash ./script/run-examples node-esm-auto-loader", "test:example:webpack": "bash ./script/run-examples webpack", "version:write": "echo \"export default '$npm_package_version'\" > src/version.js", @@ -96,7 +97,7 @@ }, "dependencies": { "lodash": "^4.17.21", - "quibble": "^0.11.0", + "quibble": "^0.12.1", "stringify-object-es5": "^2.5.0", "theredoc": "^1.0.0" }, diff --git a/script/run-examples b/script/run-examples index 3e785f2e..39e43781 100755 --- a/script/run-examples +++ b/script/run-examples @@ -9,7 +9,16 @@ cd examples for D in $projects; do echo "<------ Running tests for example project '${D}'" cd "${D}" - npm install + if [ -f pnpm-workspace.yaml ]; then + if ! command -v pnpm > /dev/null; then + echo "pnpm is required to run the example project '${D}' (npm install --global pnpm)" + exit 1 + fi + # Its lockfile isn't committed, which pnpm refuses by default on CI + pnpm install --no-frozen-lockfile + else + npm install + fi npm test cd .. done