Skip to content

Commit

Permalink
feat: support virtual contexts (#50)
Browse files Browse the repository at this point in the history
This adds support for virtual contexts by testing `RegExp` and `Error`
via `toString` instead of an `instanceof` check.
  • Loading branch information
43081j authored May 8, 2024
1 parent fd56c9e commit d3622a0
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 7 deletions.
24 changes: 19 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
function isErrorInstance(obj) {
// eslint-disable-next-line prefer-reflect
return obj instanceof Error || Object.prototype.toString.call(obj) === '[object Error]';
}

function isErrorClass(obj) {
return obj === Error || (typeof obj === 'function' && obj.name === 'Error');
}

function isRegExp(obj) {
// eslint-disable-next-line prefer-reflect
return Object.prototype.toString.call(obj) === '[object RegExp]';
}

/**
* ### .compatibleInstance(thrown, errorLike)
*
Expand All @@ -13,7 +27,7 @@
*/

function compatibleInstance(thrown, errorLike) {
return errorLike instanceof Error && thrown === errorLike;
return isErrorInstance(errorLike) && thrown === errorLike;
}

/**
Expand All @@ -33,10 +47,10 @@ function compatibleInstance(thrown, errorLike) {
*/

function compatibleConstructor(thrown, errorLike) {
if (errorLike instanceof Error) {
if (isErrorInstance(errorLike)) {
// If `errorLike` is an instance of any error we compare their constructors
return thrown.constructor === errorLike.constructor || thrown instanceof errorLike.constructor;
} else if (errorLike.prototype instanceof Error || errorLike === Error) {
} else if (isErrorClass(Object.getPrototypeOf(errorLike)) || isErrorClass(errorLike)) {
// If `errorLike` is a constructor that inherits from Error, we compare `thrown` to `errorLike` directly
return thrown.constructor === errorLike || thrown instanceof errorLike;
}
Expand All @@ -60,7 +74,7 @@ function compatibleConstructor(thrown, errorLike) {

function compatibleMessage(thrown, errMatcher) {
const comparisonString = typeof thrown === 'string' ? thrown : thrown.message;
if (errMatcher instanceof RegExp) {
if (isRegExp(errMatcher)) {
return errMatcher.test(comparisonString);
} else if (typeof errMatcher === 'string') {
return comparisonString.indexOf(errMatcher) !== -1; // eslint-disable-line no-magic-numbers
Expand All @@ -82,7 +96,7 @@ function compatibleMessage(thrown, errMatcher) {

function getConstructorName(errorLike) {
let constructorName = errorLike;
if (errorLike instanceof Error) {
if (isErrorInstance(errorLike)) {
constructorName = errorLike.constructor.name;
} else if (typeof errorLike === 'function') {
// If `err` is not an instance of Error it is an error constructor itself or another function.
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"pretest": "npm run lint && npm run build",
"test": "npm run test:node && npm run test:browser",
"test:browser": "web-test-runner --node-resolve test/",
"test:browser": "web-test-runner",
"test:node": "mocha"
},
"config": {
Expand All @@ -53,7 +53,8 @@
"rules": {
"complexity": "off",
"max-statements": "off",
"prefer-arrow-callback": "off"
"prefer-arrow-callback": "off",
"prefer-reflect": "off"
}
},
"devDependencies": {
Expand Down
70 changes: 70 additions & 0 deletions test/virtual-machines.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { createContext, runInContext } from 'node:vm';
import { assert } from 'simple-assert';
import * as checkError from '../index.js';

const vmContext = { checkError };
createContext(vmContext);

function runCodeInVm(code) {
return runInContext(`{${ code }}`, vmContext);
}

describe('node virtual machines', function () {
it('compatibleMessage', function () {
assert(runCodeInVm(`
const errorInstance = new Error('I am an instance');
checkError.compatibleMessage(errorInstance, /instance$/) === true;
`) === true);
});

it('constructorName', function () {
assert(runCodeInVm(`
const errorInstance = new Error('I am an instance');
checkError.getConstructorName(errorInstance);
`) === 'Error');
assert(runCodeInVm(`
const derivedInstance = new TypeError('I inherit from Error');
checkError.getConstructorName(derivedInstance);
`) === 'TypeError');
});

it('compatibleInstance', function () {
assert(runCodeInVm(`
const errorInstance = new Error('I am an instance');
const sameInstance = errorInstance;
checkError.compatibleInstance(errorInstance, sameInstance);
`) === true);
assert(runCodeInVm(`
const errorInstance = new Error('I am an instance');
const otherInstance = new Error('I am another');
checkError.compatibleInstance(errorInstance, otherInstance);
`) === false);
});

it('compatibleConstructor', function () {
assert(runCodeInVm(`
const errorInstance = new Error('I am an instance');
const sameInstance = errorInstance;
checkError.compatibleConstructor(errorInstance, sameInstance);
`) === true);
assert(runCodeInVm(`
const errorInstance = new Error('I am an instance');
const otherInstance = new Error('I an another instance');
checkError.compatibleConstructor(errorInstance, otherInstance);
`) === true);
assert(runCodeInVm(`
const errorInstance = new Error('I am an instance');
const derivedInstance = new TypeError('I inherit from Error');
checkError.compatibleConstructor(derivedInstance, errorInstance);
`) === true);
assert(runCodeInVm(`
const errorInstance = new Error('I am an instance');
const derivedInstance = new TypeError('I inherit from Error');
checkError.compatibleConstructor(errorInstance, derivedInstance);
`) === false);
assert(runCodeInVm(`
const errorInstance = new TypeError('I am an instance');
checkError.compatibleConstructor(errorInstance, TypeError);
`) === true);
});
});
9 changes: 9 additions & 0 deletions web-test-runner.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default {
nodeResolve: true,
files: [
'test/*.js',
'!test/virtual-machines.js',
],
plugins: [
],
};

0 comments on commit d3622a0

Please sign in to comment.