support expect().toThrow(/pattern/) (#2314)

- fix time-zone-dependent test failure
This commit is contained in:
Alex Lam S.L
2023-03-06 03:33:38 +02:00
committed by GitHub
parent c7bfb3aa3a
commit d4bd156d9d
6 changed files with 124 additions and 71 deletions

View File

@@ -76,7 +76,7 @@ declare module "bun:test" {
toBeGreaterThanOrEqual(value: number | bigint): void;
toBeLessThan(value: number | bigint): void;
toBeLessThanOrEqual(value: number | bigint): void;
toThrow(error?: string | Error | ErrorConstructor): void;
toThrow(error?: string | Error | ErrorConstructor | RegExp): void;
}
}

View File

@@ -1768,7 +1768,7 @@ pub const Expect = struct {
if (expected_value.isString()) {
const received_message = result.getIfPropertyExistsImpl(globalObject, "message", 7);
// partial match (regex not supported)
// partial match
{
var expected_string = ZigString.Empty;
var received_string = ZigString.Empty;
@@ -1798,6 +1798,29 @@ pub const Expect = struct {
return .zero;
}
if (expected_value.isRegExp()) {
const received_message = result.getIfPropertyExistsImpl(globalObject, "message", 7);
if (expected_value.get(globalObject, "test")) |test_fn| {
const matches = test_fn.callWithThis(globalObject, expected_value, &.{received_message});
if (!matches.toBooleanSlow(globalObject)) return thisValue;
}
const fmt = signature ++ "\n\nExpected pattern: not <green>{any}<r>\nReceived message: <red>{any}<r>\n";
if (Output.enable_ansi_colors) {
globalObject.throw(Output.prettyFmt(fmt, true), .{
expected_value.toFmt(globalObject, &formatter),
received_message.toFmt(globalObject, &formatter),
});
return .zero;
}
globalObject.throw(Output.prettyFmt(fmt, false), .{
expected_value.toFmt(globalObject, &formatter),
received_message.toFmt(globalObject, &formatter),
});
return .zero;
}
if (expected_value.get(globalObject, "message")) |expected_message| {
const received_message = result.getIfPropertyExistsImpl(globalObject, "message", 7);
// no partial match for this case
@@ -1835,7 +1858,7 @@ pub const Expect = struct {
if (expected_value.isString()) {
if (_received_message) |received_message| {
// partial match (regex not supported)
// partial match
var expected_string = ZigString.Empty;
var received_string = ZigString.Empty;
expected_value.toZigString(&expected_string, globalObject);
@@ -1877,6 +1900,42 @@ pub const Expect = struct {
return .zero;
}
if (expected_value.isRegExp()) {
if (_received_message) |received_message| {
if (expected_value.get(globalObject, "test")) |test_fn| {
const matches = test_fn.callWithThis(globalObject, expected_value, &.{received_message});
if (matches.toBooleanSlow(globalObject)) return thisValue;
}
}
// error: message from received error does not match expected pattern
var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true };
if (_received_message) |received_message| {
const expected_value_fmt = expected_value.toFmt(globalObject, &formatter);
const received_message_fmt = received_message.toFmt(globalObject, &formatter);
const fmt = signature ++ "\n\n" ++ "Expected pattern: <green>{any}<r>\nReceived message: <red>{any}<r>\n";
if (Output.enable_ansi_colors) {
globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_value_fmt, received_message_fmt });
return .zero;
}
globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_value_fmt, received_message_fmt });
return .zero;
}
const expected_fmt = expected_value.toFmt(globalObject, &formatter);
const received_fmt = result.toFmt(globalObject, &formatter);
const fmt = signature ++ "\n\n" ++ "Expected pattern: <green>{any}<r>\nReceived value: <red>{any}<r>";
if (Output.enable_ansi_colors) {
globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_fmt, received_fmt });
return .zero;
}
globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_fmt, received_fmt });
return .zero;
}
if (expected_value.get(globalObject, "message")) |expected_message| {
if (_received_message) |received_message| {
if (received_message.isSameValue(expected_message, globalObject)) return thisValue;
@@ -1985,6 +2044,18 @@ pub const Expect = struct {
return .zero;
}
if (expected_value.isRegExp()) {
const expected_fmt = "\n\nExpected pattern: <green>{any}<r>\n\n" ++ received_line;
const fmt = signature ++ expected_fmt;
if (Output.enable_ansi_colors) {
globalObject.throw(Output.prettyFmt(fmt, true), .{expected_value.toFmt(globalObject, &formatter)});
return .zero;
}
globalObject.throw(Output.prettyFmt(fmt, false), .{expected_value.toFmt(globalObject, &formatter)});
return .zero;
}
if (expected_value.get(globalObject, "message")) |expected_message| {
const expected_fmt = "\n\nExpected message: <green>{any}<r>\n\n" ++ received_line;
const fmt = signature ++ expected_fmt;
@@ -2275,7 +2346,7 @@ pub const TestScope = struct {
task,
);
task.done_callback_state = .pending;
initial_value = JSValue.fromRef(callback.?).call(vm.global, &.{callback_func});
initial_value = JSValue.fromRef(callback).call(vm.global, &.{callback_func});
} else {
initial_value = js.JSObjectCallAsFunctionReturnValue(vm.global, callback, null, 0, null);
}

View File

@@ -8,7 +8,7 @@ false
null
undefined
Symbol(Symbol Description)
2022-02-27T05:11:48.999Z
2000-06-27T02:24:34.304Z
[ 123, 456, 789 ]
{
name: "foo"

View File

@@ -8,7 +8,7 @@ console.log(false);
console.log(null);
console.log(undefined);
console.log(Symbol("Symbol Description"));
console.log(new Date(2021, 12, 30, 666, 777, 888, 999));
console.log(new Date(Math.pow(2, 34) * 56));
console.log([123, 456, 789]);
console.log({ name: "foo" });
console.log({ a: 123, b: 456, c: 789 });

View File

@@ -3,35 +3,6 @@
import { expect, it } from "bun:test";
const crypto = require("crypto");
const assert = {
strictEqual: (a, b) => {
expect(a).toEqual(b);
},
deepStrictEqual: (a, b) => {
expect(a).toEqual(b);
},
throws: (fn, err) => {
try {
fn();
throw "Fail";
} catch (e) {
if (err.name) {
expect(e?.name).toEqual(err.name);
}
// if (err.message) {
// expect(err.message.test(e?.message)).toBeTruthy();
// }
if (err.code) {
expect(e?.code).toEqual(err.code);
}
expect(e).not.toEqual("Fail");
return;
}
},
};
const good = [
// Zero-length key is legal, functions as a parameter validation check.
{
@@ -157,8 +128,24 @@ const badargs = [
args: ["", "", null],
expected: { code: "ERR_INVALID_ARG_TYPE" /*message: /"keylen"/ */ },
},
{
args: ["", "", 42, null],
expected: { code: "ERR_INVALID_ARG_TYPE" },
},
// TODO: throw on these
// {
// args: ["", "", 42, {}],
// expected: { code: "ERR_INVALID_ARG_TYPE" },
// },
// {
// args: ["", "", 42, {}, {}],
// expected: { code: "ERR_INVALID_ARG_TYPE" },
// },
// {
// args: ["", "", 42, {}, null],
// expected: { code: "ERR_INVALID_ARG_TYPE" },
// },
// {
// args: ["", "", 0.42],
// expected: { code: "ERR_OUT_OF_RANGE" /*message: /"keylen"/ */ },
// },
@@ -170,41 +157,37 @@ const badargs = [
// args: ["", "", 2147485780],
// expected: { code: "ERR_OUT_OF_RANGE" /*message: /"keylen"/ */ },
// },
// {
// args: ["", "", 0, { maxmem: 2 ** 53 }],
// expected: { code: "ERR_OUT_OF_RANGE" /*message: /"keylen"/ */ },
// },
];
it("scrypt good", () => {
for (const options of good) {
const { pass, salt, keylen, expected } = options;
const actual = crypto.scryptSync(pass, salt, keylen, options);
assert.strictEqual(actual.toString("hex"), expected);
expect(actual.toString("hex")).toBe(expected);
}
});
it("scrypt bad", () => {
for (const options of bad) {
const expected = {
message: /Invalid scrypt param/,
};
assert.throws(() => crypto.scryptSync("pass", "salt", 1, options), expected);
expect(() => crypto.scryptSync("pass", "salt", 1, options)).toThrow(/Invalid scrypt param/);
}
});
it("scrypt toobig", () => {
for (const options of toobig) {
const expected = {
message: /Invalid scrypt param/,
};
assert.throws(() => crypto.scryptSync("pass", "salt", 1, options), expected);
expect(() => crypto.scryptSync("pass", "salt", 1, options)).toThrow(/Invalid scrypt param/);
}
});
it("scrypt defaults eql", () => {
{
const defaults = { N: 16384, p: 1, r: 8 };
const expected = crypto.scryptSync("pass", "salt", 1, defaults);
const actual = crypto.scryptSync("pass", "salt", 1);
assert.deepStrictEqual(actual.toString("hex"), expected.toString("hex"));
}
const defaults = { N: 16384, p: 1, r: 8 };
const expected = crypto.scryptSync("pass", "salt", 1, defaults);
const actual = crypto.scryptSync("pass", "salt", 1);
expect(actual.toString("hex")).toBe(expected.toString("hex"));
});
// TODO: DEFAULT_ENCODING is read-only
@@ -217,27 +200,23 @@ it("scrypt defaults eql", () => {
// const testEncoding = "latin1";
// crypto.DEFAULT_ENCODING = testEncoding;
// const actual = crypto.scryptSync("pass", "salt", 1);
// assert.deepStrictEqual(actual, expected.toString(testEncoding));
// expect(actual).toBe(expected.toString(testEncoding));
// crypto.DEFAULT_ENCODING = defaultEncoding;
// }
// });
it("scrypt badargs", () => {
{
for (const { args, expected } of badargs) {
assert.throws(() => crypto.scryptSync(...args), expected);
for (const { args, expected } of badargs) {
try {
crypto.scryptSync(...args);
expect(() => {}).toThrow();
} catch (e) {
if (!("code" in e)) throw e;
expect(e.code).toBe(expected.code);
}
}
{
const expected = { code: "ERR_INVALID_ARG_TYPE" };
assert.throws(() => crypto.scryptSync("", "", 42, null), expected);
// assert.throws(() => crypto.scryptSync("", "", 42, {}, null), expected);
// assert.throws(() => crypto.scryptSync("", "", 42, {}), expected);
// assert.throws(() => crypto.scryptSync("", "", 42, {}, {}), expected);
}
// {
// // Values for maxmem that do not fit in 32 bits but that are still safe
// // integers should be allowed.
@@ -247,13 +226,7 @@ it("scrypt badargs", () => {
// 4,
// { maxmem: 2 ** 52 },
// common.mustSucceed((actual) => {
// assert.strictEqual(actual.toString("hex"), "d72c87d0");
// expect(actual.toString("hex")).toBe("d72c87d0");
// }),
// );
// // Values that exceed Number.isSafeInteger should not be allowed.
// assert.throws(() => crypto.scryptSync("", "", 0, { maxmem: 2 ** 53 }), {
// code: "ERR_OUT_OF_RANGE",
// });
// }
});

View File

@@ -156,11 +156,20 @@ test("toThrow", () => {
throw err;
}).toThrow(err);
var err = new Error("good");
expect(() => {
throw err;
throw new Error("good");
}).toThrow();
expect(() => {
throw new Error("foo");
}).toThrow(/oo/);
expect(() =>
expect(() => {
throw new Error("bar");
}).toThrow(/baz/),
).toThrow("/baz/");
expect(() => {
return true;
}).not.toThrow();