Skip to content

Commit

Permalink
Merge pull request #172 from iambumblehead/add-support-for-esmock.str…
Browse files Browse the repository at this point in the history
…ictest

added support for esmock.strictest
  • Loading branch information
iambumblehead authored Oct 5, 2022
2 parents ca69fb9 + a5939c9 commit 7fde5d8
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 14 deletions.
15 changes: 15 additions & 0 deletions src/esmock.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ declare const esmock: MockFunction & {
*/
strict: MockFunction & {
p: MockFunction
},

/**
* The "strictest" variant replaces original module definitions
* with mock definitions AND requires all imported modules to be mocked.
*/
strictest: MockFunction & {
p: MockFunction
}
}

Expand All @@ -70,9 +78,16 @@ declare const esmock: MockFunction & {
*/
declare const strict: typeof esmock['strict']

/**
* The "strictest" variant replaces original module definitions
* with mock definitions AND requires all imported modules to be mocked.
*/
declare const strictest: typeof esmock['strictest']

export {
esmock as default,
strict,
strictest,
type MockFunction,
type MockMap,
type Options
Expand Down
11 changes: 7 additions & 4 deletions src/esmock.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ const purge = mockModule => mockModule
&& /object|function/.test(typeof mockModule) && 'esmkTreeId' in mockModule
&& esmockModule.purge(mockModule.esmkTreeId)

const strict = Object.assign(esmockGo({ strict: true }), {
purge, p: esmockGo({ strict: true, purge: false }) })
const strict = Object.assign(esmockGo({ strict: 1 }), {
purge, p: esmockGo({ strict: 1, purge: false }) })

const strictest = Object.assign(esmockGo({ strict: 3 }), {
purge, p: esmockGo({ strict: 3, purge: false }) })

const esmock = Object.assign(esmockGo(), {
purge, p: esmockGo({ purge: false }), strict })
purge, p: esmockGo({ purge: false }), strict, strictest })

export {esmock as default, strict}
export {esmock as default, strict, strictest}
15 changes: 15 additions & 0 deletions src/esmockErr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const narrow = p => p
.replace('file://', '')
.replace(process.cwd(), '.')
.replace(process.env.HOME, '~')

const errModuleIdNotFound = (moduleId, parent) => new Error(
`invalid moduleId: "${narrow(moduleId)}" (used by ${narrow(parent)})`)

const errModuleIdNotMocked = (moduleId, parent) => new Error(
`un-mocked moduleId: "${narrow(moduleId)}" (used by ${narrow(parent)})`)

export default {
errModuleIdNotFound,
errModuleIdNotMocked
}
4 changes: 4 additions & 0 deletions src/esmockLoader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import process from 'process'
import urlDummy from './esmockDummy.js'
import esmockErr from './esmockErr.js'

const [major, minor] = process.versions.node.split('.').map(it => +it)
const isLT1612 = major < 16 || (major === 16 && minor < 12)
Expand Down Expand Up @@ -73,6 +74,9 @@ const resolve = async (specifier, context, nextResolve) => {
}
}

if (/strict=3/.test(treeidspec) && !moduleId)
throw esmockErr.errModuleIdNotMocked(resolvedurl, treeidspec.split('?')[0])

return resolved
}

Expand Down
16 changes: 6 additions & 10 deletions src/esmockModule.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fs from 'fs'
import resolvewith from 'resolvewithplus'
import esmockErr from './esmockErr.js'

