From 54f14c477fd89160f264ff67455c15a5be71f6a5 Mon Sep 17 00:00:00 2001 From: chris Date: Sat, 3 Sep 2022 22:42:52 -0700 Subject: [PATCH 01/10] rename px to partial --- src/esmock.js | 26 +++++---------------- src/esmockArgs.js | 14 +++++++++++ tests/tests-ava/spec/esmock.ava.spec.js | 22 ++++++++--------- tests/tests-no-loader/esmock.loader.test.js | 2 +- tests/tests-node/esmock.node.only.test.js | 2 +- tests/tests-node/esmock.node.test.js | 20 ++++++++-------- tests/tests-uvu/esmock.uvu.spec.js | 16 ++++++------- 7 files changed, 51 insertions(+), 51 deletions(-) create mode 100644 src/esmockArgs.js diff --git a/src/esmock.js b/src/esmock.js index 43142c1f..b7a2d94a 100644 --- a/src/esmock.js +++ b/src/esmock.js @@ -1,4 +1,5 @@ import esmockIsLoader from './esmockIsLoader.js' +import esmockArgs from './esmockArgs.js' import { esmockModuleMock, @@ -10,23 +11,8 @@ import { esmockCache } from './esmockCache.js' -// this function normalizes different "overloaded" args signatures, returning -// one predictable args list. ex, -// [modulepath, mockdefs, globaldefs, opts] -// -> [modulepath, mockdefs, globaldefs, opts] -// [modulepath, parent, mockdefs, globaldefs, opts] -// -> [modulepath, mockdefs, globaldefs, { ...opts, parent }] -const argsnormal = (args, argsextra, parent) => { - parent = typeof args[1] === 'string' && args[1] - args = parent ? [args[0], ...args.slice(2)] : args - args[3] = { parent, ...args[3], ...(argsextra && argsextra[0]) } - args[4] = (argsextra && argsextra[1]) || args[4] - - return args -} - const esmock = async (...args) => { - const [modulePath, mockDefs, globalDefs, opt = {}, err] = argsnormal(args) + const [modulePath, mockDefs, globalDefs, opt = {}, err] = esmockArgs(args) const calleePath = (opt.parent || (err || new Error).stack.split('\n')[2]) .replace(/^.*file:\/\//, '') // rm every before filepath .replace(/:[\d]*:[\d]*.*$/, '') // rm line and row number @@ -47,11 +33,11 @@ const esmock = async (...args) => { return esmockModuleImportedSanitize(importedModule, modulePathKey) } -esmock.px = async (...args) => ( - esmock(...argsnormal(args, [{ partial: true }, new Error]))) +esmock.partial = async (...args) => esmock( + ...esmockArgs(args, { partial: true }, new Error)) -esmock.p = async (...args) => ( - esmock(...argsnormal(args, [{ purge: false }, new Error]))) +esmock.p = async (...args) => esmock( + ...esmockArgs(args, { purge: false }, new Error)) esmock.purge = mockModule => { if (mockModule && /object|function/.test(typeof mockModule) diff --git a/src/esmockArgs.js b/src/esmockArgs.js new file mode 100644 index 00000000..c56e95cb --- /dev/null +++ b/src/esmockArgs.js @@ -0,0 +1,14 @@ +// this function normalizes "overloaded" args signatures, returning +// one predictable args list. ex, +// esmockArgs([modulepath, mockdefs, globaldefs, opts]) +// -> [modulepath, mockdefs, globaldefs, opts] +// esmockArgs([modulepath, parent, mockdefs, globaldefs, opts]) +// -> [modulepath, mockdefs, globaldefs, { ...opts, parent }] +export default (args, optsextra, err, parent) => { + parent = typeof args[1] === 'string' && args[1] + args = parent ? [args[0], ...args.slice(2)] : args + args[3] = { parent, ...args[3], ...optsextra } + args[4] = err || args[4] + + return args +} diff --git a/tests/tests-ava/spec/esmock.ava.spec.js b/tests/tests-ava/spec/esmock.ava.spec.js index 8b96b36f..972cd787 100644 --- a/tests/tests-ava/spec/esmock.ava.spec.js +++ b/tests/tests-ava/spec/esmock.ava.spec.js @@ -5,7 +5,7 @@ import sinon from 'sinon' test('should not error when handling non-extensible object', async t => { // if esmock tries to simulate babel and define default.default // runtime error may occur if non-extensible is defined there - await esmock.px('../../local/importsNonDefaultClass.js', { + await esmock.partial('../../local/importsNonDefaultClass.js', { '../../local/exportsNonDefaultClass.js': { getNotifier: { default: class getNotifier { @@ -18,8 +18,8 @@ test('should not error when handling non-extensible object', async t => { // this error can also occur when an esmocked module is used to // mock antother module, where esmock defined default.default on the first // module and tried to define again from the outer module - const mockedIndex = await esmock.px('../../local/importsNonDefaultClass.js', { - '../../local/exportsNonDefaultClass.js': await esmock.px( + const mockedIndex = await esmock.partial('../../local/importsNonDefaultClass.js', { + '../../local/exportsNonDefaultClass.js': await esmock.partial( '../../local/exportsNonDefaultClass.js', { '../../local/pathWrap.js': { basename: () => 'mocked basename' @@ -42,7 +42,7 @@ test('should return un-mocked file', async t => { }) test('should mock a local file', async t => { - const main = await esmock.px('../../local/main.js', { + const main = await esmock.partial('../../local/main.js', { '../../local/mainUtil.js': { createString: () => 'test string' } @@ -165,7 +165,7 @@ test('should return un-mocked file (again)', async t => { }) test('should mock local file', async t => { - const mainUtil = await esmock.px('../../local/mainUtil.js', { + const mainUtil = await esmock.partial('../../local/mainUtil.js', { '../../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar' } @@ -181,7 +181,7 @@ test('should mock local file', async t => { }) test('should mock module and local file at the same time', async t => { - const mainUtil = await esmock.px('../../local/mainUtil.js', { + const mainUtil = await esmock.partial('../../local/mainUtil.js', { 'form-urlencoded': o => JSON.stringify(o), '../../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar' @@ -196,7 +196,7 @@ test('should mock module and local file at the same time', async t => { }) test('__esModule definition, inconsequential', async t => { - const mainUtil = await esmock.px('../../local/mainUtil.js', { + const mainUtil = await esmock.partial('../../local/mainUtil.js', { 'babelGeneratedDoubleDefault': o => o, '../../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar', @@ -208,7 +208,7 @@ test('__esModule definition, inconsequential', async t => { }) test('should work well with sinon', async t => { - const mainUtil = await esmock.px('../../local/mainUtil.js', { + const mainUtil = await esmock.partial('../../local/mainUtil.js', { '../../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: sinon.stub().returns('foobar') } @@ -261,7 +261,7 @@ test('should mock core module', async t => { }) test('should apply third parameter "global" definitions', async t => { - const main = await esmock.px('../../local/main.js', { + const main = await esmock.partial('../../local/main.js', { '../../local/mainUtil.js': { exportedFunction: () => 'foobar' } @@ -333,7 +333,7 @@ test('should have small querystring in stacktrace filename', async t => { test('should have small querystring in stacktrace filename, deep', async t => { const { causeRuntimeErrorFromImportedFile - } = await esmock.px('../../local/main.js', {}, { + } = await esmock.partial('../../local/main.js', {}, { '../../local/mainUtil.js': { causeRuntimeError: () => { t.nonexistantmethod() @@ -354,7 +354,7 @@ test('should have small querystring in stacktrace filename, deep', async t => { test('should have small querystring in stacktrace filename, deep2', async t => { const causeDeepErrorParent = - await esmock.px('../../local/causeDeepErrorParent.js', {}, { + await esmock.partial('../../local/causeDeepErrorParent.js', {}, { '../../local/causeDeepErrorGrandChild.js': { what: 'now' } diff --git a/tests/tests-no-loader/esmock.loader.test.js b/tests/tests-no-loader/esmock.loader.test.js index 3e72586b..4590c530 100644 --- a/tests/tests-no-loader/esmock.loader.test.js +++ b/tests/tests-no-loader/esmock.loader.test.js @@ -3,7 +3,7 @@ import assert from 'node:assert/strict' import esmock from 'esmock' test('should throw error if !esmockloader', async () => { - const main = await esmock.px('../local/main.js', { + const main = await esmock.partial('../local/main.js', { '../local/mainUtil.js': { createString: () => 'test string' } diff --git a/tests/tests-node/esmock.node.only.test.js b/tests/tests-node/esmock.node.only.test.js index b295da07..9a305a25 100644 --- a/tests/tests-node/esmock.node.only.test.js +++ b/tests/tests-node/esmock.node.only.test.js @@ -1,6 +1,6 @@ import test from 'node:test' import assert from 'assert' -import esmock from '../../src/esmock.js' +import esmock from 'esmock' // this error can occur when sources do not define 'esmockloader' // on 'global' but use a process linked variable instead diff --git a/tests/tests-node/esmock.node.test.js b/tests/tests-node/esmock.node.test.js index 50db9178..4c2bf0ae 100644 --- a/tests/tests-node/esmock.node.test.js +++ b/tests/tests-node/esmock.node.test.js @@ -1,7 +1,7 @@ import path from 'path' import test from 'node:test' import assert from 'node:assert/strict' -import esmock from '../../src/esmock.js' +import esmock from 'esmock' import sinon from 'sinon' test('should mock package, even when package is not installed', async () => { @@ -52,7 +52,7 @@ test('should return un-mocked file', async () => { }) test('should mock a local file', async () => { - const main = await esmock.px('../local/main.js', { + const main = await esmock.partial('../local/main.js', { '../local/mainUtil.js': { createString: () => 'test string' } @@ -175,7 +175,7 @@ test('should return un-mocked file (again)', async () => { }) test('should mock local file', async () => { - const mainUtil = await esmock.px('../local/mainUtil.js', { + const mainUtil = await esmock.partial('../local/mainUtil.js', { '../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar' } @@ -191,7 +191,7 @@ test('should mock local file', async () => { }) test('should mock module and local file at the same time', async () => { - const mainUtil = await esmock.px('../local/mainUtil.js', { + const mainUtil = await esmock.partial('../local/mainUtil.js', { 'form-urlencoded': o => JSON.stringify(o), '../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar' @@ -206,7 +206,7 @@ test('should mock module and local file at the same time', async () => { }) test('__esModule definition, inconsequential', async () => { - const mainUtil = await esmock.px('../local/mainUtil.js', { + const mainUtil = await esmock.partial('../local/mainUtil.js', { 'form-urlencoded': o => JSON.stringify(o), '../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar', @@ -222,7 +222,7 @@ test('__esModule definition, inconsequential', async () => { }) test('should work well with sinon', async () => { - const mainUtil = await esmock.px('../local/mainUtil.js', { + const mainUtil = await esmock.partial('../local/mainUtil.js', { '../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: sinon.stub().returns('foobar') } @@ -275,7 +275,7 @@ test('should mock core module', async () => { }) test('should apply third parameter "global" definitions', async () => { - const main = await esmock.px('../local/main.js', { + const main = await esmock.partial('../local/main.js', { '../local/mainUtil.js': { exportedFunction: () => 'foobar' } @@ -349,7 +349,7 @@ test('should have small querystring in stacktrace filename', async () => { test('should have small querystring in stacktrace filename, deep', async () => { const { causeRuntimeErrorFromImportedFile - } = await esmock.px('../local/main.js', {}, { + } = await esmock.partial('../local/main.js', {}, { '../local/mainUtil.js': { causeRuntimeError: () => { assert.nonexistantmethod() @@ -401,7 +401,7 @@ test('should strict mock by default, partial mock optional', async () => { namedexport: 'namedexport' } }) - const mainpartial = await esmock.px('../local/main.js', { + const mainpartial = await esmock.partial('../local/main.js', { '../local/space in path/wild-file.js': { default: 'tamed', namedexport: 'namedexport' @@ -422,7 +422,7 @@ test('should strict mock by default, partial mock optional', async () => { const pathWrapStrict = await esmock('../local/pathWrap.js', { path: { dirname: '/path/to/file' } }) - const pathWrapPartial = await esmock.px('../local/pathWrap.js', { + const pathWrapPartial = await esmock.partial('../local/pathWrap.js', { path: { dirname: '/path/to/file' } }) diff --git a/tests/tests-uvu/esmock.uvu.spec.js b/tests/tests-uvu/esmock.uvu.spec.js index 506f5492..39fb12aa 100644 --- a/tests/tests-uvu/esmock.uvu.spec.js +++ b/tests/tests-uvu/esmock.uvu.spec.js @@ -15,7 +15,7 @@ test('should return un-mocked file', async () => { }) test('should mock a local file', async () => { - const main = await esmock.px('../local/main.js', { + const main = await esmock.partial('../local/main.js', { '../local/mainUtil.js': { createString: () => 'test string' } @@ -50,7 +50,7 @@ test('should throw error if local definition file not found', async () => { }) test('should mock a module', async () => { - const main = await esmock.px('../local/mainUtil.js', { + const main = await esmock.partial('../local/mainUtil.js', { 'form-urlencoded': () => 'mock encode' }) @@ -134,7 +134,7 @@ test('should return un-mocked file (again)', async () => { }) test('should mock local file', async () => { - const mainUtil = await esmock.px('../local/mainUtil.js', { + const mainUtil = await esmock.partial('../local/mainUtil.js', { '../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar' } @@ -150,7 +150,7 @@ test('should mock local file', async () => { }) test('should mock module and local file at the same time', async () => { - const mainUtil = await esmock.px('../local/mainUtil.js', { + const mainUtil = await esmock.partial('../local/mainUtil.js', { 'form-urlencoded': o => JSON.stringify(o), '../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar' @@ -165,7 +165,7 @@ test('should mock module and local file at the same time', async () => { }) test('__esModule definition, inconsequential', async () => { - const mainUtil = await esmock.px('../local/mainUtil.js', { + const mainUtil = await esmock.partial('../local/mainUtil.js', { 'babelGeneratedDoubleDefault': o => o, '../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: () => 'foobar', @@ -177,7 +177,7 @@ test('__esModule definition, inconsequential', async () => { }) test('should work well with sinon', async () => { - const mainUtil = await esmock.px('../local/mainUtil.js', { + const mainUtil = await esmock.partial('../local/mainUtil.js', { '../local/mainUtilNamedExports.js': { mainUtilNamedExportOne: sinon.stub().returns('foobar') } @@ -230,7 +230,7 @@ test('should mock core module', async () => { }) test('should apply third parameter "global" definitions', async () => { - const main = await esmock.px('../local/main.js', { + const main = await esmock.partial('../local/main.js', { '../local/mainUtil.js': { exportedFunction: () => 'foobar' } @@ -303,7 +303,7 @@ test('should have small querystring in stacktrace filename', async () => { test('should have small querystring in stacktrace filename, deep', async () => { const { causeRuntimeErrorFromImportedFile - } = await esmock.px('../local/main.js', {}, { + } = await esmock.partial('../local/main.js', {}, { '../local/mainUtil.js': { causeRuntimeError: () => { assert.nonexistantmethod() From 128c176ebb8460391da3eb134b70b743d25556b6 Mon Sep 17 00:00:00 2001 From: chris Date: Sat, 3 Sep 2022 23:54:36 -0700 Subject: [PATCH 02/10] define and export explicit strict and partial names --- src/esmock.js | 14 ++++++++++---- src/esmockLoader.js | 5 ++--- tests/package.json.esmock.export.js | 3 ++- tests/tests-ava/spec/esmock.ava.spec.js | 17 +++++++++-------- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/esmock.js b/src/esmock.js index b7a2d94a..80c070cf 100644 --- a/src/esmock.js +++ b/src/esmock.js @@ -33,11 +33,17 @@ const esmock = async (...args) => { return esmockModuleImportedSanitize(importedModule, modulePathKey) } -esmock.partial = async (...args) => esmock( +const strict = async (...args) => esmock( + ...esmockArgs(args, { partial: false }, new Error)) +strict.p = async (...args) => esmock( + ...esmockArgs(args, { partial: false, purge: false }, new Error)) + +const partial = async (...args) => esmock( ...esmockArgs(args, { partial: true }, new Error)) +partial.p = async (...args) => esmock( + ...esmockArgs(args, { partial: true, purge: false }, new Error)) -esmock.p = async (...args) => esmock( - ...esmockArgs(args, { purge: false }, new Error)) +Object.assign(esmock, strict, { strict, partial }) esmock.purge = mockModule => { if (mockModule && /object|function/.test(typeof mockModule) @@ -47,4 +53,4 @@ esmock.purge = mockModule => { esmock.esmockCache = esmockCache -export default esmock +export {esmock as default, partial, strict} diff --git a/src/esmockLoader.js b/src/esmockLoader.js index cc4213ea..bba3bbb2 100644 --- a/src/esmockLoader.js +++ b/src/esmockLoader.js @@ -1,9 +1,8 @@ import process from 'process' -import esmock from './esmock.js' +export * from './esmock.js' +export {default} from './esmock.js' import urlDummy from './esmockDummy.js' -export default esmock - const [major, minor] = process.versions.node.split('.').map(it => +it) const isLT1612 = major < 16 || (major === 16 && minor < 12) diff --git a/tests/package.json.esmock.export.js b/tests/package.json.esmock.export.js index 3b48f82b..55cb5083 100644 --- a/tests/package.json.esmock.export.js +++ b/tests/package.json.esmock.export.js @@ -1,4 +1,5 @@ -export {default, load, resolve, getSource} from '../src/esmockLoader.js' +export * from '../src/esmockLoader.js' +export {default} from '../src/esmockLoader.js' // this file is used in tandem with two other things, // diff --git a/tests/tests-ava/spec/esmock.ava.spec.js b/tests/tests-ava/spec/esmock.ava.spec.js index 972cd787..4690b255 100644 --- a/tests/tests-ava/spec/esmock.ava.spec.js +++ b/tests/tests-ava/spec/esmock.ava.spec.js @@ -18,14 +18,15 @@ test('should not error when handling non-extensible object', async t => { // this error can also occur when an esmocked module is used to // mock antother module, where esmock defined default.default on the first // module and tried to define again from the outer module - const mockedIndex = await esmock.partial('../../local/importsNonDefaultClass.js', { - '../../local/exportsNonDefaultClass.js': await esmock.partial( - '../../local/exportsNonDefaultClass.js', { - '../../local/pathWrap.js': { - basename: () => 'mocked basename' - } - }) - }) + const mockedIndex = await esmock.partial( + '../../local/importsNonDefaultClass.js', { + '../../local/exportsNonDefaultClass.js': await esmock.partial( + '../../local/exportsNonDefaultClass.js', { + '../../local/pathWrap.js': { + basename: () => 'mocked basename' + } + }) + }) t.is(await mockedIndex.callNotifier(), 'mocked basename') }) From 91fd4b2dd34a817cc248ae18bdaa01d167bc47c7 Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 4 Sep 2022 09:43:11 -0700 Subject: [PATCH 03/10] tests failiing, typescript types file no good --- src/esmock.d.ts | 11 ++++- tests/package.json.esmock.export.d.ts | 5 +-- .../tests-node/esmock.node.importing.test.js | 43 +++++++++++++++++++ .../esmock.node-ts.importing.test.ts | 43 +++++++++++++++++++ tests/tests-nodets/package.json | 2 +- 5 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 tests/tests-node/esmock.node.importing.test.js create mode 100644 tests/tests-nodets/esmock.node-ts.importing.test.ts diff --git a/src/esmock.d.ts b/src/esmock.d.ts index 683543aa..d9da3a31 100644 --- a/src/esmock.d.ts +++ b/src/esmock.d.ts @@ -17,6 +17,10 @@ */ declare function esmock(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; declare function esmock(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; +declare function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; +declare function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; +declare function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; +declare function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; declare namespace esmock { interface Options { @@ -44,8 +48,10 @@ declare namespace esmock { * @param opt * @returns The result of importing {@link modulePath}, similar to `import(modulePath)`. */ - function px(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - function px(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; /** * Mocks dynamic imports for the module specified by {@link modulePath}. @@ -76,3 +82,4 @@ declare namespace esmock { } export default esmock; +export { partial, strict }; diff --git a/tests/package.json.esmock.export.d.ts b/tests/package.json.esmock.export.d.ts index 347022d7..57d28cc4 100644 --- a/tests/package.json.esmock.export.d.ts +++ b/tests/package.json.esmock.export.d.ts @@ -1,3 +1,2 @@ -import esmock from "../src/esmock.js"; - -export default esmock; +export * from '../src/esmock.js' +export {default} from '../src/esmock.js' diff --git a/tests/tests-node/esmock.node.importing.test.js b/tests/tests-node/esmock.node.importing.test.js new file mode 100644 index 00000000..e580c861 --- /dev/null +++ b/tests/tests-node/esmock.node.importing.test.js @@ -0,0 +1,43 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import esmock, { partial, strict } from 'esmock' + +const isPassingPartial = async esmockPartial => { + const main = await esmockPartial('../local/main.js', { + '../local/mainUtil.js': { + createString: () => 'test string' + } + }) + + assert.strictEqual(typeof main, 'function') + assert.strictEqual(main(), 'main string, test string') +} + +const isPassingStrict = async esmockStrict => { + await assert.rejects(() => esmockStrict('../local/main.js', { + '../local/mainUtil.js': { + createString: () => 'test string' + } + }), { + // eslint-disable-next-line max-len + message: "The requested module ':module' does not provide an export named ':named'" + .replace(/:module/, './mainUtil.js') + .replace(/:named/, 'causeRuntimeError') + }) +} + +test('should export esmock partial', async () => { + await isPassingPartial(partial) + await isPassingPartial(partial.p) + await isPassingPartial(esmock.partial) + await isPassingPartial(esmock.partial.p) +}) + +test('should export esmock strict', async () => { + await isPassingStrict(strict) + await isPassingStrict(strict.p) + await isPassingStrict(esmock.strict) + await isPassingStrict(esmock.strict.p) + await isPassingStrict(esmock) + await isPassingStrict(esmock.p) +}) diff --git a/tests/tests-nodets/esmock.node-ts.importing.test.ts b/tests/tests-nodets/esmock.node-ts.importing.test.ts new file mode 100644 index 00000000..e580c861 --- /dev/null +++ b/tests/tests-nodets/esmock.node-ts.importing.test.ts @@ -0,0 +1,43 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import esmock, { partial, strict } from 'esmock' + +const isPassingPartial = async esmockPartial => { + const main = await esmockPartial('../local/main.js', { + '../local/mainUtil.js': { + createString: () => 'test string' + } + }) + + assert.strictEqual(typeof main, 'function') + assert.strictEqual(main(), 'main string, test string') +} + +const isPassingStrict = async esmockStrict => { + await assert.rejects(() => esmockStrict('../local/main.js', { + '../local/mainUtil.js': { + createString: () => 'test string' + } + }), { + // eslint-disable-next-line max-len + message: "The requested module ':module' does not provide an export named ':named'" + .replace(/:module/, './mainUtil.js') + .replace(/:named/, 'causeRuntimeError') + }) +} + +test('should export esmock partial', async () => { + await isPassingPartial(partial) + await isPassingPartial(partial.p) + await isPassingPartial(esmock.partial) + await isPassingPartial(esmock.partial.p) +}) + +test('should export esmock strict', async () => { + await isPassingStrict(strict) + await isPassingStrict(strict.p) + await isPassingStrict(esmock.strict) + await isPassingStrict(esmock.strict.p) + await isPassingStrict(esmock) + await isPassingStrict(esmock.p) +}) diff --git a/tests/tests-nodets/package.json b/tests/tests-nodets/package.json index 7e0d914b..0c0723a5 100644 --- a/tests/tests-nodets/package.json +++ b/tests/tests-nodets/package.json @@ -14,6 +14,6 @@ "babelGeneratedDoubleDefault": "file:../local/babelGeneratedDoubleDefault" }, "scripts": { - "test": "node --loader=ts-node/esm --loader=esmock --test esmock.node-ts.test.ts" + "test": "node --loader=ts-node/esm --loader=esmock --test esmock.node-ts.test.ts esmock.node-ts.importing.test.ts" } } From bf9ef2ea9e2f2eaadede361fd15096547565a001 Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 4 Sep 2022 10:44:32 -0700 Subject: [PATCH 04/10] try using typed args reusable across function declarations --- src/esmock.d.ts | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/esmock.d.ts b/src/esmock.d.ts index d9da3a31..71cfca2d 100644 --- a/src/esmock.d.ts +++ b/src/esmock.d.ts @@ -1,3 +1,20 @@ +type esmockArgs = [ + modulePath: string, + mockDefs?: Record, + globalDefs?: Record, + opt?: esmock.Options +]; + +type esmockArgsParent = [ + modulePath: string, + parent: string, + mockDefs?: Record, + globalDefs?: Record, + opt?: esmock.Options +]; + +// function processPerson(...args: PersonArgs) {} + /** * Mocks imports for the module specified by {@link modulePath}. * @@ -15,13 +32,14 @@ * @param opt * @returns The result of importing {@link modulePath}, similar to `import(modulePath)`. */ -declare function esmock(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; -declare function esmock(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; +declare function esmock(...args: esmockArgsParent): any; +declare function esmock(...args: esmockArgs): any; +/* declare function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; declare function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; declare function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; declare function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - +*/ declare namespace esmock { interface Options { partial?: boolean | undefined; @@ -48,11 +66,12 @@ declare namespace esmock { * @param opt * @returns The result of importing {@link modulePath}, similar to `import(modulePath)`. */ - function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function partial(...args: esmockArgsParent): any; + function partial(...args: esmockArgs): any; + function strict(...args: esmockArgsParent): any; + function strict(...args: esmockArgs): any; + /** * Mocks dynamic imports for the module specified by {@link modulePath}. * @@ -70,8 +89,8 @@ declare namespace esmock { * @param opt * @returns The result of importing {@link modulePath}, similar to `import(modulePath)`. */ - function p(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - function p(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function p(...args: esmockArgsParent): any; + function p(...args: esmockArgs): any; /** * Unregisters a dynamic mock created by {@link esmock.p}. @@ -82,4 +101,4 @@ declare namespace esmock { } export default esmock; -export { partial, strict }; +export { esmock as partial, esmock as strict }; From afbb5eb3d6ddba9b58b3f80109a0c7b500306f27 Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 4 Sep 2022 11:21:41 -0700 Subject: [PATCH 05/10] use long explicit function declarations needed by typescript --- src/esmock.d.ts | 40 +++++++++------------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/src/esmock.d.ts b/src/esmock.d.ts index 71cfca2d..1347158f 100644 --- a/src/esmock.d.ts +++ b/src/esmock.d.ts @@ -1,20 +1,3 @@ -type esmockArgs = [ - modulePath: string, - mockDefs?: Record, - globalDefs?: Record, - opt?: esmock.Options -]; - -type esmockArgsParent = [ - modulePath: string, - parent: string, - mockDefs?: Record, - globalDefs?: Record, - opt?: esmock.Options -]; - -// function processPerson(...args: PersonArgs) {} - /** * Mocks imports for the module specified by {@link modulePath}. * @@ -32,14 +15,9 @@ type esmockArgsParent = [ * @param opt * @returns The result of importing {@link modulePath}, similar to `import(modulePath)`. */ -declare function esmock(...args: esmockArgsParent): any; -declare function esmock(...args: esmockArgs): any; -/* -declare function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; -declare function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; -declare function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; -declare function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; -*/ +declare function esmock(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; +declare function esmock(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + declare namespace esmock { interface Options { partial?: boolean | undefined; @@ -66,11 +44,11 @@ declare namespace esmock { * @param opt * @returns The result of importing {@link modulePath}, similar to `import(modulePath)`. */ - function partial(...args: esmockArgsParent): any; - function partial(...args: esmockArgs): any; + function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - function strict(...args: esmockArgsParent): any; - function strict(...args: esmockArgs): any; + function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; /** * Mocks dynamic imports for the module specified by {@link modulePath}. @@ -89,8 +67,8 @@ declare namespace esmock { * @param opt * @returns The result of importing {@link modulePath}, similar to `import(modulePath)`. */ - function p(...args: esmockArgsParent): any; - function p(...args: esmockArgs): any; + function p(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function p(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; /** * Unregisters a dynamic mock created by {@link esmock.p}. From 86061a9fba783544e118623b66f2652b7d416a1d Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 4 Sep 2022 13:07:17 -0700 Subject: [PATCH 06/10] updated types file --- src/esmock.d.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/esmock.d.ts b/src/esmock.d.ts index 1347158f..56c0a48f 100644 --- a/src/esmock.d.ts +++ b/src/esmock.d.ts @@ -46,10 +46,22 @@ declare namespace esmock { */ function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + export namespace partial { + function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function p(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function p(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + + } function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - + export namespace strict { + function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function p(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + function p(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; + } /** * Mocks dynamic imports for the module specified by {@link modulePath}. * From b1c2dc9d225469209c3c15ea48a24a7d04aef530 Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 4 Sep 2022 13:09:36 -0700 Subject: [PATCH 07/10] update README --- README.md | 4 ++-- src/esmock.d.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a73ab6d0..ed01ddae 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,8 @@ test('should suppport partial mocks', async () => { message: 'path.basename is not a function' }) - // use esmock.px to create a "partial mock" - const pathWrapPartial = await esmock.px('../src/pathWrap.js', { + // use esmock.partial to create a "partial mock" + const pathWrapPartial = await esmock.partial('../src/pathWrap.js', { path: { dirname: () => '/home/' } }) diff --git a/src/esmock.d.ts b/src/esmock.d.ts index 56c0a48f..864d79e5 100644 --- a/src/esmock.d.ts +++ b/src/esmock.d.ts @@ -51,7 +51,6 @@ declare namespace esmock { function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function p(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function p(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - } function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; From 90bdf8eccb465ef4520fc0f6693bcdc58b09ab74 Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 4 Sep 2022 13:11:56 -0700 Subject: [PATCH 08/10] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b030af5..2e06b10b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # changelog + * 2.0.0 _Sep.06.2022_ + * [export both 'partial' and 'strict'](https://github.com/iambumblehead/esmock/pull/140) variants of esmock * 1.9.8 _Aug.28.2022_ * [use latest node v18](https://github.com/iambumblehead/esmock/pull/130) for ci-tests, a bug in the ava package prevented this * [use latest resolvewithplus](https://github.com/iambumblehead/esmock/pull/130) and remove many lines of code needed for the older variant From eaa028f344c77d0bc85f93a3b512b458ec83a088 Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 4 Sep 2022 13:39:35 -0700 Subject: [PATCH 09/10] update types file, remove nested partial.strict and strict.partial --- src/esmock.d.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/esmock.d.ts b/src/esmock.d.ts index 864d79e5..a79e2c85 100644 --- a/src/esmock.d.ts +++ b/src/esmock.d.ts @@ -47,8 +47,6 @@ declare namespace esmock { function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; export namespace partial { - function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function p(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function p(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; } @@ -56,8 +54,6 @@ declare namespace esmock { function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; export namespace strict { - function partial(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; - function partial(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function p(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function p(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; } From 297fe3510e4a7ecaf21fe126e80acfcc3a7f786d Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 4 Sep 2022 13:51:12 -0700 Subject: [PATCH 10/10] add comments for strict variant --- src/esmock.d.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/esmock.d.ts b/src/esmock.d.ts index a79e2c85..7ff654e0 100644 --- a/src/esmock.d.ts +++ b/src/esmock.d.ts @@ -29,8 +29,8 @@ declare namespace esmock { /** * Mocks imports for the module specified by {@link modulePath}. * - * The provided mocks replace the imported modules _partially_, allowing some exports to - * be overridden while the rest are provided by the real module. + * This "partial" variant gives mock definitions that are merged with the + * original module definitions. * * @param modulePath The module whose imports will be mocked. * @param parent A URL to resolve specifiers relative to; typically `import.meta.url`. @@ -51,6 +51,24 @@ declare namespace esmock { function p(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; } + /** + * Mocks imports for the module specified by {@link modulePath}. + * + * This "strict" variant gives un-modified mock definitions that are not + * merged with original module definitions. + * + * @param modulePath The module whose imports will be mocked. + * @param parent A URL to resolve specifiers relative to; typically `import.meta.url`. + * If not specified, it will be inferred via the stack, which may not work + * if source maps are in use. + * @param mockDefs A mapping of import specifiers to mocked module objects; these mocks will + * only be used for imports resolved in the module specified by {@link modulePath}. + * @param globalDefs A mapping of import specifiers to mocked module objects; these mocks will + * apply to imports within the module specified by {@link modulePath}, as well + * as any transitively imported modules. + * @param opt + * @returns The result of importing {@link modulePath}, similar to `import(modulePath)`. + */ function strict(modulePath: string, parent: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; function strict(modulePath: string, mockDefs?: Record, globalDefs?: Record, opt?: esmock.Options): any; export namespace strict {