mirror of
https://github.com/oven-sh/bun
synced 2026-02-17 22:32:06 +00:00
* Change status icon for skipped tests from "-" to "»" * Show file path instead of filename in `bun test` * Emit collapsable logs when running `bun test` in Github Actions https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#grouping-log-lines * Add fallback for test icons when emojis are not available * Only check for GITHUB_ACTIONS when running `bun test` * Emit error annotations when running `bun test` in Github Actions https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message * Remove ANSI output from Github annotation, it doesn't work * Remove outdated code from internal test runner * Add GithubActionFormatter to handle cases where error name or message is already ANSI * Fix formatting of test * Fix #3070 * Implement `bun test --run-todo` By default, `test.todo()` is no longer run, unless `--run-todo` is specified. * Fix test that relies on test.todo() being run * Support vitest-style test options * Disable GITHUB_ACTION in test harness * Add types for TestOptions * Fix bug where test.skip() actually ran * Implement `test.skipIf()` and `describe.skipIf()` * Implement `test.runIf()` * Move DiffFormatter to its own file * Fix bug where Bun.inspect() would emit a Github annotation * Introduce `bun test --only`, rename `--run-todo` to `--todo` * Implement `test.if()`, `describe.if()`, and other test fixes * Remove unwanted files from last commit * Fix last reference to --run-todo * Fix memory issues with printing github actions text * Update bindings.zig * Fix bug with `test.only()` * Remove debug test * Make the github annotations better * Improve .vscode/launch.json * Implement `expect().toBeNil()` * Remove .only() from test * Implement toBeBoolean(), toBeTrue(), toBeFalse() * Add lots of matchers * toBeNil() * toBeBoolean() * toBeTrue() * toBeFalse() * toBeNumber() * toBeInteger() * toBeFinite() * toBePositive() * toBeNegative() * toBeWithin() * toBeSymbol() * toBeFunction() * toBeDate() * toBeString() * toInclude() * toStartWith() * toEndWith() * Fix #3135 * Reduce verbosity of test * Fix snapshot bug --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
294 lines
12 KiB
Zig
294 lines
12 KiB
Zig
const std = @import("std");
|
|
const bun = @import("root").bun;
|
|
const MutableString = bun.MutableString;
|
|
const Output = bun.Output;
|
|
const default_allocator = bun.default_allocator;
|
|
const string = bun.string;
|
|
const JSC = bun.JSC;
|
|
const JSValue = JSC.JSValue;
|
|
const JSGlobalObject = JSC.JSGlobalObject;
|
|
const ZigConsoleClient = JSC.ZigConsoleClient;
|
|
const DiffMatchPatch = @import("../../deps/diffz/DiffMatchPatch.zig");
|
|
|
|
pub const DiffFormatter = struct {
|
|
received_string: ?string = null,
|
|
expected_string: ?string = null,
|
|
received: ?JSValue = null,
|
|
expected: ?JSValue = null,
|
|
globalObject: *JSGlobalObject,
|
|
not: bool = false,
|
|
|
|
pub fn format(this: DiffFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
|
if (this.expected_string != null and this.received_string != null) {
|
|
const received = this.received_string.?;
|
|
const expected = this.expected_string.?;
|
|
|
|
var dmp = DiffMatchPatch.default;
|
|
dmp.diff_timeout = 200;
|
|
var diffs = try dmp.diff(default_allocator, received, expected, false);
|
|
defer diffs.deinit(default_allocator);
|
|
|
|
const equal_fmt = "<d>{s}<r>";
|
|
const delete_fmt = "<red>{s}<r>";
|
|
const insert_fmt = "<green>{s}<r>";
|
|
|
|
try writer.writeAll("Expected: ");
|
|
for (diffs.items) |df| {
|
|
switch (df.operation) {
|
|
.delete => continue,
|
|
.insert => {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(insert_fmt, true), .{df.text});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(insert_fmt, false), .{df.text});
|
|
}
|
|
},
|
|
.equal => {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text});
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
try writer.writeAll("\nReceived: ");
|
|
for (diffs.items) |df| {
|
|
switch (df.operation) {
|
|
.insert => continue,
|
|
.delete => {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(delete_fmt, true), .{df.text});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(delete_fmt, false), .{df.text});
|
|
}
|
|
},
|
|
.equal => {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text});
|
|
}
|
|
},
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (this.received == null or this.expected == null) return;
|
|
|
|
const received = this.received.?;
|
|
const expected = this.expected.?;
|
|
var received_buf = MutableString.init(default_allocator, 0) catch unreachable;
|
|
var expected_buf = MutableString.init(default_allocator, 0) catch unreachable;
|
|
defer {
|
|
received_buf.deinit();
|
|
expected_buf.deinit();
|
|
}
|
|
|
|
{
|
|
var buffered_writer_ = MutableString.BufferedWriter{ .context = &received_buf };
|
|
var buffered_writer = &buffered_writer_;
|
|
|
|
var buf_writer = buffered_writer.writer();
|
|
const Writer = @TypeOf(buf_writer);
|
|
|
|
const fmt_options = ZigConsoleClient.FormatOptions{
|
|
.enable_colors = false,
|
|
.add_newline = false,
|
|
.flush = false,
|
|
.ordered_properties = true,
|
|
.quote_strings = true,
|
|
};
|
|
ZigConsoleClient.format(
|
|
.Debug,
|
|
this.globalObject,
|
|
@ptrCast([*]const JSValue, &received),
|
|
1,
|
|
Writer,
|
|
Writer,
|
|
buf_writer,
|
|
fmt_options,
|
|
);
|
|
buffered_writer.flush() catch unreachable;
|
|
|
|
buffered_writer_.context = &expected_buf;
|
|
|
|
ZigConsoleClient.format(
|
|
.Debug,
|
|
this.globalObject,
|
|
@ptrCast([*]const JSValue, &this.expected),
|
|
1,
|
|
Writer,
|
|
Writer,
|
|
buf_writer,
|
|
fmt_options,
|
|
);
|
|
buffered_writer.flush() catch unreachable;
|
|
}
|
|
|
|
const received_slice = received_buf.toOwnedSliceLeaky();
|
|
const expected_slice = expected_buf.toOwnedSliceLeaky();
|
|
|
|
if (this.not) {
|
|
const not_fmt = "Expected: not <green>{s}<r>";
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(not_fmt, true), .{expected_slice});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(not_fmt, false), .{expected_slice});
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (received.determineDiffMethod(expected, this.globalObject)) {
|
|
.none => {
|
|
const fmt = "Expected: <green>{any}<r>\nReceived: <red>{any}<r>";
|
|
var formatter = ZigConsoleClient.Formatter{ .globalThis = this.globalObject, .quote_strings = true };
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(fmt, true), .{
|
|
expected.toFmt(this.globalObject, &formatter),
|
|
received.toFmt(this.globalObject, &formatter),
|
|
});
|
|
return;
|
|
}
|
|
|
|
try writer.print(Output.prettyFmt(fmt, true), .{
|
|
expected.toFmt(this.globalObject, &formatter),
|
|
received.toFmt(this.globalObject, &formatter),
|
|
});
|
|
return;
|
|
},
|
|
.character => {
|
|
var dmp = DiffMatchPatch.default;
|
|
dmp.diff_timeout = 200;
|
|
var diffs = try dmp.diff(default_allocator, received_slice, expected_slice, false);
|
|
defer diffs.deinit(default_allocator);
|
|
|
|
const equal_fmt = "<d>{s}<r>";
|
|
const delete_fmt = "<red>{s}<r>";
|
|
const insert_fmt = "<green>{s}<r>";
|
|
|
|
try writer.writeAll("Expected: ");
|
|
for (diffs.items) |df| {
|
|
switch (df.operation) {
|
|
.delete => continue,
|
|
.insert => {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(insert_fmt, true), .{df.text});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(insert_fmt, false), .{df.text});
|
|
}
|
|
},
|
|
.equal => {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text});
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
try writer.writeAll("\nReceived: ");
|
|
for (diffs.items) |df| {
|
|
switch (df.operation) {
|
|
.insert => continue,
|
|
.delete => {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(delete_fmt, true), .{df.text});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(delete_fmt, false), .{df.text});
|
|
}
|
|
},
|
|
.equal => {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text});
|
|
}
|
|
},
|
|
}
|
|
}
|
|
return;
|
|
},
|
|
.line => {
|
|
var dmp = DiffMatchPatch.default;
|
|
dmp.diff_timeout = 200;
|
|
var diffs = try dmp.diffLines(default_allocator, received_slice, expected_slice);
|
|
defer diffs.deinit(default_allocator);
|
|
|
|
const equal_fmt = "<d> {s}<r>";
|
|
const delete_fmt = "<red>+ {s}<r>";
|
|
const insert_fmt = "<green>- {s}<r>";
|
|
|
|
var insert_count: usize = 0;
|
|
var delete_count: usize = 0;
|
|
|
|
for (diffs.items) |df| {
|
|
var prev: usize = 0;
|
|
var curr: usize = 0;
|
|
switch (df.operation) {
|
|
.equal => {
|
|
while (curr < df.text.len) {
|
|
if (curr == df.text.len - 1 or df.text[curr] == '\n' and curr != 0) {
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text[prev .. curr + 1]});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text[prev .. curr + 1]});
|
|
}
|
|
prev = curr + 1;
|
|
}
|
|
curr += 1;
|
|
}
|
|
},
|
|
.insert => {
|
|
while (curr < df.text.len) {
|
|
if (curr == df.text.len - 1 or df.text[curr] == '\n' and curr != 0) {
|
|
insert_count += 1;
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(insert_fmt, true), .{df.text[prev .. curr + 1]});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(insert_fmt, false), .{df.text[prev .. curr + 1]});
|
|
}
|
|
prev = curr + 1;
|
|
}
|
|
curr += 1;
|
|
}
|
|
},
|
|
.delete => {
|
|
while (curr < df.text.len) {
|
|
if (curr == df.text.len - 1 or df.text[curr] == '\n' and curr != 0) {
|
|
delete_count += 1;
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt(delete_fmt, true), .{df.text[prev .. curr + 1]});
|
|
} else {
|
|
try writer.print(Output.prettyFmt(delete_fmt, false), .{df.text[prev .. curr + 1]});
|
|
}
|
|
prev = curr + 1;
|
|
}
|
|
curr += 1;
|
|
}
|
|
},
|
|
}
|
|
if (df.text[df.text.len - 1] != '\n') try writer.writeAll("\n");
|
|
}
|
|
|
|
if (Output.enable_ansi_colors) {
|
|
try writer.print(Output.prettyFmt("\n<green>- Expected - {d}<r>\n", true), .{insert_count});
|
|
try writer.print(Output.prettyFmt("<red>+ Received + {d}<r>", true), .{delete_count});
|
|
return;
|
|
}
|
|
try writer.print("\n- Expected - {d}\n", .{insert_count});
|
|
try writer.print("+ Received + {d}", .{delete_count});
|
|
return;
|
|
},
|
|
.word => {
|
|
// not implemented
|
|
// https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs#word-mode
|
|
},
|
|
}
|
|
return;
|
|
}
|
|
};
|