import {
esmockTreeIdSet,
Expand All @@ -12,12 +13,7 @@ import {
const isObj = o => typeof o === 'object' && o
const isDefaultIn = o => isObj(o) && 'default' in o
const isDirPathRe = /^\.?\.?([a-zA-Z]:)?(\/|\\)/
const esmockNextId = ((id = 0) => () => ++id)()

const esmockModuleIdNotFoundError = (moduleId, parent) => new Error(
`invalid moduleId: "${moduleId}" (used by ${parent})`
.replace(process.cwd(), '.')
.replace(process.env.HOME, '~'))
const nextId = ((id = 0) => () => ++id)()

const esmockModuleMergeDefault = (defLive, def) =>
(isObj(defLive) && isObj(def)) ? Object.assign({}, defLive, def) : def
Expand Down Expand Up @@ -105,7 +101,7 @@ const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => {

const fileURL = resolvewith(id, parent)
if (!fileURL && opt.isModuleNotFoundError !== false)
throw esmockModuleIdNotFoundError(id, parent)
throw esmockErr.errModuleIdNotFound(id, parent)

mocks.push(await esmockModuleCreate(treeid, defs[id], id, fileURL, opt))

Expand All @@ -115,10 +111,10 @@ const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => {
const esmockModule = async (moduleId, parent, defs, gdefs, opt) => {
const moduleFileURL = resolvewith(moduleId, parent)
if (!moduleFileURL)
throw esmockModuleIdNotFoundError(moduleId, parent)
throw esmockErr.errModuleIdNotFound(moduleId, parent)

const treeid = typeof opt.id === 'number' ? opt.id : esmockNextId()
const treeidspec = `${moduleFileURL}?key=${treeid}?` + [
const treeid = typeof opt.id === 'number' ? opt.id : nextId()
const treeidspec = `${moduleFileURL}?key=${treeid}&strict=${opt.strict}?` + [
'esmkgdefs=' + (gdefs && (await esmockModuleId(
parent, treeid, gdefs, Object.keys(gdefs), opt)).join('#-#') || 0),
'esmkdefs=', (defs && (await esmockModuleId(
Expand Down
7 changes: 7 additions & 0 deletions tests/local/importsCoreLocalAndPackage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import core from 'path'
import local from './usesCoreModule.js'
import pkg from 'form-urlencoded'

export const corePathBasename = p => core.basename(p)
export const localReadSync = p => local.readPath(p)
export const packageFn = p => pkg(p)
51 changes: 51 additions & 0 deletions tests/tests-node/esmock.node.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,54 @@ test('should throw error when strict mock definition not found', async () => {
assert.deepEqual(pathWrapPartial.basename('/dog.png'), 'dog.png')
})

test('should error when "strictest" mock tree module not mocked', async () => {
const strictestTree = await esmock.strictest(
'../local/importsCoreLocalAndPackage.js', {
path: { basename: () => 'core' },
'../local/usesCoreModule.js': { readPath: () => 'local' },
'form-urlencoded': () => 'package'
}
)

assert.deepEqual(strictestTree.corePathBasename(), 'core')
assert.deepEqual(strictestTree.localReadSync(), 'local')
assert.deepEqual(strictestTree.packageFn(), 'package')

await assert.rejects(async () => esmock.strictest(
'../local/importsCoreLocalAndPackage.js', {
'../local/usesCoreModule.js': { readPath: () => 'local' },
'form-urlencoded': () => 'package'
}
), {
name: 'Error',
message: new RegExp(
'un-mocked moduleId: "node:path" \\(used by .*:parent\\)'
.replace(':parent', 'importsCoreLocalAndPackage.js'))
})

await assert.rejects(async () => esmock.strictest(
'../local/importsCoreLocalAndPackage.js', {
path: { basename: () => 'core' },
'form-urlencoded': () => 'package'
}
), {
name: 'Error',
message: new RegExp(
'un-mocked moduleId: ".*:child" \\(used by .*:parent\\)'
.replace(':child', 'usesCoreModule.js')
.replace(':parent', 'importsCoreLocalAndPackage.js'))
})

await assert.rejects(async () => esmock.strictest(
'../local/importsCoreLocalAndPackage.js', {
path: { basename: () => 'core' },
'../local/usesCoreModule.js': { readPath: () => 'local' }
}
), {
name: 'Error',
message: new RegExp(
'un-mocked moduleId: ".*:package" \\(used by .*:parent\\)'
.replace(':package', 'form-urlencoded.mjs')
.replace(':parent', 'importsCoreLocalAndPackage.js'))
})
})

0 comments on commit 7fde5d8

Please sign in to comment.