diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..5309e4c5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,59 @@ +# changelog + + * 1.6.2 _Dec.04.2021_ + * adds uvu tests and example command to README + * 1.6.1 _Dec.03.2021_ + * adds test verifying deep stacktrace has small path file:/// + * resolve bug: '--loader=esmock' not-found error not thrown + * small README edits, update link to wiki (use Home as default) + * 1.6.0 _Dec.02.2021_ + * reduce file url length (improve readability of stacktrace) + * 1.5.0 _Dec.01.2021_ + * resolve bug around error '--loader=esmock' detection + * 1.4.0 _Nov.30.2021_ + * throw error if esmock is called without --loader=esmock + * 1.3.3 _Nov.28.2021_ + * update quick-start README to include phrase 'unit test' + * 1.3.2 _Nov.27.2021_ + * use quick-start README with link to more descriptive README + * 1.3.1 _Nov.26.2021_ + * add npm keywords, remove lines of code + * 1.3.0 _Nov.26.2021_ + * add support for await import, update README + * 1.1.0 _Nov.25.2021_ + * add windows-latest to testing pipeline and begin windows support + * removed files and functions no longer needed + * increment resolvewithplus package and other dependencies + * 1.0.1 _Nov.02.2021_ + * add node v17.x to testing pipeline + * add, make warning message go away for node 16.12.0+ + * 1.0.0 _Oct.27.2021_ + * release version 1.0 + * 0.4.2 _Oct.27.2021_ + * export 'load' hook from moduleLoader, required by node v16.12.0+ + * 0.4.1 _Oct.10.2021_ + * version bump, increment devDependencies, + * major improvement to README, thanks @swivelgames + * 0.4.0 _Sep.07.2021_ + * do not runtime error when returning type '[object Module]' default + * 0.3.9 _May.05.2021_ + * small change to README + * added a test, update gitlab action to use node 16.x + * 0.3.8 _Apr.21.2021_ + * small change to README + * 0.3.7 _Apr.20.2021_ + * add test, throw error if mocked module path is not found + * 0.3.6 _Apr.19.2021_ + * throw error if mocked module path is not found + * 0.3.5 _Apr.18.2021_ + * added gitlab actions npm test: node 12.x, 14.x and 15.x + * 0.3.3 _Apr.13.2021_ + * added keywords to package.json, use github action to npm publish + * 0.3.1 _Apr.12.2021_ + * simplify README + * 0.3.0 _Apr.10.2021_ + * adds support for mocking modules 'globally' for the instance + * 0.2.0 _Apr.10.2021_ + * adds support for mocking core modules such as fs and path + * 0.1.0 _Apr.10.2021_ + * adds support for native esm modules diff --git a/README.md b/README.md index 71b51c84..e1e23013 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ esmock ====== [![npm version](https://badge.fury.io/js/esmock.svg)](https://badge.fury.io/js/esmock) [![Build Status](https://github.com/iambumblehead/esmock/workflows/nodejs-ci/badge.svg)][2] -**esmock provides native ESM import mocking for unit tests.** Use the examples below as a quick-start guide or use a [descriptive and more friendly esmock guide here.][10] +**esmock provides native ESM import mocking for unit tests.** Use examples below as a quick-start guide or use the [descriptive and friendly esmock guide here.][10] [10]: https://github.com/iambumblehead/esmock/wiki [0]: http://www.bumblehead.com "bumblehead" @@ -17,6 +17,7 @@ esmock "name": "give-esmock-a-star", "type": "module", "scripts": { + "test-uvu": "node --loader=esmock ./node_modules/uvu/bin.js ./spec/", "test-ava": "ava --node-arguments=\"--loader=esmock\"", "test-mocha": "mocha --loader=esmock --no-warnings" } @@ -27,8 +28,8 @@ esmock ``` javascript await esmock( './to/module.js', // path to target module being tested - { ...childmocks }, // mocked definitions imported by target module - { ...globalmocks } // mocked definitions imported everywhere else + { ...childmocks }, // mock definitions imported by target module + { ...globalmocks } // mock definitions imported everywhere else ); ``` @@ -55,87 +56,22 @@ test('should mock local files, packages and core modules', async t => { test('should do global instance mocks —third parameter', async t => { const { getFile } = await esmock('../src/main.js', {}, { fs : { - readFileSync : () => { - return 'anywhere the instance uses fs readFileSync'; - } + readFileSync : () => 'returns this globally'; } }); - t.is(getFile(), 'anywhere the instance uses fs readFileSync'); + t.is(getFile(), 'returns this globally'); }); test('should mock "await import()" using esmock.p', async t => { // using esmock.p, mock definitions are not deleted from cache - const doAwaitImport = await esmock.p('../src/awaitImportLint.js', { - eslint : { ESLint : config => config } + const doAwaitImport = await esmock.p('../awaitImportLint.js', { + eslint : { ESLint : cfg => cfg } }); // cached mock definition is there when import is called - t.is(await doAwaitImport('config'), 'config'); + t.is(await doAwaitImport('cfg'), 'cfg'); - esmock.purge(doAwaitImport); // clear the cache, if you wish + esmock.purge(doAwaitImport); // clear cache, if you wish }); ``` - - -
- changelog -
- - * 1.6.1 _Dec.03.2021_ - * adds test verifying deep stacktrace has small path file:/// - * resolve bug, '--loader=esmoc' not-found error not thrown - * small README edits, update link to wiki (use Home as default) - * 1.6.0 _Dec.02.2021_ - * reduce file url length (improve readability of stacktrace) - * 1.5.0 _Dec.01.2021_ - * resolve bug around error '--loader=esmock' detection - * 1.4.0 _Nov.30.2021_ - * throw error if esmock is called without --loader=esmock - * 1.3.3 _Nov.28.2021_ - * update quick-start README to include phrase 'unit test' - * 1.3.2 _Nov.27.2021_ - * use quick-start README with link to more descriptive README - * 1.3.1 _Nov.26.2021_ - * add npm keywords, remove lines of code - * 1.3.0 _Nov.26.2021_ - * add support for await import, update README - * 1.1.0 _Nov.25.2021_ - * add windows-latest to testing pipeline and begin windows support - * removed files and functions no longer needed - * increment resolvewithplus package and other dependencies - * 1.0.1 _Nov.02.2021_ - * add node v17.x to testing pipeline - * add, make warning message go away for node 16.12.0+ - * 1.0.0 _Oct.27.2021_ - * release version 1.0 - * 0.4.2 _Oct.27.2021_ - * export 'load' hook from moduleLoader, required by node v16.12.0+ - * 0.4.1 _Oct.10.2021_ - * version bump, increment devDependencies, - * major improvement to README, thanks @swivelgames - * 0.4.0 _Sep.07.2021_ - * do not runtime error when returning type '[object Module]' default - * 0.3.9 _May.05.2021_ - * small change to README - * added a test, update gitlab action to use node 16.x - * 0.3.8 _Apr.21.2021_ - * small change to README - * 0.3.7 _Apr.20.2021_ - * add test, throw error if mocked module path is not found - * 0.3.6 _Apr.19.2021_ - * throw error if mocked module path is not found - * 0.3.5 _Apr.18.2021_ - * added gitlab actions npm test: node 12.x, 14.x and 15.x - * 0.3.3 _Apr.13.2021_ - * added keywords to package.json, use github action to npm publish - * 0.3.1 _Apr.12.2021_ - * simplify README - * 0.3.0 _Apr.10.2021_ - * adds support for mocking modules 'globally' for the instance - * 0.2.0 _Apr.10.2021_ - * adds support for mocking core modules such as fs and path - * 0.1.0 _Apr.10.2021_ - * adds support for native esm modules - -
diff --git a/package.json b/package.json index 6f07fc0b..07c3378f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "esmock", - "version": "1.6.1", + "version": "1.6.2", "license": "MIT", "readmeFilename": "README.md", "description": "provides native ESM import mocking for unit tests", @@ -49,14 +49,17 @@ "resolvewithplus": "^0.4.2" }, "devDependencies": { + "uvu": "0.5.2", "ava": "^4.0.0-rc.1", "eslint": "^8.0.1", "form-urlencoded": "4.2.1", "sinon": "^12.0.1" }, "scripts": { - "test": "ava --node-arguments=\"--loader=./src/esmockLoader.mjs\"", + "test-ava": "ava --node-arguments=\"--loader=./src/esmockLoader.mjs\" ./spec/ava/*spec.js", + "test-uvu": "node --no-warnings --loader=./src/esmockLoader.mjs ./node_modules/uvu/bin.js ./spec/uvu/", + "test": "npm run test-ava && npm run test-uvu", "test-no-warn": "ava --node-arguments=\"--loader=./src/esmockLoader.mjs --no-warnings\"", - "lint": "eslint src/*js spec/*js" + "lint": "eslint src/*js spec" } } diff --git a/spec/esmock.only.spec.js b/spec/ava/esmock.ava.only.spec.js similarity index 77% rename from spec/esmock.only.spec.js rename to spec/ava/esmock.ava.only.spec.js index 6719884c..8f599e85 100644 --- a/spec/esmock.only.spec.js +++ b/spec/ava/esmock.ava.only.spec.js @@ -1,10 +1,10 @@ import test from 'ava'; -import esmock from '../src/esmock.js'; +import esmock from '../../src/esmock.js'; // this error can occur when sources do not define 'esmockloader' // on 'global' but use a process linked variable instead test.only('should not error when esmock used with ava.only', async t => { - await esmock('./local/mainUtil.js', { + await esmock('../local/mainUtil.js', { 'form-urlencoded' : () => 'mock encode' }); diff --git a/spec/esmock.spec.js b/spec/ava/esmock.ava.spec.js similarity index 76% rename from spec/esmock.spec.js rename to spec/ava/esmock.ava.spec.js index bbe2f5d6..03fc6dd2 100644 --- a/spec/esmock.spec.js +++ b/spec/ava/esmock.ava.spec.js @@ -1,9 +1,9 @@ import test from 'ava'; -import esmock from '../src/esmock.js'; +import esmock from '../../src/esmock.js'; import sinon from 'sinon'; test('should return un-mocked file', async t => { - const main = await esmock('./local/main.js'); + const main = await esmock('../local/main.js'); const mainqs = [ 'a+string', 'mainUtilNamedExportOneValue=namedExportOne', @@ -14,8 +14,8 @@ test('should return un-mocked file', async t => { }); test('should mock a local file', async t => { - const main = await esmock('./local/main.js', { - './local/mainUtil.js' : { + const main = await esmock('../local/main.js', { + '../local/mainUtil.js' : { createString : () => 'test string' } }); @@ -33,27 +33,27 @@ test.serial('should throw error if !esmockloader', async t => { }); test('should throw error if local file not found', async t => { - await t.throwsAsync(() => esmock('./local/not/found.js', { - './local/mainUtil.js' : { + await t.throwsAsync(() => esmock('../local/not/found.js', { + '../local/mainUtil.js' : { createString : () => 'test string' } }), { - message : 'modulePath not found: "./local/not/found.js"' + message : 'modulePath not found: "../local/not/found.js"' }); }); test('should throw error if local definition file not found', async t => { - await t.throwsAsync(() => esmock('./local/not/found.js', { - './local/not/found.js' : { + await t.throwsAsync(() => esmock('../local/not/found.js', { + '../local/not/found.js' : { createString : () => 'test string' } }), { - message : /not a valid path: \".\/local\/not\/found.js\" \(used by/ + message : /not a valid path: \"..\/local\/not\/found.js\" \(used by/ }); }); test('should mock a module', async t => { - const main = await esmock('./local/mainUtil.js', { + const main = await esmock('../local/mainUtil.js', { 'form-urlencoded' : () => 'mock encode' }); @@ -62,8 +62,8 @@ test('should mock a module', async t => { }); test('should mock a module, globally', async t => { - const main = await esmock('./local/main.js', { - './local/mainUtilNamedExports.js' : { + const main = await esmock('../local/main.js', { + '../local/mainUtilNamedExports.js' : { mainUtilNamedExportOne : 'mocked' } }, { @@ -85,8 +85,8 @@ test('should mock a module, globally', async t => { }); test('should purge local and global mocks', async t => { - await esmock('./local/main.js', { - './local/mainUtilNamedExports.js' : { + await esmock('../local/main.js', { + '../local/mainUtilNamedExports.js' : { mainUtilNamedExportOne : 'mocked' } }, { @@ -110,13 +110,13 @@ test('should purge local and global mocks', async t => { }); test('should mock a module, many times differently', async t => { - const mainfoo = await esmock('./local/mainUtil.js', { + const mainfoo = await esmock('../local/mainUtil.js', { 'form-urlencoded' : () => 'mock encode foo' }); - const mainbar = await esmock('./local/mainUtil.js', { + const mainbar = await esmock('../local/mainUtil.js', { 'form-urlencoded' : () => 'mock encode bar' }); - const mainbaz = await esmock('./local/mainUtil.js', { + const mainbaz = await esmock('../local/mainUtil.js', { 'form-urlencoded' : () => 'mock encode baz' }); t.is(typeof mainfoo, 'function'); @@ -126,7 +126,7 @@ test('should mock a module, many times differently', async t => { }); test('should return un-mocked file (again)', async t => { - const main = await esmock('./local/main.js'); + const main = await esmock('../local/main.js'); const mainqs = [ 'a+string', 'mainUtilNamedExportOneValue=namedExportOne', @@ -137,8 +137,8 @@ test('should return un-mocked file (again)', async t => { }); test('should mock local file', async t => { - const mainUtil = await esmock('./local/mainUtil.js', { - './local/mainUtilNamedExports.js' : { + const mainUtil = await esmock('../local/mainUtil.js', { + '../local/mainUtilNamedExports.js' : { mainUtilNamedExportOne : () => 'foobar' } }); @@ -153,9 +153,9 @@ test('should mock local file', async t => { }); test('should mock module and local file at the same time', async t => { - const mainUtil = await esmock('./local/mainUtil.js', { + const mainUtil = await esmock('../local/mainUtil.js', { 'form-urlencoded' : o => JSON.stringify(o), - './local/mainUtilNamedExports.js' : { + '../local/mainUtilNamedExports.js' : { mainUtilNamedExportOne : () => 'foobar' } }); @@ -168,9 +168,9 @@ test('should mock module and local file at the same time', async t => { }); test('__esModule definition, inconsequential', async t => { - const mainUtil = await esmock('./local/mainUtil.js', { + const mainUtil = await esmock('../local/mainUtil.js', { 'form-urlencoded' : o => JSON.stringify(o), - './local/mainUtilNamedExports.js' : { + '../local/mainUtilNamedExports.js' : { mainUtilNamedExportOne : () => 'foobar', __esModule : true } @@ -184,8 +184,8 @@ test('__esModule definition, inconsequential', async t => { }); test('should work well with sinon', async t => { - const mainUtil = await esmock('./local/mainUtil.js', { - './local/mainUtilNamedExports.js' : { + const mainUtil = await esmock('../local/mainUtil.js', { + '../local/mainUtilNamedExports.js' : { mainUtilNamedExportOne : sinon.stub().returns('foobar') } }); @@ -198,24 +198,24 @@ test('should work well with sinon', async t => { }); test('should mock an mjs file', async t => { - const main = await esmock('./local/usesmjsModule.js', { - './local/exampleMJS.mjs' : () => 'first mocked' + const main = await esmock('../local/usesmjsModule.js', { + '../local/exampleMJS.mjs' : () => 'first mocked' }); t.is(main.verifyImportedMock(), 'first mocked'); }); test('should mock an mjs file, again', async t => { - const main = await esmock('./local/usesmjsModule.js', { - './local/exampleMJS.mjs' : () => 'second mocked' + const main = await esmock('../local/usesmjsModule.js', { + '../local/exampleMJS.mjs' : () => 'second mocked' }); t.is(main.verifyImportedMock(), 'second mocked'); }); test('should mock an exported constant values', async t => { - const main = await esmock('./local/usesmjsModule.js', { - './local/env.js' : { + const main = await esmock('../local/usesmjsModule.js', { + '../local/env.js' : { TESTCONSTANT : 'hello world' } }); @@ -224,7 +224,7 @@ test('should mock an exported constant values', async t => { }); test('should mock core module', async t => { - const usesCoreModule = await esmock('./local/usesCoreModule.js', { + const usesCoreModule = await esmock('../local/usesCoreModule.js', { fs : { existsSync : () => true, readFileSync : filepath => filepath === 'checkfilepath.js' @@ -237,8 +237,8 @@ test('should mock core module', async t => { }); test('should apply third parameter "global" definitions', async t => { - const main = await esmock('./local/main.js', { - './local/mainUtil.js' : { + const main = await esmock('../local/main.js', { + '../local/mainUtil.js' : { exportedFunction : () => 'foobar' } }, { @@ -254,7 +254,7 @@ test('should apply third parameter "global" definitions', async t => { }); test('returns spread-imported [object Module] default export', async t => { - const main = await esmock('./local/usesObjectModule.js', { + const main = await esmock('../local/usesObjectModule.js', { fs : { exportedFunction : () => 'foobar' } @@ -264,7 +264,7 @@ test('returns spread-imported [object Module] default export', async t => { }); test('mocks inline `async import("name")`', async t => { - const writeJSConfigFile = await esmock.p('./local/usesInlineImport.mjs', { + const writeJSConfigFile = await esmock.p('../local/usesInlineImport.mjs', { eslint : { ESLint : function (...o) { this.stringify = () => JSON.stringify(...o); @@ -295,7 +295,7 @@ test('mocks inline `async import("name")`', async t => { }); test('should have small querystring in stacktrace filename', async t => { - const { causeRuntimeError } = await esmock('./local/mainUtil.js'); + const { causeRuntimeError } = await esmock('../local/mainUtil.js'); try { causeRuntimeError(); @@ -309,8 +309,8 @@ 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('./local/main.js', {}, { - './local/mainUtil.js' : { + } = await esmock('../local/main.js', {}, { + '../local/mainUtil.js' : { causeRuntimeError : () => { t.nonexistantmethod(); } diff --git a/spec/uvu/esmock.uvu.spec.js b/spec/uvu/esmock.uvu.spec.js new file mode 100644 index 00000000..b3676945 --- /dev/null +++ b/spec/uvu/esmock.uvu.spec.js @@ -0,0 +1,342 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import sinon from 'sinon'; + +import esmock from '../../src/esmock.js'; + +test('should return un-mocked file', async () => { + const main = await esmock('../local/main.js'); + const mainqs = [ + 'a+string', + 'mainUtilNamedExportOneValue=namedExportOne', + 'mainUtilNamedExportTwoValue=namedExportTwo' + ].join('&'); + + assert.is(main(), `main string, mainUtil=${mainqs}`); +}); + +test('should mock a local file', async () => { + const main = await esmock('../local/main.js', { + '../local/mainUtil.js' : { + createString : () => 'test string' + } + }); + assert.is(typeof main, 'function'); + assert.is(main(), 'main string, test string'); +}); + +test('should throw error if !esmockloader', async () => { + setTimeout(async () => { + global.esmockloader = false; + try { + await esmock('./to/module.js'); + } catch (e) { + assert.is(e.message, 'process must be started with --loader=esmock'); + } + global.esmockloader = true; + }, 500); +}); + +test('should throw error if local file not found', async () => { + try { + await esmock('../local/not/found.js', { + '../local/mainUtil.js' : { + createString : () => 'test string' + } + }); + } catch (e) { + assert.is(e.message, 'modulePath not found: "../local/not/found.js"'); + }; +}); + +test('should throw error if local definition file not found', async () => { + try { + await esmock('../local/not/found.js', { + '../local/not/found.js' : { + createString : () => 'test string' + } + }); + } catch (e) { + assert.ok(e.message.startsWith( + 'not a valid path: "../local/not/found.js" (used by')); + }; +}); + +test('should mock a module', async () => { + const main = await esmock('../local/mainUtil.js', { + 'form-urlencoded' : () => 'mock encode' + }); + + assert.is(typeof main, 'function'); + assert.is(main.createString(), 'mock encode'); +}); + +test('should mock a module, globally', async () => { + const main = await esmock('../local/main.js', { + '../local/mainUtilNamedExports.js' : { + mainUtilNamedExportOne : 'mocked' + } + }, { + 'form-urlencoded' : () => 'mock encode', + fs : { + existsSync : () => true, + readFileSync : filepath => filepath === 'checkfilepath.js' + ? 'success' + : filepath + } + }); + + assert.is(typeof main, 'function'); + assert.is( + main.mainDependencyUsingCoreModuleFSReadPath('checkfilepath.js'), + 'success' + ); + assert.is(main(), 'main string and mocked export, mock encode'); +}); + +test('should purge local and global mocks', async () => { + await esmock('../local/main.js', { + '../local/mainUtilNamedExports.js' : { + mainUtilNamedExportOne : 'mocked' + } + }, { + 'form-urlencoded' : () => 'mock encode', + fs : { + existsSync : () => true, + readFileSync : filepath => filepath === 'checkfilepath.js' + ? 'success' + : filepath + } + }, { + key : 999 + }); + + const keys = Object + .keys(esmock.esmockCache.mockDefs) + .filter(key => /esmockKey=999/.test(key)); + + assert.ok(keys.length); + assert.ok(keys.every(key => esmock.esmockCache.mockDefs[key] === null)); +}); + +test('should mock a module, many times differently', async () => { + const mainfoo = await esmock('../local/mainUtil.js', { + 'form-urlencoded' : () => 'mock encode foo' + }); + const mainbar = await esmock('../local/mainUtil.js', { + 'form-urlencoded' : () => 'mock encode bar' + }); + const mainbaz = await esmock('../local/mainUtil.js', { + 'form-urlencoded' : () => 'mock encode baz' + }); + assert.is(typeof mainfoo, 'function'); + assert.is(mainfoo.createString(), 'mock encode foo'); + assert.is(mainbar.createString(), 'mock encode bar'); + assert.is(mainbaz.createString(), 'mock encode baz'); +}); + +test('should return un-mocked file (again)', async () => { + const main = await esmock('../local/main.js'); + const mainqs = [ + 'a+string', + 'mainUtilNamedExportOneValue=namedExportOne', + 'mainUtilNamedExportTwoValue=namedExportTwo' + ].join('&'); + + assert.is(main(), `main string, mainUtil=${mainqs}`); +}); + +test('should mock local file', async () => { + const mainUtil = await esmock('../local/mainUtil.js', { + '../local/mainUtilNamedExports.js' : { + mainUtilNamedExportOne : () => 'foobar' + } + }); + + const mainqs = [ + 'mainUtil=a+string', + 'mainUtilNamedExportOneValue=foobar', + 'mainUtilNamedExportTwoValue=namedExportTwo' + ].join('&'); + + assert.is(mainUtil.createString(), mainqs); +}); + +test('should mock module and local file at the same time', async () => { + const mainUtil = await esmock('../local/mainUtil.js', { + 'form-urlencoded' : o => JSON.stringify(o), + '../local/mainUtilNamedExports.js' : { + mainUtilNamedExportOne : () => 'foobar' + } + }); + + assert.is(mainUtil.createString(), JSON.stringify({ + mainUtil : 'a string', + mainUtilNamedExportOneValue : 'foobar', + mainUtilNamedExportTwoValue : 'namedExportTwo' + })); +}); + +test('__esModule definition, inconsequential', async () => { + const mainUtil = await esmock('../local/mainUtil.js', { + 'form-urlencoded' : o => JSON.stringify(o), + '../local/mainUtilNamedExports.js' : { + mainUtilNamedExportOne : () => 'foobar', + __esModule : true + } + }); + + assert.is(mainUtil.createString(), JSON.stringify({ + mainUtil : 'a string', + mainUtilNamedExportOneValue : 'foobar', + mainUtilNamedExportTwoValue : 'namedExportTwo' + })); +}); + +test('should work well with sinon', async () => { + const mainUtil = await esmock('../local/mainUtil.js', { + '../local/mainUtilNamedExports.js' : { + mainUtilNamedExportOne : sinon.stub().returns('foobar') + } + }); + + assert.is(mainUtil.createString(), [ + 'mainUtil=a+string', + 'mainUtilNamedExportOneValue=foobar', + 'mainUtilNamedExportTwoValue=namedExportTwo' + ].join('&')); +}); + +test('should mock an mjs file', async () => { + const main = await esmock('../local/usesmjsModule.js', { + '../local/exampleMJS.mjs' : () => 'first mocked' + }); + + assert.is(main.verifyImportedMock(), 'first mocked'); +}); + +test('should mock an mjs file, again', async () => { + const main = await esmock('../local/usesmjsModule.js', { + '../local/exampleMJS.mjs' : () => 'second mocked' + }); + + assert.is(main.verifyImportedMock(), 'second mocked'); +}); + +test('should mock an exported constant values', async () => { + const main = await esmock('../local/usesmjsModule.js', { + '../local/env.js' : { + TESTCONSTANT : 'hello world' + } + }); + + assert.is(main.verifyImportedConstant(), 'hello world'); +}); + +test('should mock core module', async () => { + const usesCoreModule = await esmock('../local/usesCoreModule.js', { + fs : { + existsSync : () => true, + readFileSync : filepath => filepath === 'checkfilepath.js' + ? 'success' + : filepath + } + }); + + assert.is(usesCoreModule.readPath('checkfilepath.js'), 'success'); +}); + +test('should apply third parameter "global" definitions', async () => { + const main = await esmock('../local/main.js', { + '../local/mainUtil.js' : { + exportedFunction : () => 'foobar' + } + }, { + fs : { + readFileSync : () => { + return 'this value anywhere the instance imports fs, global'; + } + } + }); + + const tplStr = main.readTemplateFile(); + assert.is(tplStr, 'this value anywhere the instance imports fs, global'); +}); + +test('returns spread-imported [object Module] default export', async () => { + const main = await esmock('../local/usesObjectModule.js', { + fs : { + exportedFunction : () => 'foobar' + } + }); + + assert.is(main.exportedFunction(), 'foobar'); +}); + +test('mocks inline `async import("name")`', async () => { + const writeJSConfigFile = await esmock.p('../local/usesInlineImport.mjs', { + eslint : { + ESLint : function (...o) { + this.stringify = () => JSON.stringify(...o); + + return this; + } + } + }); + + assert.is( + (await writeJSConfigFile('config', 'filePath')).stringify(), + JSON.stringify({ + baseConfig : 'config', + fix : true, + useEslintrc : false, + filePath : 'filePath' + })); + + const [ , key ] = writeJSConfigFile.esmockKey.match(/esmk=(\d*)/); + const keyRe = new RegExp(`esmockKey=${key}[^d]`); + + const moduleKeys = Object.keys(esmock.esmockCache.mockDefs) + .filter(moduleKey => keyRe.test(moduleKey)); + + assert.ok(moduleKeys.every(mkey => esmock.esmockCache.mockDefs[mkey])); + esmock.purge(writeJSConfigFile); + assert.ok( + moduleKeys.every(mkey => esmock.esmockCache.mockDefs[mkey] === null)); +}); + +test('should have small querystring in stacktrace filename', async () => { + const { causeRuntimeError } = await esmock('../local/mainUtil.js'); + + try { + causeRuntimeError(); + } catch (e) { + assert.ok(/\?esmk=\d/.test(e.stack.split('\n')[1])); + } + + assert.ok(true); +}); + +test('should have small querystring in stacktrace filename, deep', async () => { + const { + causeRuntimeErrorFromImportedFile + } = await esmock('../local/main.js', {}, { + '../local/mainUtil.js' : { + causeRuntimeError : () => { + assert.nonexistantmethod(); + } + } + }); + + try { + causeRuntimeErrorFromImportedFile(); + } catch (e) { + assert.ok( + e.stack.split('\n') + .every(line => !line.includes('?') || /\?esmk=\d/.test(line))); + } + + assert.ok(true); +}); + +test.run();