Skip to content

Commit

Permalink
Merge pull request #201 from iambumblehead/detect-async-resolve
Browse files Browse the repository at this point in the history
Detect async resolve
  • Loading branch information
iambumblehead authored May 9, 2023
2 parents 4425101 + 322567f commit 3ee98e8
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 15 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# changelog

* 2.2.2 _May.06.2023_
* [detect async import.meta.resolve](https://github.com/iambumblehead/esmock/pull/201) and handle in a separate way
* remove un-necessary usage of await keyword in README example
* require node version less than 20.x
* 2.2.1 _Apr.03.2023_
* [use Object.defineProperty](https://github.com/iambumblehead/esmock/pull/197) to write mock definitions protected on inherited prototype chain
* 2.2.0 _Mar.23.2023_
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
```
![npm](https://img.shields.io/npm/v/esmock) [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/iambumblehead/166d927bd0089d7bfdee4e98a537712c/raw/esmock__heads_master.json)][2] [![install size](https://packagephobia.now.sh/badge?p=esmock)](https://packagephobia.now.sh/result?p=esmock) [![downloads](https://badgen.now.sh/npm/dm/esmock)](https://npmjs.org/package/esmock)

**esmock provides native ESM import mocking for unit tests.** Use examples below as a quick-start guide, see the [descriptive and friendly esmock guide here,][10] or browse [esmock's test runner examples.][3]
**esmock provides native ESM import mocking for unit tests.** Use examples below as a quick-start guide, see the [descriptive and friendly esmock guide here,][4] or browse [esmock's test runner examples.][3]

[10]: https://github.com/iambumblehead/esmock/wiki
[0]: http://www.bumblehead.com "bumblehead"
[0]: https://www.bumblehead.com "bumblehead"
[1]: https://github.com/iambumblehead/esmock/workflows/nodejs-ci/badge.svg "nodejs-ci pipeline"
[2]: https://github.com/iambumblehead/esmock "esmock"
[3]: https://github.com/iambumblehead/esmock/tree/master/tests "tests"
[4]: https://github.com/iambumblehead/esmock/wiki

`esmock` is used with node's --loader
``` json
Expand Down Expand Up @@ -52,7 +52,7 @@ import test from 'node:test'
import assert from 'node:assert/strict'
import esmock from 'esmock'

test('should mock packages and local files', async () => {
test('package, alias and local file mocks', async () => {
const cookup = await esmock('../src/cookup.js', {
addpkg: (a, b) => a + b,
'#icon': { coffee: '', bacon: '🥓' },
Expand All @@ -65,15 +65,15 @@ test('should mock packages and local files', async () => {
assert.strictEqual(cookup('breakfast'), '☕🥓🧂')
})

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

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

test('should mock "await import()" using esmock.p', async () => {
test('mocks "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 }
Expand All @@ -84,14 +84,14 @@ test('should mock "await import()" using esmock.p', async () => {
// a bit more info are found in the wiki guide
})

test('should support "strict" mocking, at esmock.strict', async () => {
test('esmock.strict mocks', async () => {
// replace original module definitions and do not merge them
const pathWrapper = await esmock.strict('../src/pathWrapper.js', {
path: { dirname: () => '/path/to/file' }
})

// error, because "path" mock above does not define path.basename
await assert.rejects(async () => pathWrapper.basename('/dog.png'), {
assert.rejects(() => pathWrapper.basename('/dog.🐶.png'), {
name: 'TypeError',
message: 'path.basename is not a function'
})
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "esmock",
"type": "module",
"version": "2.2.1",
"version": "2.2.2",
"license": "ISC",
"readmeFilename": "README.md",
"description": "provides native ESM import mocking for unit tests",
Expand Down Expand Up @@ -54,7 +54,7 @@
"rewire"
],
"engines": {
"node": ">=14.16.0"
"node": ">=14.16.0 <=19.9.0"
},
"dependencies": {
"resolvewithplus": "^2.0.1"
Expand Down
19 changes: 15 additions & 4 deletions src/esmockModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ const asFileURL = p => p.startsWith('file://') ? p : url.pathToFileURL(p)
const objProto = Object.getPrototypeOf({})
const isPlainObj = o => Object.getPrototypeOf(o) === objProto

// when import.meta.resolve fails to resolve windows paths, fallback resolvewith
const resolve = isMetaResolve ?
(import.meta.resolve.constructor.name === 'AsyncFunction'
? async (id, p) => import.meta.resolve(id, asFileURL(p))
.catch(() => resolvewith(id, p))
: (id, p) => {
try { return import.meta.resolve(id, asFileURL(p)) }
catch { return resolvewith(id, p) }
})
: resolvewith

// assigning the object to its own prototypal inheritor can error, eg
// 'Cannot assign to read only property \'F_OK\' of object \'#<Object>\''
//
Expand Down Expand Up @@ -116,9 +127,8 @@ const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => {

if (!id) return mocks

const fileURL = isMetaResolve
? await import.meta.resolve(id, asFileURL(parent)).catch(() => null)
: resolvewith(id, parent)
const fileURL = resolve.constructor.name === 'AsyncFunction'
? await resolve(id, parent) : resolve(id, parent)
if (!fileURL && opt.isModuleNotFoundError !== false)
throw esmockErr.errModuleIdNotFound(id, parent)

Expand All @@ -128,7 +138,8 @@ const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => {
}

const esmockModule = async (moduleId, parent, defs, gdefs, opt) => {
const moduleFileURL = resolvewith(moduleId, parent)
const moduleFileURL = resolve.constructor.name === 'AsyncFunction'
? await resolve(moduleId, parent) : resolve(moduleId, parent)
if (!moduleFileURL)
throw esmockErr.errModuleIdNotFound(moduleId, parent)

Expand Down
2 changes: 1 addition & 1 deletion tests/tests-node/esmock.node.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ test('should mock package, even when package is not installed', async () => {
})

test('should mock a subpath', async () => {
const localpackagepath = path.resolve('../local/')
const localpackagepath = path.resolve('../local/') + path.sep
const { subpathfunctionWrap } = await esmock(
'../local/subpathimporter.js', localpackagepath, {
'#sub': {
Expand Down

0 comments on commit 3ee98e8

Please sign in to comment.