Skip to content

Commit 37263c0

Browse files
authored
fix: support some virtual contexts in toThrow (#1609)
* fix: support some virtual contexts in `toThrow` This adds support for VM situations where we pass a `RegExp` from another process. Note that we don't have a full fix for this stuff until `check-error` also supports `Error` being from another origin. * fix: support throwing of unusual errors Adds support for throwing things like `undefined`, functions, etc. * chore: upgrade check-error
1 parent 91e58ed commit 37263c0

7 files changed

+83
-13
lines changed

lib/chai/core/assertions.js

+21-7
Original file line numberDiff line numberDiff line change
@@ -2676,15 +2676,17 @@ function assertThrows (errorLike, errMsgMatcher, msg) {
26762676
, negate = flag(this, 'negate') || false;
26772677
new Assertion(obj, flagMsg, ssfi, true).is.a('function');
26782678

2679-
if (errorLike instanceof RegExp || typeof errorLike === 'string') {
2679+
if (_.isRegExp(errorLike) || typeof errorLike === 'string') {
26802680
errMsgMatcher = errorLike;
26812681
errorLike = null;
26822682
}
26832683

2684-
var caughtErr;
2684+
let caughtErr;
2685+
let errorWasThrown = false;
26852686
try {
26862687
obj();
26872688
} catch (err) {
2689+
errorWasThrown = true;
26882690
caughtErr = err;
26892691
}
26902692

@@ -2708,14 +2710,26 @@ function assertThrows (errorLike, errMsgMatcher, msg) {
27082710
errorLikeString = _.checkError.getConstructorName(errorLike);
27092711
}
27102712

2713+
let actual = caughtErr;
2714+
if (caughtErr instanceof Error) {
2715+
actual = caughtErr.toString();
2716+
} else if (typeof caughtErr === 'string') {
2717+
actual = caughtErr;
2718+
} else if (caughtErr && (typeof caughtErr === 'object' || typeof caughtErr === 'function')) {
2719+
try {
2720+
actual = _.checkError.getConstructorName(caughtErr);
2721+
} catch (_err) {
2722+
// somehow wasn't a constructor, maybe we got a function thrown
2723+
// or similar
2724+
}
2725+
}
2726+
27112727
this.assert(
2712-
caughtErr
2728+
errorWasThrown
27132729
, 'expected #{this} to throw ' + errorLikeString
27142730
, 'expected #{this} to not throw an error but #{act} was thrown'
27152731
, errorLike && errorLike.toString()
2716-
, (caughtErr instanceof Error ?
2717-
caughtErr.toString() : (typeof caughtErr === 'string' ? caughtErr : caughtErr &&
2718-
_.checkError.getConstructorName(caughtErr)))
2732+
, actual
27192733
);
27202734
}
27212735

@@ -2760,7 +2774,7 @@ function assertThrows (errorLike, errMsgMatcher, msg) {
27602774
if (caughtErr && errMsgMatcher !== undefined && errMsgMatcher !== null) {
27612775
// Here we check compatible messages
27622776
var placeholder = 'including';
2763-
if (errMsgMatcher instanceof RegExp) {
2777+
if (_.isRegExp(errMsgMatcher)) {
27642778
placeholder = 'matching'
27652779
}
27662780

lib/chai/utils/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,14 @@ export {isNaN} from './isNaN.js';
9494

9595
// getOperator method
9696
export {getOperator} from './getOperator.js';
97+
98+
/**
99+
* Determines if an object is a `RegExp`
100+
* This is used since `instanceof` will not work in virtual contexts
101+
*
102+
* @param {*} obj Object to test
103+
* @returns {boolean}
104+
*/
105+
export function isRegExp(obj) {
106+
return Object.prototype.toString.call(obj) === '[object RegExp]';
107+
}

package-lock.json

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
},
4444
"dependencies": {
4545
"assertion-error": "^2.0.1",
46-
"check-error": "^2.0.0",
46+
"check-error": "^2.1.1",
4747
"deep-eql": "^5.0.1",
4848
"loupe": "^3.1.0",
4949
"pathval": "^2.0.0"

test/assert.js

+8
Original file line numberDiff line numberDiff line change
@@ -1635,6 +1635,8 @@ describe('assert', function () {
16351635
});
16361636

16371637
it('throws / throw / Throw', function() {
1638+
class CustomError extends Error {}
1639+
16381640
['throws', 'throw', 'Throw'].forEach(function (throws) {
16391641
assert[throws](function() { throw new Error('foo'); });
16401642
assert[throws](function() { throw new Error(''); }, '');
@@ -1644,6 +1646,12 @@ describe('assert', function () {
16441646
assert[throws](function() { throw new Error('bar'); }, Error, 'bar');
16451647
assert[throws](function() { throw new Error(''); }, Error, '');
16461648
assert[throws](function() { throw new Error('foo') }, '');
1649+
assert[throws](function() { throw ''; }, '');
1650+
assert[throws](function() { throw ''; }, /^$/);
1651+
assert[throws](function() { throw new Error(''); }, /^$/);
1652+
assert[throws](function() { throw undefined; });
1653+
assert[throws](function() { throw new CustomError('foo'); });
1654+
assert[throws](function() { throw (() => {}); });
16471655

16481656
var thrownErr = assert[throws](function() { throw new Error('foo'); });
16491657
assert(thrownErr instanceof Error, 'assert.' + throws + ' returns error');

test/virtual-machines.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import vm from 'node:vm';
2+
import * as chai from '../index.js';
3+
4+
const {assert} = chai;
5+
const vmContext = {assert};
6+
vm.createContext(vmContext);
7+
8+
/**
9+
* Run the code in a virtual context
10+
*
11+
* @param {string} code Code to run
12+
*/
13+
function runCodeInVm(code) {
14+
vm.runInContext(code, vmContext);
15+
}
16+
17+
describe('node virtual machines', function () {
18+
it('throws', function() {
19+
const shouldNotThrow = [
20+
`assert.throws(function() { throw ''; }, /^$/);`,
21+
`assert.throws(function() { throw new Error('bleepbloop'); });`,
22+
`assert.throws(function() { throw new Error(''); });`,
23+
`assert.throws(function() { throw new Error('swoosh'); }, /swoosh/);`
24+
];
25+
26+
for (const code of shouldNotThrow) {
27+
assert.doesNotThrow(
28+
() => {
29+
runCodeInVm(code);
30+
}
31+
);
32+
}
33+
});
34+
});

web-test-runner.config.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ const commonjs = fromRollup(rollupCommonjs);
55

66
export default {
77
nodeResolve: true,
8-
files: ["test/*.js"],
8+
files: [
9+
"test/*.js",
10+
"!test/virtual-machines.js"
11+
],
912
plugins: [
1013
commonjs({
1114
include: [

0 commit comments

Comments
 (0)