Skip to content

Commit

Permalink
Merge pull request #122 from jakebailey/parent-param
Browse files Browse the repository at this point in the history
Add overloads for mock functions, fully expand out d.ts file
  • Loading branch information
iambumblehead authored Aug 21, 2022
2 parents b6a1bde + 945a808 commit 129a728
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 8 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# changelog

* 1.9.5
* 1.9.6 _Aug.19.2022_
* support parent url to facilitate sourcemap usage, [113](https://github.com/iambumblehead/esmock/issues/113)
* 1.9.5 _Aug.19.2022_
* support cjs packges that define [main relative directory only](https://github.com/iambumblehead/esmock/issues/119)
* 1.9.4 _Aug.15.2022_
* support core modules [w/ node: prefix](https://github.com/iambumblehead/resolvewithplus/pull/27), credit @gmahomarf
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"test-ci": "npm run test:install && npm run test:all-ci",
"test-cover": "npm run test:install && c8 npm run test:all",
"lint": "eslint .",
"lint-fix": "eslint --fix .",
"mini:pkg": "npm pkg delete scripts devDependencies",
"prepublishOnly": "npm run lint && npm run test-ci && npm run mini:pkg"
}
Expand Down
76 changes: 75 additions & 1 deletion src/esmock.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,76 @@
declare function esmock(path: string, localmock?: any, globalmock?: any): any;
/**
* Mocks imports for the module specified by {@link modulePath}.
*
* The provided mocks replace the imported modules _fully_.
*
* @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)`.
*/
declare function esmock(modulePath: string, parent: string, mockDefs?: Record<string, any>, globalDefs?: Record<string, any>, opt?: esmock.Options): any;
declare function esmock(modulePath: string, mockDefs?: Record<string, any>, globalDefs?: Record<string, any>, opt?: esmock.Options): any;

declare namespace esmock {
interface Options {
partial?: boolean | undefined;
purge?: boolean | undefined;
}

/**
* 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.
*
* @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 px(modulePath: string, parent: string, mockDefs?: Record<string, any>, globalDefs?: Record<string, any>, opt?: esmock.Options): any;
function px(modulePath: string, mockDefs?: Record<string, any>, globalDefs?: Record<string, any>, opt?: esmock.Options): any;

/**
* Mocks dynamic imports for the module specified by {@link modulePath}.
*
* After using this function, consider calling {@link esmock.purge} to free memory.
*
* @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 p(modulePath: string, parent: string, mockDefs?: Record<string, any>, globalDefs?: Record<string, any>, opt?: esmock.Options): any;
function p(modulePath: string, mockDefs?: Record<string, any>, globalDefs?: Record<string, any>, opt?: esmock.Options): any;

/**
* Unregisters a dynamic mock created by {@link esmock.p}.
*
* @param mockModule A module object that was previously returned by {@link esmock.p}.
*/
function purge(mockModule: any): void;
}

export default esmock;
33 changes: 27 additions & 6 deletions src/esmock.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,21 @@ import {
esmockCache
} from './esmockCache.js'

const esmock = async (modulePath, mockDefs, globalDefs, opt = {}, err) => {
const calleePath = (err || new Error).stack.split('\n')[2]
const argsToObj = args => {
// Distinguish between the two overloads; see esmock.d.ts.
let modulePath, parent, mockDefs, globalDefs, opt
if (typeof args[1] === "string") {
[ modulePath, parent, mockDefs, globalDefs, opt ] = args
} else {
[ modulePath, mockDefs, globalDefs, opt ] = args
}
return { modulePath, parent, mockDefs, globalDefs, opt }
}

const _esmock = async (argsObj, err) => {
const { modulePath, parent, mockDefs, globalDefs, opt = {} } = argsObj

const calleePath = (parent || err.stack.split('\n')[2])
.replace(/^.*file:\/\//, '') // rm every before filepath
.replace(/:[\d]*:[\d]*.*$/, '') // rm line and row number
.replace(/^.*:/, '') // rm windows-style drive location
Expand All @@ -29,11 +42,19 @@ const esmock = async (modulePath, mockDefs, globalDefs, opt = {}, err) => {
return esmockModuleImportedSanitize(importedModule, modulePathKey)
}

esmock.px = async (modulePath, mockDefs, globalDefs, opt) => esmock(
modulePath, mockDefs, globalDefs, { ...opt, partial: true }, new Error)
const esmock = async (...args) => _esmock(argsToObj(args), new Error)

esmock.p = async (modulePath, mockDefs, globalDefs, opt) => esmock(
modulePath, mockDefs, globalDefs, { ...opt, purge: false }, new Error)
esmock.px = async (...args) => {
const argsObj = argsToObj(args)
argsObj.opt = { ...argsObj.opt, partial: true }
return _esmock(argsObj, new Error)
}

esmock.p = async (...args) => {
const argsObj = argsToObj(args)
argsObj.opt = { ...argsObj.opt, purge: false }
return _esmock(argsObj, new Error)
}

esmock.purge = mockModule => {
if (mockModule && /object|function/.test(typeof mockModule)
Expand Down
6 changes: 6 additions & 0 deletions tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"url": "https://github.com/iambumblehead/esmock.git"
},
"main": "package.json.esmock.export.js",
"exports": {
"types": "./package.json.esmock.export.d.ts",
"import": "./package.json.esmock.export.js"
},
"dependencies": {
"pg": "^8.7.3",
"eslint": "^8.12.0",
Expand All @@ -27,6 +31,7 @@
"install:test-tsm": "cd tests-tsm && npm install",
"install:test-node": "cd tests-node && npm install",
"install:test-jest": "cd tests-jest && npm install",
"install:test-source-map": "cd tests-source-map && npm install",
"install:test-nodets": "cd tests-nodets && npm install",
"install:all": "node --version && npm install && npm-run-all install:test*",
"test:test-ava": "cd tests-ava && npm test",
Expand All @@ -35,6 +40,7 @@
"test:node18-test-node": "cd tests-node && npm test",
"test:node18-test-jest": "cd tests-jest && npm test",
"test:node18-test-nodets": "cd tests-nodets && npm test",
"test:node18-test-source-map": "cd tests-source-map && npm test",
"test:node18:all": "npm run isnodelt18 || npm-run-all test:node18-test*",
"test:all": "npm-run-all test:test* && npm run test:node18:all",
"test:all-cover": "c8 --src=../src/* npm run test:all",
Expand Down
3 changes: 3 additions & 0 deletions tests/package.json.esmock.export.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import esmock from "../src/esmock.js";

export default esmock;
1 change: 1 addition & 0 deletions tests/tests-source-map/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
7 changes: 7 additions & 0 deletions tests/tests-source-map/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# esmock-source-map-bug

To run with source maps, run `npm run test`. esmock would previously fail unless the provided module path was absolute

To run without source maps, run `npm run test-no-maps`. This works.

https://github.com/iambumblehead/esmock/issues/113
34 changes: 34 additions & 0 deletions tests/tests-source-map/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"type": "module",
"description": "esmock unit tests, ava with sourcemap",
"repository": {
"type": "git",
"url": "https://github.com/iambumblehead/esmock.git"
},
"dependencies": {
"esmock": "file:..",
"@ava/typescript": "^3.0.1",
"@tsconfig/node14": "^1.0.3",
"@types/node": "^18.7.3",
"ava": "^4.3.1",
"cross-env": "^7.0.3",
"rimraf": "^3.0.2",
"typescript": "^4.7.4"
},
"scripts": {
"test": "rimraf dist && tsc && cross-env NODE_OPTIONS=--loader=esmock NODE_NO_WARNINGS=1 ava",
"test-no-maps": "rimraf dist && tsc --sourceMap false && cross-env NODE_OPTIONS=--loader=esmock NODE_NO_WARNINGS=1 ava"
},
"ava": {
"typescript": {
"rewritePaths": {
"src/": "dist/"
},
"compile": false
},
"environmentVariables": {
"NO_COLOR": "1",
"FORCE_COLOR": "0"
}
}
}
30 changes: 30 additions & 0 deletions tests/tests-source-map/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import test from "ava";
import esmock from "esmock";
import { fileURLToPath } from "url";

import type * as indexType from "../index.js";

const expectedHostname = "my-machine";

test("using absolute path", async (t) => {
const urlFull = fileURLToPath(new URL("../index.js", import.meta.url));
const indexModule = await esmock(urlFull, {
os: {
hostname: () => expectedHostname,
},
});

const getHostname: typeof indexType.getHostname = indexModule.getHostname;
t.is(getHostname(), expectedHostname);
});

test("using relative path", async (t) => {
const indexModule = await esmock("../index.js", import.meta.url, {
os: {
hostname: () => expectedHostname,
},
});

const getHostname: typeof indexType.getHostname = indexModule.getHostname;
t.is(getHostname(), expectedHostname);
});
5 changes: 5 additions & 0 deletions tests/tests-source-map/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import os from "os";

export function getHostname() {
return os.hostname();
}
23 changes: 23 additions & 0 deletions tests/tests-source-map/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16",
"rootDir": "src",
"outDir": "dist",
"declarationDir": "dist",
"declaration": true,
"stripInternal": true,
"sourceMap": true,

"allowUnusedLabels": false,
"allowUnreachableCode": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"importsNotUsedAsValues": "error"
},
"exclude": ["dist"]
}

0 comments on commit 129a728

Please sign in to comment.