fix(bun:test): toContain and toContainEqual string fix (#8270)

* update toContain, toContainEqual, and tests

* fix build, use cursor

* `strings.indexOf` and `jsType` once
This commit is contained in:
Dylan Conway
2024-01-18 20:13:57 -08:00
committed by GitHub
parent e17709a2d3
commit 8efa01bd62
3 changed files with 73 additions and 17 deletions

View File

@@ -3360,6 +3360,18 @@ pub const JSValue = enum(JSValueReprInt) {
return this == .String;
}
pub inline fn isStringObject(this: JSType) bool {
return this == .StringObject;
}
pub inline fn isDerivedStringObject(this: JSType) bool {
return this == .DerivedStringObject;
}
pub inline fn isStringObjectLike(this: JSType) bool {
return this == .StringObject or this == .DerivedStringObject;
}
pub inline fn isStringLike(this: JSType) bool {
return switch (this) {
.String, .StringObject, .DerivedStringObject => true,
@@ -4183,6 +4195,27 @@ pub const JSValue = enum(JSValueReprInt) {
return jsType(this).isStringLike();
}
/// Returns true only for string literals
/// - `" string literal"`
pub inline fn isStringLiteral(this: JSValue) bool {
if (!this.isCell()) {
return false;
}
return jsType(this).isString();
}
/// Returns true if
/// - `new String("123")`
/// - `class DerivedString extends String; new DerivedString("123")`
pub inline fn isStringObjectLike(this: JSValue) bool {
if (!this.isCell()) {
return false;
}
return jsType(this).isStringObjectLike();
}
pub fn isBigInt(this: JSValue) bool {
return cppFn("isBigInt", .{this});
}

View File

@@ -670,13 +670,15 @@ pub const Expect = struct {
break;
}
}
} else if (value.isString() and expected.isString()) {
const value_string = value.toString(globalObject).toSlice(globalObject, default_allocator).slice();
const expected_string = expected.toString(globalObject).toSlice(globalObject, default_allocator).slice();
} else if (value.isStringLiteral() and expected.isStringLiteral()) {
const value_string = value.toString(globalObject).toSlice(globalObject, default_allocator);
defer value_string.deinit();
const expected_string = expected.toString(globalObject).toSlice(globalObject, default_allocator);
defer expected_string.deinit();
if (expected_string.len == 0) { // edge case empty string is always contained
pass = true;
} else if (strings.contains(value_string, expected_string)) {
} else if (strings.contains(value_string.slice(), expected_string.slice())) {
pass = true;
} else if (value_string.len == 0 and expected_string.len == 0) { // edge case two empty strings are true
pass = true;
@@ -952,7 +954,10 @@ pub const Expect = struct {
pass: *bool,
};
if (value.jsTypeLoose().isArrayLike()) {
const value_type = value.jsType();
const expected_type = expected.jsType();
if (value_type.isArrayLike()) {
var itr = value.arrayIterator(globalObject);
while (itr.next()) |item| {
if (item.jestDeepEquals(expected, globalObject)) {
@@ -960,13 +965,24 @@ pub const Expect = struct {
break;
}
}
} else if (value.isString() and expected.isString()) {
const value_string = value.toString(globalObject).toSlice(globalObject, default_allocator).slice();
const expected_string = expected.toString(globalObject).toSlice(globalObject, default_allocator).slice();
if (strings.contains(value_string, expected_string)) {
pass = true;
} else if (value_string.len == 0 and expected_string.len == 0) { // edge case two empty strings are true
pass = true;
} else if (value_type.isStringLike() and expected_type.isStringLike()) {
if (expected_type.isStringObjectLike() and value_type.isString()) pass = false else {
const value_string = value.toString(globalObject).toSlice(globalObject, default_allocator);
defer value_string.deinit();
const expected_string = expected.toString(globalObject).toSlice(globalObject, default_allocator);
defer expected_string.deinit();
// jest does not have a `typeof === "string"` check for `toContainEqual`.
// it immediately spreads the value into an array.
var expected_codepoint_cursor = strings.CodepointIterator.Cursor{};
var expected_iter = strings.CodepointIterator.init(expected_string.slice());
_ = expected_iter.next(&expected_codepoint_cursor);
pass = if (expected_iter.next(&expected_codepoint_cursor))
false
else
strings.indexOf(value_string.slice(), expected_string.slice()) != null;
}
} else if (value.isIterable(globalObject)) {
var expected_entry = ExpectedEntry{

View File

@@ -2086,9 +2086,9 @@ describe("expect()", () => {
test.each([
["hello", "h"],
["hello", ""],
["hello", "hello"],
[new String("hello"), "h"],
[new String("hello"), "hello"],
["emoji: 😃", "😃"],
["😄", "😄"],
["", ""],
@@ -2118,9 +2118,10 @@ describe("expect()", () => {
test.each([
["hello", "a"],
["hello", "hello?"],
["hello", ""],
[new String("hello"), "a"],
[new String("hello"), "hello?"],
[new String("hello"), "hello"],
[new String("hello"), "llo"],
[new String("hello"), ""],
["emoji: 😃", "😄"],
[[1, 2, 3], -1],
@@ -2147,12 +2148,10 @@ describe("expect()", () => {
test.each([
["hello", "h"],
["hello", "hello"],
[new String("hello"), "h"],
[new String("hello"), "hello"],
["emoji: 😃", "😃"],
["😄", "😄"],
["", ""],
[new String("😄"), "😄"],
[[1, 2, 3], 1],
[[{ a: 1 }, { b: 2 }, { c: 3 }], { c: 3 }],
[[{}], {}],
@@ -2197,10 +2196,18 @@ describe("expect()", () => {
test.each([
["hello", "a"],
["hello", "hello?"],
["hello", "hello"],
["hello", "llo"],
["hello", ""],
["", ""],
[new String(""), new String("")],
[new String("hello"), "a"],
[new String("hello"), "hello?"],
[new String("hello"), "hello"],
[new String("hello"), "llo"],
[new String("hello"), ""],
["😄", new String("😄")],
["1", new String("1")],
["emoji: 😃", "😄"],
[[1, 2, 3], -1],
[[{ a: 1 }, { b: 2 }, { c: 3 }], { c: 1 }],