Files
bun.sh/src/bun.js/test/diff_format.zig
Ashcon Partovi e632941c52 Small improvements to bun test (#3071)
* 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>
2023-05-31 23:12:04 -07:00

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;
}
};