diff options
author | 2023-10-11 06:27:19 +0300 | |
---|---|---|
committer | 2023-10-10 20:27:19 -0700 | |
commit | c2c3b0d4a9004ffbf8a86ac362b432a5d858dd0e (patch) | |
tree | 705ba4c40a7b60918215c337bc5c744b48a68ec3 | |
parent | 9a90d909661e9ddfb40d8b1af47803fd04645f5a (diff) | |
download | bun-c2c3b0d4a9004ffbf8a86ac362b432a5d858dd0e.tar.gz bun-c2c3b0d4a9004ffbf8a86ac362b432a5d858dd0e.tar.zst bun-c2c3b0d4a9004ffbf8a86ac362b432a5d858dd0e.zip |
feat(test): implement `toEqualIgnoringWhitespace` (#6293)
* feat(test): implement `toEqualIgnoringWhitespace`
* equality check in matcher & incorrect arg error
-rw-r--r-- | packages/bun-types/bun-test.d.ts | 10 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses.cpp | 32 | ||||
-rw-r--r-- | src/bun.js/bindings/generated_classes.zig | 3 | ||||
-rw-r--r-- | src/bun.js/test/expect.zig | 77 | ||||
-rw-r--r-- | src/bun.js/test/jest.classes.ts | 4 | ||||
-rw-r--r-- | test/js/bun/test/expect.test.js | 21 | ||||
-rw-r--r-- | test/js/bun/test/jest-extended.test.js | 19 |
7 files changed, 165 insertions, 1 deletions
diff --git a/packages/bun-types/bun-test.d.ts b/packages/bun-types/bun-test.d.ts index a78c74b85..47380f63c 100644 --- a/packages/bun-types/bun-test.d.ts +++ b/packages/bun-types/bun-test.d.ts @@ -983,6 +983,16 @@ declare module "bun:test" { */ toBeWithin(start: number, end: number): void; /** + * Asserts that a value is equal to the expected string, ignoring any whitespace. + * + * @example + * expect(" foo ").toEqualIgnoringWhitespace("foo"); + * expect("bar").toEqualIgnoringWhitespace(" bar "); + * + * @param expected the expected string + */ + toEqualIgnoringWhitespace(expected: string): void; + /** * Asserts that a value is a `symbol`. * * @example diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp index f8541ac1e..6e3a2e1e5 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses.cpp +++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp @@ -7321,6 +7321,9 @@ JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toEndWithCallback); extern "C" EncodedJSValue ExpectPrototype__toEqual(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toEqualCallback); +extern "C" EncodedJSValue ExpectPrototype__toEqualIgnoringWhitespace(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toEqualIgnoringWhitespaceCallback); + extern "C" EncodedJSValue ExpectPrototype__toHaveBeenCalled(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toHaveBeenCalledCallback); @@ -7435,6 +7438,7 @@ static const HashTableValue JSExpectPrototypeTableValues[] = { { "toContainEqual"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toContainEqualCallback, 1 } }, { "toEndWith"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toEndWithCallback, 1 } }, { "toEqual"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toEqualCallback, 1 } }, + { "toEqualIgnoringWhitespace"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toEqualIgnoringWhitespaceCallback, 1 } }, { "toHaveBeenCalled"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveBeenCalledCallback, 0 } }, { "toHaveBeenCalledTimes"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveBeenCalledTimesCallback, 1 } }, { "toHaveBeenCalledWith"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveBeenCalledWithCallback, 1 } }, @@ -8602,6 +8606,34 @@ JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toEqualCallback, (JSGlobalObject * lex return ExpectPrototype__toEqual(thisObject->wrapped(), lexicalGlobalObject, callFrame); } +JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toEqualIgnoringWhitespaceCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSExpect* thisObject = jsDynamicCast<JSExpect*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + throwVMTypeError(lexicalGlobalObject, throwScope, "Expected 'this' to be instanceof Expect"_s); + return JSValue::encode({}); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + +#ifdef BUN_DEBUG + /** View the file name of the JS file that called this function + * from a debugger */ + SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm); + const char* fileName = sourceOrigin.string().utf8().data(); + static const char* lastFileName = nullptr; + if (lastFileName != fileName) { + lastFileName = fileName; + } +#endif + + return ExpectPrototype__toEqualIgnoringWhitespace(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toHaveBeenCalledCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) { auto& vm = lexicalGlobalObject->vm(); diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig index fc1aa2be6..c44d554fc 100644 --- a/src/bun.js/bindings/generated_classes.zig +++ b/src/bun.js/bindings/generated_classes.zig @@ -2211,6 +2211,8 @@ pub const JSExpect = struct { @compileLog("Expected Expect.toEndWith to be a callback but received " ++ @typeName(@TypeOf(Expect.toEndWith))); if (@TypeOf(Expect.toEqual) != CallbackType) @compileLog("Expected Expect.toEqual to be a callback but received " ++ @typeName(@TypeOf(Expect.toEqual))); + if (@TypeOf(Expect.toEqualIgnoringWhitespace) != CallbackType) + @compileLog("Expected Expect.toEqualIgnoringWhitespace to be a callback but received " ++ @typeName(@TypeOf(Expect.toEqualIgnoringWhitespace))); if (@TypeOf(Expect.toHaveBeenCalled) != CallbackType) @compileLog("Expected Expect.toHaveBeenCalled to be a callback but received " ++ @typeName(@TypeOf(Expect.toHaveBeenCalled))); if (@TypeOf(Expect.toHaveBeenCalledTimes) != CallbackType) @@ -2347,6 +2349,7 @@ pub const JSExpect = struct { @export(Expect.toContainEqual, .{ .name = "ExpectPrototype__toContainEqual" }); @export(Expect.toEndWith, .{ .name = "ExpectPrototype__toEndWith" }); @export(Expect.toEqual, .{ .name = "ExpectPrototype__toEqual" }); + @export(Expect.toEqualIgnoringWhitespace, .{ .name = "ExpectPrototype__toEqualIgnoringWhitespace" }); @export(Expect.toHaveBeenCalled, .{ .name = "ExpectPrototype__toHaveBeenCalled" }); @export(Expect.toHaveBeenCalledTimes, .{ .name = "ExpectPrototype__toHaveBeenCalledTimes" }); @export(Expect.toHaveBeenCalledWith, .{ .name = "ExpectPrototype__toHaveBeenCalledWith" }); diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig index 04b8670d2..ff58965f5 100644 --- a/src/bun.js/test/expect.zig +++ b/src/bun.js/test/expect.zig @@ -2649,6 +2649,83 @@ pub const Expect = struct { return .zero; } + pub fn toEqualIgnoringWhitespace(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { + defer this.postMatch(globalThis); + + const thisValue = callFrame.this(); + const _arguments = callFrame.arguments(1); + const arguments: []const JSValue = _arguments.ptr[0.._arguments.len]; + + if (arguments.len < 1) { + globalThis.throwInvalidArguments("toEqualIgnoringWhitespace() requires 1 argument", .{}); + return .zero; + } + + active_test_expectation_counter.actual += 1; + + const expected = arguments[0]; + const value: JSValue = this.getValue(globalThis, thisValue, "toEqualIgnoringWhitespace", "<green>expected<r>") orelse return .zero; + + if (!expected.isString()) { + globalThis.throw("toEqualIgnoringWhitespace() requires argument to be a string", .{}); + return .zero; + } + + const not = this.flags.not; + var pass = value.isString() and expected.isString(); + + if (pass) { + var valueStr = value.toString(globalThis).toSlice(globalThis, default_allocator).slice(); + var expectedStr = expected.toString(globalThis).toSlice(globalThis, default_allocator).slice(); + + var left: usize = 0; + var right: usize = 0; + + // Skip leading whitespaces + while (left < valueStr.len and std.ascii.isWhitespace(valueStr[left])) left += 1; + while (right < expectedStr.len and std.ascii.isWhitespace(expectedStr[right])) right += 1; + + while (left < valueStr.len and right < expectedStr.len) { + const left_char = valueStr[left]; + const right_char = expectedStr[right]; + + if (left_char != right_char) { + pass = false; + break; + } + + left += 1; + right += 1; + + // Skip trailing whitespaces + while (left < valueStr.len and std.ascii.isWhitespace(valueStr[left])) left += 1; + while (right < expectedStr.len and std.ascii.isWhitespace(expectedStr[right])) right += 1; + } + + if (left < valueStr.len or right < expectedStr.len) { + pass = false; + } + } + + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalThis, .quote_strings = true }; + const expected_fmt = expected.toFmt(globalThis, &formatter); + const value_fmt = value.toFmt(globalThis, &formatter); + + if (not) { + const fmt = comptime getSignature("toEqualIgnoringWhitespace", "<green>expected<r>", true) ++ "\n\n" ++ "Expected: not <green>{any}<r>\n" ++ "Received: <red>{any}<r>\n"; + globalThis.throwPretty(fmt, .{ expected_fmt, value_fmt }); + return .zero; + } + + const fmt = comptime getSignature("toEqualIgnoringWhitespace", "<green>expected<r>", false) ++ "\n\n" ++ "Expected: <green>{any}<r>\n" ++ "Received: <red>{any}<r>\n"; + globalThis.throwPretty(fmt, .{ expected_fmt, value_fmt }); + return .zero; + } + pub fn toBeSymbol(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue { defer this.postMatch(globalThis); diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts index 186c8f9a8..f10d9fb37 100644 --- a/src/bun.js/test/jest.classes.ts +++ b/src/bun.js/test/jest.classes.ts @@ -345,6 +345,10 @@ export default [ fn: "toBeWithin", length: 2, }, + toEqualIgnoringWhitespace: { + fn: "toEqualIgnoringWhitespace", + length: 1, + }, toBeSymbol: { fn: "toBeSymbol", length: 0, diff --git a/test/js/bun/test/expect.test.js b/test/js/bun/test/expect.test.js index 8c9959d01..13b549179 100644 --- a/test/js/bun/test/expect.test.js +++ b/test/js/bun/test/expect.test.js @@ -3080,6 +3080,27 @@ describe("expect()", () => { expect(Infinity).not.toBeWithin(-Infinity, Infinity); }); + test("toEqualIgnoringWhitespace()", () => { + expect("hello world").toEqualIgnoringWhitespace("hello world"); + expect(" hello world ").toEqualIgnoringWhitespace("hello world"); + expect(" h e l l o w o r l d ").toEqualIgnoringWhitespace("hello world"); + expect(" hello\nworld ").toEqualIgnoringWhitespace("hello\nworld"); + expect(`h + e + l + l + o`).toEqualIgnoringWhitespace("hello"); + expect(`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nec posuere felis. Aliquam tincidunt elit a nunc hendrerit maximus. Morbi semper tristique lectus, eget ullamcorper velit ullamcorper non. Aenean nibh augue, ultrices id ornare quis, eleifend id metus. Aliquam erat volutpat. Proin maximus, ligula at consequat venenatis, sapien odio auctor mi, sit amet placerat augue odio et orci. Vivamus tempus hendrerit tortor, et interdum est semper malesuada. Ut venenatis iaculis felis eget euismod. Suspendisse sed nisi eget massa fringilla rhoncus non quis enim. Mauris feugiat pellentesque justo, at sagittis augue sollicitudin vel. Pellentesque porttitor consequat mi nec varius. Praesent aliquet at justo nec finibus. Donec ut lorem eu ex dignissim pulvinar at sit amet sem. Ut fringilla sit amet dolor vitae convallis. Ut faucibus a purus sit amet fermentum. + Sed sit amet tortor magna. Pellentesque laoreet lorem at pulvinar efficitur. Nulla dictum nibh ac gravida semper. Duis tempus elit in ipsum feugiat porttitor.`).toEqualIgnoringWhitespace( + `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nec posuere felis. Aliquam tincidunt elit a nunc hendrerit maximus. Morbi semper tristique lectus, eget ullamcorper velit ullamcorper non. Aenean nibh augue, ultrices id ornare quis, eleifend id metus. Aliquam erat volutpat. Proin maximus, ligula at consequat venenatis, sapien odio auctor mi, sit amet placerat augue odio et orci. Vivamus tempus hendrerit tortor, et interdum est semper malesuada. Ut venenatis iaculis felis eget euismod. Suspendisse sed nisi eget massa fringilla rhoncus non quis enim. Mauris feugiat pellentesque justo, at sagittis augue sollicitudin vel. Pellentesque porttitor consequat mi nec varius. Praesent aliquet at justo nec finibus. Donec ut lorem eu ex dignissim pulvinar at sit amet sem. Ut fringilla sit amet dolor vitae convallis. Ut faucibus a purus sit amet fermentum. Sed sit amet tortor magna. Pellentesque laoreet lorem at pulvinar efficitur. Nulla dictum nibh ac gravida semper. Duis tempus elit in ipsum feugiat porttitor.`, + ); + + expect("hello world").not.toEqualIgnoringWhitespace("hello world!"); + expect(() => { + expect({}).not.toEqualIgnoringWhitespace({}); + }).toThrow(); + }); + test("toBeSymbol()", () => { expect(Symbol()).toBeSymbol(); expect(Symbol("")).toBeSymbol(); diff --git a/test/js/bun/test/jest-extended.test.js b/test/js/bun/test/jest-extended.test.js index 7e018d310..2194a8399 100644 --- a/test/js/bun/test/jest-extended.test.js +++ b/test/js/bun/test/jest-extended.test.js @@ -587,7 +587,24 @@ describe("jest-extended", () => { }); // test("toIncludeMultiple()") - // test("toEqualIgnoringWhitespace()") + test("toEqualIgnoringWhitespace()", () => { + expect("hello world").toEqualIgnoringWhitespace("hello world"); + expect(" hello world ").toEqualIgnoringWhitespace("hello world"); + expect(" h e l l o w o r l d ").toEqualIgnoringWhitespace("hello world"); + expect(" hello\nworld ").toEqualIgnoringWhitespace("hello\nworld"); + expect(`h + e + l + l + o`).toEqualIgnoringWhitespace("hello"); + expect(`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nec posuere felis. Aliquam tincidunt elit a nunc hendrerit maximus. Morbi semper tristique lectus, eget ullamcorper velit ullamcorper non. Aenean nibh augue, ultrices id ornare quis, eleifend id metus. Aliquam erat volutpat. Proin maximus, ligula at consequat venenatis, sapien odio auctor mi, sit amet placerat augue odio et orci. Vivamus tempus hendrerit tortor, et interdum est semper malesuada. Ut venenatis iaculis felis eget euismod. Suspendisse sed nisi eget massa fringilla rhoncus non quis enim. Mauris feugiat pellentesque justo, at sagittis augue sollicitudin vel. Pellentesque porttitor consequat mi nec varius. Praesent aliquet at justo nec finibus. Donec ut lorem eu ex dignissim pulvinar at sit amet sem. Ut fringilla sit amet dolor vitae convallis. Ut faucibus a purus sit amet fermentum. + Sed sit amet tortor magna. Pellentesque laoreet lorem at pulvinar efficitur. Nulla dictum nibh ac gravida semper. Duis tempus elit in ipsum feugiat porttitor.`).toEqualIgnoringWhitespace( + `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nec posuere felis. Aliquam tincidunt elit a nunc hendrerit maximus. Morbi semper tristique lectus, eget ullamcorper velit ullamcorper non. Aenean nibh augue, ultrices id ornare quis, eleifend id metus. Aliquam erat volutpat. Proin maximus, ligula at consequat venenatis, sapien odio auctor mi, sit amet placerat augue odio et orci. Vivamus tempus hendrerit tortor, et interdum est semper malesuada. Ut venenatis iaculis felis eget euismod. Suspendisse sed nisi eget massa fringilla rhoncus non quis enim. Mauris feugiat pellentesque justo, at sagittis augue sollicitudin vel. Pellentesque porttitor consequat mi nec varius. Praesent aliquet at justo nec finibus. Donec ut lorem eu ex dignissim pulvinar at sit amet sem. Ut fringilla sit amet dolor vitae convallis. Ut faucibus a purus sit amet fermentum. Sed sit amet tortor magna. Pellentesque laoreet lorem at pulvinar efficitur. Nulla dictum nibh ac gravida semper. Duis tempus elit in ipsum feugiat porttitor.`, + ); + + expect("hello world").not.toEqualIgnoringWhitespace("hello world!"); + expect({}).not.toEqualIgnoringWhitespace({}); + }); // Symbol |