Skip to content

Commit bcf023c

Browse files
authored
Implement expect().toMatchInlineSnapshot() (#15570)
1 parent b7b1ca8 commit bcf023c

File tree

16 files changed

+1093
-126
lines changed

16 files changed

+1093
-126
lines changed

docs/guides/test/migrate-from-jest.md

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ Bun implements the vast majority of Jest's matchers, but compatibility isn't 100
3030

3131
Some notable missing features:
3232

33-
- `expect().toMatchInlineSnapshot()`
3433
- `expect().toHaveReturned()`
3534

3635
---

docs/guides/test/snapshot.md

+1-5
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ name: Use snapshot testing in `bun test`
44

55
Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`.
66

7-
{% callout %}
8-
The `.toMatchInlineSnapshot()` method is not yet supported.
9-
{% /callout %}
10-
117
```ts#snap.test.ts
128
import { test, expect } from "bun:test";
139

@@ -96,4 +92,4 @@ Ran 1 tests across 1 files. [102.00ms]
9692

9793
---
9894

99-
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner.
95+
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner.

docs/guides/test/update-snapshots.md

+1-5
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ name: Update snapshots in `bun test`
44

55
Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`.
66

7-
{% callout %}
8-
The `.toMatchInlineSnapshot()` method is not yet supported.
9-
{% /callout %}
10-
117
```ts#snap.test.ts
128
import { test, expect } from "bun:test";
139

@@ -47,4 +43,4 @@ Ran 1 tests across 1 files. [102.00ms]
4743

4844
---
4945

50-
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner.
46+
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner.

docs/test/writing.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ Bun implements the following matchers. Full Jest compatibility is on the roadmap
531531

532532
---
533533

534-
-
534+
-
535535
- [`.toMatchInlineSnapshot()`](https://jestjs.io/docs/expect#tomatchinlinesnapshotpropertymatchers-inlinesnapshot)
536536

537537
---

packages/bun-types/test.d.ts

+23
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,29 @@ declare module "bun:test" {
12951295
* @param hint Hint used to identify the snapshot in the snapshot file.
12961296
*/
12971297
toMatchSnapshot(propertyMatchers?: object, hint?: string): void;
1298+
/**
1299+
* Asserts that a value matches the most recent inline snapshot.
1300+
*
1301+
* @example
1302+
* expect("Hello").toMatchInlineSnapshot(`"Hello"`);
1303+
* @param value The latest snapshot value.
1304+
*/
1305+
toMatchInlineSnapshot(value?: string): void;
1306+
/**
1307+
* Asserts that a value matches the most recent inline snapshot.
1308+
*
1309+
* @example
1310+
* expect("Hello").toMatchInlineSnapshot(`"Hello"`);
1311+
* expect({ c: new Date() }).toMatchInlineSnapshot({ c: expect.any(Date) }, `
1312+
* {
1313+
* "v": Any<Date>,
1314+
* }
1315+
* `);
1316+
*
1317+
* @param propertyMatchers Object containing properties to match against the value.
1318+
* @param hint Hint used to identify the snapshot in the snapshot file.
1319+
*/
1320+
toMatchInlineSnapshot(propertyMatchers?: object, value?: string): void;
12981321
/**
12991322
* Asserts that an object matches a subset of properties.
13001323
*

src/bun.js/bindings/bindings.cpp

+73
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
#include "JavaScriptCore/CustomGetterSetter.h"
124124

125125
#include "ErrorStackFrame.h"
126+
#include "ErrorStackTrace.h"
126127
#include "ObjectBindings.h"
127128

128129
#if OS(DARWIN)
@@ -6030,3 +6031,75 @@ CPP_DECL bool Bun__CallFrame__isFromBunMain(JSC::CallFrame* callFrame, JSC::VM*
60306031
return false;
60316032
return source.string() == "builtin://bun/main"_s;
60326033
}
6034+
6035+
CPP_DECL void Bun__CallFrame__getCallerSrcLoc(JSC::CallFrame* callFrame, JSC::JSGlobalObject* globalObject, unsigned int* outSourceID, unsigned int* outLine, unsigned int* outColumn)
6036+
{
6037+
JSC::VM& vm = globalObject->vm();
6038+
JSC::LineColumn lineColumn;
6039+
JSC::SourceID sourceID = 0;
6040+
String sourceURL;
6041+
6042+
ZigStackFrame remappedFrame = {};
6043+
6044+
JSC::StackVisitor::visit(callFrame, vm, [&](JSC::StackVisitor& visitor) -> WTF::IterationStatus {
6045+
if (Zig::isImplementationVisibilityPrivate(visitor))
6046+
return WTF::IterationStatus::Continue;
6047+
6048+
if (visitor->hasLineAndColumnInfo()) {
6049+
lineColumn = visitor->computeLineAndColumn();
6050+
6051+
String sourceURLForFrame = visitor->sourceURL();
6052+
6053+
// Sometimes, the sourceURL is empty.
6054+
// For example, pages in Next.js.
6055+
if (sourceURLForFrame.isEmpty()) {
6056+
6057+
// hasLineAndColumnInfo() checks codeBlock(), so this is safe to access here.
6058+
const auto& source = visitor->codeBlock()->source();
6059+
6060+
// source.isNull() is true when the SourceProvider is a null pointer.
6061+
if (!source.isNull()) {
6062+
auto* provider = source.provider();
6063+
// I'm not 100% sure we should show sourceURLDirective here.
6064+
if (!provider->sourceURLDirective().isEmpty()) {
6065+
sourceURLForFrame = provider->sourceURLDirective();
6066+
} else if (!provider->sourceURL().isEmpty()) {
6067+
sourceURLForFrame = provider->sourceURL();
6068+
} else {
6069+
const auto& origin = provider->sourceOrigin();
6070+
if (!origin.isNull()) {
6071+
sourceURLForFrame = origin.string();
6072+
}
6073+
}
6074+
6075+
sourceID = provider->asID();
6076+
}
6077+
}
6078+
6079+
sourceURL = sourceURLForFrame;
6080+
6081+
return WTF::IterationStatus::Done;
6082+
}
6083+
6084+
return WTF::IterationStatus::Continue;
6085+
});
6086+
6087+
if (!sourceURL.isEmpty() and lineColumn.line > 0) {
6088+
OrdinalNumber originalLine = OrdinalNumber::fromOneBasedInt(lineColumn.line);
6089+
OrdinalNumber originalColumn = OrdinalNumber::fromOneBasedInt(lineColumn.column);
6090+
6091+
remappedFrame.position.line_zero_based = originalLine.zeroBasedInt();
6092+
remappedFrame.position.column_zero_based = originalColumn.zeroBasedInt();
6093+
remappedFrame.source_url = Bun::toStringRef(sourceURL);
6094+
6095+
Bun__remapStackFramePositions(globalObject, &remappedFrame, 1);
6096+
6097+
sourceURL = remappedFrame.source_url.toWTFString();
6098+
lineColumn.line = OrdinalNumber::fromZeroBasedInt(remappedFrame.position.line_zero_based).oneBasedInt();
6099+
lineColumn.column = OrdinalNumber::fromZeroBasedInt(remappedFrame.position.column_zero_based).oneBasedInt();
6100+
}
6101+
6102+
*outSourceID = sourceID;
6103+
*outLine = lineColumn.line;
6104+
*outColumn = lineColumn.column;
6105+
}

src/bun.js/bindings/bindings.zig

+22
Original file line numberDiff line numberDiff line change
@@ -6618,6 +6618,10 @@ pub const CallFrame = opaque {
66186618
};
66196619
}
66206620

6621+
pub fn arguments(self: *const CallFrame) []const JSValue {
6622+
// this presumably isn't allowed given that it doesn't exist
6623+
return self.argumentsPtr()[0..self.argumentsCount()];
6624+
}
66216625
pub fn arguments_old(self: *const CallFrame, comptime max: usize) Arguments(max) {
66226626
const len = self.argumentsCount();
66236627
const ptr = self.argumentsPtr();
@@ -6661,6 +6665,24 @@ pub const CallFrame = opaque {
66616665
}
66626666
return value;
66636667
}
6668+
6669+
extern fn Bun__CallFrame__getCallerSrcLoc(*const CallFrame, *JSGlobalObject, *c_uint, *c_uint, *c_uint) void;
6670+
pub const CallerSrcLoc = struct {
6671+
source_file_id: c_uint,
6672+
line: c_uint,
6673+
column: c_uint,
6674+
};
6675+
pub fn getCallerSrcLoc(call_frame: *const CallFrame, globalThis: *JSGlobalObject) CallerSrcLoc {
6676+
var source_id: c_uint = undefined;
6677+
var line: c_uint = undefined;
6678+
var column: c_uint = undefined;
6679+
Bun__CallFrame__getCallerSrcLoc(call_frame, globalThis, &source_id, &line, &column);
6680+
return .{
6681+
.source_file_id = source_id,
6682+
.line = line,
6683+
.column = column,
6684+
};
6685+
}
66646686
};
66656687

66666688
pub const EncodedJSValue = extern union {

src/bun.js/event_loop.zig

+1
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,7 @@ pub const EventLoop = struct {
14381438
}
14391439
}
14401440

1441+
this.processGCTimer();
14411442
this.processGCTimer();
14421443
loop.tick();
14431444

0 commit comments

Comments
 (0)