Skip to content

Commit

Permalink
Merge pull request #65 from iambumblehead/64-make-partial-mocks-optio…
Browse files Browse the repository at this point in the history
…nal-thing

use strict mock by default, with partial mock optional
  • Loading branch information
iambumblehead authored Jul 18, 2022
2 parents 15eddd0 + 8fe7d06 commit db63e0e
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 101 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# changelog

* 1.8.0 _Jul.18.2022_
* use strict-mocking behaviour by default, "partial mock" is optional
* 1.7.8 _Jul.16.2022_
* add tests using the node native test-runner
* update README to use node native test-runner
Expand Down
84 changes: 44 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,58 +43,62 @@ await esmock(

`esmock` examples
``` javascript
import test from 'node:test';
import assert from 'node:assert/strict';
import esmock from 'esmock';
import test from 'node:test'
import assert from 'node:assert/strict'
import esmock from 'esmock'

test('should mock local files and packages', async () => {
const main = await esmock('../src/main.js', {
stringifierpackage : JSON.stringify,
'../src/hello.js' : {
default : () => 'world',
exportedFunction : () => 'foobar'
stringifierpackage: JSON.stringify,
'../src/hello.js': {
default: () => 'world',
exportedFunction: () => 'foo'
}
});
})

assert.strictEqual(main(), JSON.stringify({ test : 'world foobar' }));
});
assert.strictEqual(main(), JSON.stringify({ test: 'world foo' }))
})

test('should do global instance mocks —third param', async () => {
const { getFile } = await esmock('../src/main.js', {}, {
fs : {
readFileSync : () => 'returns this globally';
fs: {
readFileSync: () => 'returns this globally'
}
});
})

assert.strictEqual(getFile(), 'returns this globally');
});
assert.strictEqual(getFile(), 'returns this globally')
})

test('should mock "await import()" using esmock.p', async () => {
// using esmock.p, mock definitions are kept in cache
const doAwaitImport = await esmock.p('../awaitImportLint.js', {
eslint : { ESLint : cfg => cfg }
});

// mock definition is there, in cache, when import is called
assert.strictEqual(await doAwaitImport('cfg'), 'cfg');

esmock.purge(doAwaitImport); // clear cache, if you wish
});

test('should merge "default" value, when safe', async () => {
const main = await esmock('../src/main.js');

assert.strictEqual(main(), main.default());
});

test('should use implicit "default"', async () => {
const mainA = await esmock('../src/exportsMain.js', {
'../src/main.js' : () => 'mocked main' // short-hand, smaller
});
const mainB = await esmock('../src/exportsMain.js', {
'../src/main.js' : { default : () => 'mocked main' }
});

assert.strictEqual(mainA(), mainB());
});
eslint: { ESLint: cfg => cfg }
})

// mock definition is returned from cache, when import is called
assert.strictEqual(await doAwaitImport('cfg'), 'cfg')
// a bit more info are found in the descriptive guide
})

// a "partial mock" merges the new and original definitions
test('should suppport partial mocks', async () => {
const pathWrap = await esmock('../src/pathWrap.js', {
path: { dirname: () => '/path/to/file' }
})

// an error, because path.basename was not defined
await assert.rejects(async () => pathWrap.basename('/dog.png'), {
name: 'TypeError',
message: 'path.basename is not a function'
})

// use esmock.px to create a "partial mock"
const pathWrapPartial = await esmock.px('../src/pathWrap.js', {
path: { dirname: () => '/home/' }
})

// no error, because "core" path.basename was merged into the mock
assert.deepEqual(pathWrapPartial.basename('/dog.png'), 'dog.png')
assert.deepEqual(pathWrapPartial.dirname(), '/home/')
})
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "esmock",
"version": "1.7.8",
"version": "1.8.0",
"license": "MIT",
"readmeFilename": "README.md",
"description": "provides native ESM import mocking for unit tests",
Expand Down
16 changes: 8 additions & 8 deletions spec/ava/esmock.ava.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test('should return un-mocked file', async t => {
});

test('should mock a local file', async t => {
const main = await esmock('../local/main.js', {
const main = await esmock.px('../local/main.js', {
'../local/mainUtil.js' : {
createString : () => 'test string'
}
Expand Down Expand Up @@ -137,7 +137,7 @@ test('should return un-mocked file (again)', async t => {
});

test('should mock local file', async t => {
const mainUtil = await esmock('../local/mainUtil.js', {
const mainUtil = await esmock.px('../local/mainUtil.js', {
'../local/mainUtilNamedExports.js' : {
mainUtilNamedExportOne : () => 'foobar'
}
Expand All @@ -153,7 +153,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('../local/mainUtil.js', {
const mainUtil = await esmock.px('../local/mainUtil.js', {
'form-urlencoded' : o => JSON.stringify(o),
'../local/mainUtilNamedExports.js' : {
mainUtilNamedExportOne : () => 'foobar'
Expand All @@ -168,7 +168,7 @@ 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.px('../local/mainUtil.js', {
'form-urlencoded' : o => JSON.stringify(o),
'../local/mainUtilNamedExports.js' : {
mainUtilNamedExportOne : () => 'foobar',
Expand All @@ -184,7 +184,7 @@ test('__esModule definition, inconsequential', async t => {
});

test('should work well with sinon', async t => {
const mainUtil = await esmock('../local/mainUtil.js', {
const mainUtil = await esmock.px('../local/mainUtil.js', {
'../local/mainUtilNamedExports.js' : {
mainUtilNamedExportOne : sinon.stub().returns('foobar')
}
Expand Down Expand Up @@ -237,7 +237,7 @@ test('should mock core module', async t => {
});

test('should apply third parameter "global" definitions', async t => {
const main = await esmock('../local/main.js', {
const main = await esmock.px('../local/main.js', {
'../local/mainUtil.js' : {
exportedFunction : () => 'foobar'
}
Expand Down Expand Up @@ -309,7 +309,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('../local/main.js', {}, {
} = await esmock.px('../local/main.js', {}, {
'../local/mainUtil.js' : {
causeRuntimeError : () => {
t.nonexistantmethod();
Expand All @@ -330,7 +330,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('../local/causeDeepErrorParent.js', {}, {
await esmock.px('../local/causeDeepErrorParent.js', {}, {
'../local/causeDeepErrorGrandChild.js' : {
what : 'now'
}
Expand Down
4 changes: 3 additions & 1 deletion spec/local/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from './mainUtilNamedExports.js';
import { readPath, readSync } from './usesCoreModule.js';

import wildfile from './space in path/wild-file.js';
import wildfile, * as wildfileexports from './space in path/wild-file.js';

import {
createString,
Expand Down Expand Up @@ -37,4 +37,6 @@ export default () => {
: 'main string, ' + createString();
};

export const wildexports = wildfileexports;

export const wild = wildfile;
10 changes: 10 additions & 0 deletions spec/local/pathWrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import path from 'path';

const basename = f => path.basename(f);

const dirname = f => path.dirname(f);

export {
basename,
dirname
};
3 changes: 3 additions & 0 deletions spec/local/space in path/wild-file.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export default 'success';

export const originalDefinition1 = 'originalDefinition1';
export const originalDefinition2 = 'originalDefinition2';
54 changes: 47 additions & 7 deletions spec/node/esmock.node.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ test('should return un-mocked file', async () => {
});

test('should mock a local file', async () => {
const main = await esmock('../local/main.js', {
const main = await esmock.px('../local/main.js', {
'../local/mainUtil.js' : {
createString : () => 'test string'
}
Expand Down Expand Up @@ -130,7 +130,7 @@ test('should return un-mocked file (again)', async () => {
});

test('should mock local file', async () => {
const mainUtil = await esmock('../local/mainUtil.js', {
const mainUtil = await esmock.px('../local/mainUtil.js', {
'../local/mainUtilNamedExports.js' : {
mainUtilNamedExportOne : () => 'foobar'
}
Expand All @@ -146,7 +146,7 @@ test('should mock local file', async () => {
});

test('should mock module and local file at the same time', async () => {
const mainUtil = await esmock('../local/mainUtil.js', {
const mainUtil = await esmock.px('../local/mainUtil.js', {
'form-urlencoded' : o => JSON.stringify(o),
'../local/mainUtilNamedExports.js' : {
mainUtilNamedExportOne : () => 'foobar'
Expand All @@ -161,7 +161,7 @@ test('should mock module and local file at the same time', async () => {
});

test('__esModule definition, inconsequential', async () => {
const mainUtil = await esmock('../local/mainUtil.js', {
const mainUtil = await esmock.px('../local/mainUtil.js', {
'form-urlencoded' : o => JSON.stringify(o),
'../local/mainUtilNamedExports.js' : {
mainUtilNamedExportOne : () => 'foobar',
Expand All @@ -177,7 +177,7 @@ test('__esModule definition, inconsequential', async () => {
});

test('should work well with sinon', async () => {
const mainUtil = await esmock('../local/mainUtil.js', {
const mainUtil = await esmock.px('../local/mainUtil.js', {
'../local/mainUtilNamedExports.js' : {
mainUtilNamedExportOne : sinon.stub().returns('foobar')
}
Expand Down Expand Up @@ -230,7 +230,7 @@ test('should mock core module', async () => {
});

test('should apply third parameter "global" definitions', async () => {
const main = await esmock('../local/main.js', {
const main = await esmock.px('../local/main.js', {
'../local/mainUtil.js' : {
exportedFunction : () => 'foobar'
}
Expand Down Expand Up @@ -304,7 +304,7 @@ test('should have small querystring in stacktrace filename', async () => {
test('should have small querystring in stacktrace filename, deep', async () => {
const {
causeRuntimeErrorFromImportedFile
} = await esmock('../local/main.js', {}, {
} = await esmock.px('../local/main.js', {}, {
'../local/mainUtil.js' : {
causeRuntimeError : () => {
assert.nonexistantmethod();
Expand Down Expand Up @@ -348,3 +348,43 @@ test('should not error when mocked file has space in path', async () => {
assert.strictEqual(main.wild, 'tamed');
});

test('should strict mock by default, partial mock optional', async () => {
const wildfile = await import('../local/space in path/wild-file.js');
const mainstrict = await esmock('../local/main.js', {
'../local/space in path/wild-file.js' : {
default : 'tamed',
namedexport : 'namedexport'
}
});
const mainpartial = await esmock.px('../local/main.js', {
'../local/space in path/wild-file.js' : {
default : 'tamed',
namedexport : 'namedexport'
}
});
const wildfilenamedexports = Object.keys(wildfile)
.filter(n => n !== 'default');
const mainstrictwildexports = Object.keys(mainstrict.wildexports);
const mainpartialwildexports = Object.keys(mainpartial.wildexports);

assert.strictEqual(
true, wildfilenamedexports.every(e => !mainstrictwildexports.includes(e)));
assert.strictEqual(
true, wildfilenamedexports.every(e => mainpartialwildexports.includes(e)));
});

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', {
path : { dirname : '/path/to/file' }
});

await assert.rejects(async () => pathWrapStrict.basename('/filename.js'), {
name : 'TypeError',
message : 'path.basename is not a function'
});

assert.deepEqual(pathWrapPartial.basename('/dog.png'), 'dog.png');
});
Loading

0 comments on commit db63e0e

Please sign in to comment.