mirror of
https://github.com/react/react-native.git
synced 2026-09-28 13:23:09 +08:00
Add toMatchInlineSnapshot support (#55892)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55892 Adds `toMatchInlineSnapshot()` to the Fantom custom `expect` implementation, matching Jest's inline snapshot behavior. On first run, snapshot values are written directly into the test source file as template literal arguments. On subsequent runs, values are compared and the test fails on mismatch unless the `-u` flag is passed to force an update. The implementation spans the two-process Fantom architecture: **Runtime (Hermes VM):** - `snapshotContext.js`: New `toMatchInlineSnapshot` method that compares received values against existing inline snapshots, captures stack traces for new/mismatched snapshots, and tracks results per test. - `expect.js`: New `toMatchInlineSnapshot` method on the `Expect` class that serializes values with `prettyFormat` and delegates to snapshot context. - `setup.js`: Plumbs `inlineSnapshotResults` through `TestCaseResult`. **Runner (Node.js):** - `snapshotUtils.js`: New `processInlineSnapshotResults` resolves VM stack traces back to original source locations via source map symbolication. New `saveInlineSnapshotsToSource` directly rewrites the test source file by finding each `toMatchInlineSnapshot(...)` call and replacing its argument with the formatted template literal. This avoids jest-snapshot's `saveInlineSnapshots` which requires Babel and Prettier 2.x. - `runner.js`: Generates source maps when inline snapshots need updating, collects pending snapshots across test configs, and writes them after all configs complete. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D95077617 fbshipit-source-id: 6a25c8ce966ed71ecd8977c13c25de5907311c6e
This commit is contained in:
committed by
meta-codesync[bot]
parent
57e42c29d6
commit
43278f94d2
+36
-2
@@ -13,9 +13,13 @@ import type {
|
||||
TestCaseResult,
|
||||
TestSuiteResult,
|
||||
} from '../runtime/setup';
|
||||
import type {TestSnapshotResults} from '../runtime/snapshotContext';
|
||||
import type {
|
||||
TestInlineSnapshotResults,
|
||||
TestSnapshotResults,
|
||||
} from '../runtime/snapshotContext';
|
||||
import type {BenchmarkResult} from '../src/Benchmark';
|
||||
import type {CoverageMap} from './coverage/types.flow';
|
||||
import type {PendingInlineSnapshot} from './snapshotUtils';
|
||||
import type {
|
||||
AsyncCommandResult,
|
||||
ConsoleLogMessage,
|
||||
@@ -45,6 +49,8 @@ import {
|
||||
} from './paths';
|
||||
import {
|
||||
getInitialSnapshotData,
|
||||
processInlineSnapshotResults,
|
||||
saveInlineSnapshotsToSource,
|
||||
updateSnapshotsAndGetJestSnapshotResult,
|
||||
} from './snapshotUtils';
|
||||
import {
|
||||
@@ -239,6 +245,7 @@ module.exports = async function runTest(
|
||||
|
||||
const testResultsByConfig = [];
|
||||
const benchmarkResults = [];
|
||||
const allPendingInlineSnapshots: Array<PendingInlineSnapshot> = [];
|
||||
|
||||
const skippedTestResults = ({
|
||||
ancestorTitles,
|
||||
@@ -255,6 +262,7 @@ module.exports = async function runTest(
|
||||
fullName: title,
|
||||
numPassingAsserts: 0,
|
||||
snapshotResults: {} as TestSnapshotResults,
|
||||
inlineSnapshotResults: [] as TestInlineSnapshotResults,
|
||||
status: 'pending' as TestCaseResult['status'],
|
||||
testFilePath: testPath,
|
||||
title,
|
||||
@@ -414,7 +422,16 @@ module.exports = async function runTest(
|
||||
const [processedResult, benchmarkResult] =
|
||||
await processRNTesterCommandResult(rnTesterCommandResult);
|
||||
|
||||
if (containsError(processedResult) || EnvironmentOptions.profileJS) {
|
||||
const hasInlineSnapshotUpdates =
|
||||
processedResult.testResults?.some(r =>
|
||||
r.inlineSnapshotResults.some(ir => !ir.pass),
|
||||
) ?? false;
|
||||
|
||||
if (
|
||||
containsError(processedResult) ||
|
||||
EnvironmentOptions.profileJS ||
|
||||
hasInlineSnapshotUpdates
|
||||
) {
|
||||
await createSourceMap({
|
||||
...bundleOptions,
|
||||
out: sourceMapPath,
|
||||
@@ -452,6 +469,20 @@ module.exports = async function runTest(
|
||||
snapshotResults: testResult.snapshotResults,
|
||||
})) ?? [];
|
||||
|
||||
// Process inline snapshot results (requires source map for symbolication)
|
||||
if (hasInlineSnapshotUpdates) {
|
||||
const inlineResults = nullthrows(processedResult.testResults).map(
|
||||
r => r.inlineSnapshotResults,
|
||||
);
|
||||
const pending = processInlineSnapshotResults(
|
||||
snapshotState,
|
||||
inlineResults,
|
||||
sourceMapPath,
|
||||
testPath,
|
||||
);
|
||||
allPendingInlineSnapshots.push(...pending);
|
||||
}
|
||||
|
||||
// Display the Fantom test configuration as a suffix of the name of the root
|
||||
// `describe` block of the test, or adds one if the test doesn't have it.
|
||||
const maybeCommonAncestor = testResults[0]?.ancestorTitles?.[0];
|
||||
@@ -515,6 +546,9 @@ module.exports = async function runTest(
|
||||
}
|
||||
}
|
||||
|
||||
// Write pending inline snapshots to the test source file
|
||||
saveInlineSnapshotsToSource(testPath, allPendingInlineSnapshots);
|
||||
|
||||
const endTime = Date.now();
|
||||
|
||||
const testResults = testResultsByConfig.flat();
|
||||
|
||||
+248
-1
@@ -8,9 +8,16 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {TestSnapshotResults} from '../runtime/snapshotContext';
|
||||
import type {
|
||||
TestInlineSnapshotResults,
|
||||
TestSnapshotResults,
|
||||
} from '../runtime/snapshotContext';
|
||||
import type {SnapshotState} from 'jest-snapshot';
|
||||
|
||||
import {symbolicateStackTrace} from './utils';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
type JestSnapshotResult = {
|
||||
added: number,
|
||||
fileDeleted: boolean,
|
||||
@@ -46,6 +53,246 @@ export const getInitialSnapshotData = (
|
||||
return initialData;
|
||||
};
|
||||
|
||||
const STACK_FRAME_REGEX: RegExp = /at .* \((.+):(\d+):(\d+)\)/;
|
||||
|
||||
/**
|
||||
* Extract the frame from a symbolicated stack trace that points to the test file.
|
||||
*/
|
||||
function extractTestFileFrame(
|
||||
symbolicatedStack: string,
|
||||
testPath: string,
|
||||
): {file: string, line: number, column: number} | null {
|
||||
const testBaseName = path.basename(testPath);
|
||||
const lines = symbolicatedStack.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
const match = line.match(STACK_FRAME_REGEX);
|
||||
if (match) {
|
||||
const file = match[1];
|
||||
const lineNumber = parseInt(match[2], 10);
|
||||
const column = parseInt(match[3], 10);
|
||||
|
||||
// Match by basename since source map paths may be relative
|
||||
if (file.endsWith(testBaseName) || file === testPath) {
|
||||
return {file, line: lineNumber, column};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export type PendingInlineSnapshot = {
|
||||
line: number,
|
||||
snapshot: string,
|
||||
};
|
||||
|
||||
/**
|
||||
* Process inline snapshot results from the VM. Resolves stack traces to
|
||||
* source locations and returns pending snapshots for source file rewriting.
|
||||
* Also updates snapshotState counters.
|
||||
*/
|
||||
export const processInlineSnapshotResults = (
|
||||
snapshotState: SnapshotState,
|
||||
inlineSnapshotResults: Array<TestInlineSnapshotResults>,
|
||||
sourceMapPath: string,
|
||||
testPath: string,
|
||||
): Array<PendingInlineSnapshot> => {
|
||||
const pending: Array<PendingInlineSnapshot> = [];
|
||||
|
||||
for (const results of inlineSnapshotResults) {
|
||||
for (const result of results) {
|
||||
if (result.pass) {
|
||||
snapshotState.matched++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// For new snapshots in CI mode, don't write
|
||||
if (result.isNew && snapshotState._updateSnapshot === 'none') {
|
||||
snapshotState.unmatched++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// For mismatches without -u, don't write to source
|
||||
if (!result.isNew && snapshotState._updateSnapshot !== 'all') {
|
||||
snapshotState.unmatched++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Symbolicate stack trace to get original source location
|
||||
const symbolicatedStack = symbolicateStackTrace(
|
||||
sourceMapPath,
|
||||
result.stackTrace,
|
||||
);
|
||||
const frame = extractTestFileFrame(symbolicatedStack, testPath);
|
||||
|
||||
if (frame == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pending.push({
|
||||
line: frame.line,
|
||||
snapshot: result.value,
|
||||
});
|
||||
|
||||
snapshotState._dirty = true;
|
||||
if (result.isNew) {
|
||||
snapshotState.added++;
|
||||
snapshotState.matched++;
|
||||
} else {
|
||||
snapshotState.updated++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pending;
|
||||
};
|
||||
|
||||
/**
|
||||
* Escape a string for use inside a template literal.
|
||||
*/
|
||||
function escapeForTemplateLiteral(str: string): string {
|
||||
return str.replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a snapshot value as a template literal string for source insertion.
|
||||
*/
|
||||
function formatSnapshotAsTemplateLiteral(
|
||||
snapshot: string,
|
||||
baseIndent: string,
|
||||
): string {
|
||||
const escaped = escapeForTemplateLiteral(snapshot);
|
||||
|
||||
if (!escaped.includes('\n')) {
|
||||
return '`' + escaped + '`';
|
||||
}
|
||||
|
||||
// Multiline: snapshot has \n prefix and \n suffix from addExtraLineBreaks.
|
||||
// Indent content lines and place closing backtick at baseIndent.
|
||||
const lines = escaped.split('\n');
|
||||
const result = lines.map((line, i) => {
|
||||
if (i === 0) {
|
||||
// Opening empty line (after the leading \n)
|
||||
return line;
|
||||
}
|
||||
if (i === lines.length - 1) {
|
||||
// Closing line: just the base indent before the backtick
|
||||
return baseIndent;
|
||||
}
|
||||
if (line === '') {
|
||||
return '';
|
||||
}
|
||||
return baseIndent + ' ' + line;
|
||||
});
|
||||
|
||||
return '`' + result.join('\n') + '`';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the offset of the closing paren that matches the open paren at
|
||||
* `openParenOffset`. Handles template literals inside the arguments.
|
||||
*/
|
||||
function findMatchingParen(source: string, openParenOffset: number): number {
|
||||
let depth = 1;
|
||||
let i = openParenOffset + 1;
|
||||
let inTemplate = false;
|
||||
|
||||
while (i < source.length && depth > 0) {
|
||||
const ch = source[i];
|
||||
|
||||
if (inTemplate) {
|
||||
if (ch === '\\') {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if (ch === '`') {
|
||||
inTemplate = false;
|
||||
}
|
||||
} else {
|
||||
if (ch === '`') {
|
||||
inTemplate = true;
|
||||
} else if (ch === '(') {
|
||||
depth++;
|
||||
} else if (ch === ')') {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write pending inline snapshots directly into the test source file.
|
||||
* Replaces argument of each `toMatchInlineSnapshot(...)` call at the
|
||||
* resolved line with the formatted snapshot template literal.
|
||||
*/
|
||||
export function saveInlineSnapshotsToSource(
|
||||
testPath: string,
|
||||
pendingSnapshots: Array<PendingInlineSnapshot>,
|
||||
): void {
|
||||
if (pendingSnapshots.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduplicate by line (last value wins)
|
||||
const byLine = new Map<number, PendingInlineSnapshot>();
|
||||
for (const snapshot of pendingSnapshots) {
|
||||
byLine.set(snapshot.line, snapshot);
|
||||
}
|
||||
|
||||
let source = fs.readFileSync(testPath, 'utf8');
|
||||
const originalLines = source.split('\n');
|
||||
|
||||
// Process in reverse line order so earlier offsets stay valid
|
||||
const sorted = [...byLine.values()].sort((a, b) => b.line - a.line);
|
||||
|
||||
for (const {line, snapshot} of sorted) {
|
||||
const lineContent = originalLines[line - 1];
|
||||
if (lineContent == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const matcherIndex = lineContent.indexOf('toMatchInlineSnapshot');
|
||||
if (matcherIndex === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const parenIndex = lineContent.indexOf('(', matcherIndex);
|
||||
if (parenIndex === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute character offset of the open paren in the current source
|
||||
let lineOffset = 0;
|
||||
for (let i = 0; i < line - 1; i++) {
|
||||
lineOffset += originalLines[i].length + 1;
|
||||
}
|
||||
const openParenOffset = lineOffset + parenIndex;
|
||||
|
||||
const closeParenOffset = findMatchingParen(source, openParenOffset);
|
||||
if (closeParenOffset === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const baseIndent = lineContent.match(/^(\s*)/)?.[1] ?? '';
|
||||
const formatted = formatSnapshotAsTemplateLiteral(snapshot, baseIndent);
|
||||
|
||||
source =
|
||||
source.slice(0, openParenOffset + 1) +
|
||||
formatted +
|
||||
source.slice(closeParenOffset);
|
||||
}
|
||||
|
||||
fs.writeFileSync(testPath, source, 'utf8');
|
||||
}
|
||||
|
||||
export const updateSnapshotsAndGetJestSnapshotResult = (
|
||||
snapshotState: SnapshotState,
|
||||
testSnapshotResults: Array<TestSnapshotResults>,
|
||||
|
||||
+24
@@ -528,6 +528,30 @@ class Expect {
|
||||
}
|
||||
}
|
||||
|
||||
toMatchInlineSnapshot(expected?: string): void {
|
||||
if (this.#isNot) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
'Snapshot matchers cannot be used with not.',
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
|
||||
const receivedValue = format(this.#received, {
|
||||
plugins: [plugins.ReactElement],
|
||||
});
|
||||
|
||||
const stackTrace = new Error().stack ?? '';
|
||||
|
||||
try {
|
||||
snapshotContext.toMatchInlineSnapshot(
|
||||
receivedValue,
|
||||
expected,
|
||||
stackTrace,
|
||||
);
|
||||
} catch (err) {
|
||||
throw new ErrorWithCustomBlame(err.message).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
#isExpectedResult(pass: boolean): boolean {
|
||||
return this.#isNot ? !pass : pass;
|
||||
}
|
||||
|
||||
+8
-1
@@ -10,7 +10,11 @@
|
||||
|
||||
import type {CoverageMap} from '../runner/coverage/types.flow';
|
||||
import type {BenchmarkResult} from '../src/Benchmark';
|
||||
import type {SnapshotConfig, TestSnapshotResults} from './snapshotContext';
|
||||
import type {
|
||||
SnapshotConfig,
|
||||
TestInlineSnapshotResults,
|
||||
TestSnapshotResults,
|
||||
} from './snapshotContext';
|
||||
|
||||
import {getConstants} from '../src/Constants';
|
||||
import expect from './expect';
|
||||
@@ -28,6 +32,7 @@ export type TestCaseResult = {
|
||||
failureDetails: Array<FailureDetail>,
|
||||
numPassingAsserts: number,
|
||||
snapshotResults: TestSnapshotResults,
|
||||
inlineSnapshotResults: TestInlineSnapshotResults,
|
||||
// location: string,
|
||||
};
|
||||
|
||||
@@ -317,6 +322,7 @@ function runSpec(spec: Spec): TestCaseResult {
|
||||
failureDetails: [],
|
||||
numPassingAsserts: 0,
|
||||
snapshotResults: {},
|
||||
inlineSnapshotResults: [],
|
||||
};
|
||||
|
||||
if (!shouldRunSuite(spec)) {
|
||||
@@ -356,6 +362,7 @@ function runSpec(spec: Spec): TestCaseResult {
|
||||
}
|
||||
|
||||
result.snapshotResults = snapshotContext.getSnapshotResults();
|
||||
result.inlineSnapshotResults = snapshotContext.getInlineSnapshotResults();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,14 +26,60 @@ export type TestSnapshotResults = {
|
||||
},
|
||||
};
|
||||
|
||||
export type InlineSnapshotResult =
|
||||
| {pass: true}
|
||||
| {pass: false, isNew: boolean, value: string, stackTrace: string};
|
||||
|
||||
export type TestInlineSnapshotResults = Array<InlineSnapshotResult>;
|
||||
|
||||
const COMPARISON_EQUALS_STRING = 'Compared values have no visual difference.';
|
||||
|
||||
const INDENTATION_REGEX: RegExp = /^([^\S\n]*)\S/m;
|
||||
|
||||
let snapshotConfig: ?SnapshotConfig;
|
||||
|
||||
// Add extra line breaks at beginning and end of multiline snapshot
|
||||
// to make the content easier to read.
|
||||
const addExtraLineBreaks = (string: string): string =>
|
||||
string.includes('\n') ? `\n${string}\n` : string;
|
||||
|
||||
// Strips source-code indentation from inline snapshot template literals.
|
||||
// Matches jest-snapshot's stripAddedIndentation implementation.
|
||||
function stripAddedIndentation(inlineSnapshot: string): string {
|
||||
const match = inlineSnapshot.match(INDENTATION_REGEX);
|
||||
if (!match || !match[1]) {
|
||||
return inlineSnapshot;
|
||||
}
|
||||
|
||||
const indentation = match[1];
|
||||
const lines = inlineSnapshot.split('\n');
|
||||
|
||||
if (lines.length <= 2) {
|
||||
return inlineSnapshot;
|
||||
}
|
||||
|
||||
if (lines[0].trim() !== '' || lines[lines.length - 1].trim() !== '') {
|
||||
return inlineSnapshot;
|
||||
}
|
||||
|
||||
for (let i = 1; i < lines.length - 1; i++) {
|
||||
if (lines[i] !== '') {
|
||||
if (lines[i].indexOf(indentation) !== 0) {
|
||||
return inlineSnapshot;
|
||||
}
|
||||
lines[i] = lines[i].substring(indentation.length);
|
||||
}
|
||||
}
|
||||
|
||||
lines[lines.length - 1] = '';
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
type SnapshotState = {
|
||||
callCount: number,
|
||||
testFullName: string,
|
||||
snapshotResults: TestSnapshotResults,
|
||||
inlineSnapshotResults: TestInlineSnapshotResults,
|
||||
};
|
||||
|
||||
class SnapshotContext {
|
||||
@@ -44,6 +90,7 @@ class SnapshotContext {
|
||||
callCount: 0,
|
||||
testFullName,
|
||||
snapshotResults: {},
|
||||
inlineSnapshotResults: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -100,9 +147,74 @@ class SnapshotContext {
|
||||
snapshotState.snapshotResults[snapshotKey] = {pass: true};
|
||||
}
|
||||
|
||||
toMatchInlineSnapshot(
|
||||
received: string,
|
||||
inlineSnapshot: string | void,
|
||||
stackTrace: string,
|
||||
): void {
|
||||
const snapshotState = this.#snapshotState;
|
||||
if (snapshotState == null) {
|
||||
throw new Error(
|
||||
'Snapshot state is not set, call `setTargetTest()` first',
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshotConfig == null) {
|
||||
throw new Error(
|
||||
'Snapshot config is not set. Did you forget to call `setupSnapshotConfig`?',
|
||||
);
|
||||
}
|
||||
|
||||
const updateSnapshot = snapshotConfig.updateSnapshot;
|
||||
const receivedSerialized = addExtraLineBreaks(received);
|
||||
|
||||
if (inlineSnapshot === undefined) {
|
||||
// First run - no inline snapshot yet
|
||||
snapshotState.inlineSnapshotResults.push({
|
||||
pass: false,
|
||||
isNew: true,
|
||||
value: receivedSerialized,
|
||||
stackTrace,
|
||||
});
|
||||
|
||||
if (updateSnapshot === 'none') {
|
||||
throw new Error(
|
||||
'New inline snapshot was not written. The update flag must be explicitly passed to write a new snapshot.\n\nThis is likely because this test is run in a continuous integration (CI) environment in which snapshots are not written by default.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const expected = stripAddedIndentation(inlineSnapshot);
|
||||
|
||||
if (receivedSerialized === expected) {
|
||||
snapshotState.inlineSnapshotResults.push({pass: true});
|
||||
return;
|
||||
}
|
||||
|
||||
// Mismatch
|
||||
snapshotState.inlineSnapshotResults.push({
|
||||
pass: false,
|
||||
isNew: false,
|
||||
value: receivedSerialized,
|
||||
stackTrace,
|
||||
});
|
||||
|
||||
if (updateSnapshot !== 'all') {
|
||||
const diffResult =
|
||||
diff(expected, receivedSerialized) ?? 'Failed to compare output';
|
||||
throw new Error(`Expected to match inline snapshot.\n${diffResult}`);
|
||||
}
|
||||
}
|
||||
|
||||
getSnapshotResults(): TestSnapshotResults {
|
||||
return {...this.#snapshotState?.snapshotResults};
|
||||
}
|
||||
|
||||
getInlineSnapshotResults(): TestInlineSnapshotResults {
|
||||
return [...(this.#snapshotState?.inlineSnapshotResults ?? [])];
|
||||
}
|
||||
}
|
||||
|
||||
export const snapshotContext: SnapshotContext = new SnapshotContext();
|
||||
|
||||
@@ -865,4 +865,62 @@ describe('expect', () => {
|
||||
expect({a: 'b'}).toMatchSnapshot('named snapshot');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toMatchInlineSnapshot()', () => {
|
||||
test('primitive types', () => {
|
||||
expect(42).toMatchInlineSnapshot(`42`);
|
||||
expect('hello').toMatchInlineSnapshot(`"hello"`);
|
||||
expect(true).toMatchInlineSnapshot(`true`);
|
||||
expect(null).toMatchInlineSnapshot(`null`);
|
||||
expect(undefined).toMatchInlineSnapshot(`undefined`);
|
||||
expect('foo\nbar').toMatchInlineSnapshot(`
|
||||
"foo
|
||||
bar"
|
||||
`);
|
||||
});
|
||||
|
||||
test('complex types', () => {
|
||||
expect({foo: 'bar'}).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"foo": "bar",
|
||||
}
|
||||
`);
|
||||
expect(new Map([['foo', 'bar']])).toMatchInlineSnapshot(`
|
||||
Map {
|
||||
"foo" => "bar",
|
||||
}
|
||||
`);
|
||||
expect(new Set([1, 2])).toMatchInlineSnapshot(`
|
||||
Set {
|
||||
1,
|
||||
2,
|
||||
}
|
||||
`);
|
||||
expect(new Date('2025-01-02')).toMatchInlineSnapshot(
|
||||
`2025-01-02T00:00:00.000Z`,
|
||||
);
|
||||
expect(new Error()).toMatchInlineSnapshot(`[Error]`);
|
||||
expect(new RegExp('asd')).toMatchInlineSnapshot(`/asd/`);
|
||||
expect(function foo() {}).toMatchInlineSnapshot(`[Function foo]`);
|
||||
});
|
||||
|
||||
test('JSX elements', () => {
|
||||
expect(<span>hello</span>).toMatchInlineSnapshot(`
|
||||
<span>
|
||||
hello
|
||||
</span>
|
||||
`);
|
||||
expect(
|
||||
<div>
|
||||
<span>nested</span>
|
||||
</div>,
|
||||
).toMatchInlineSnapshot(`
|
||||
<div>
|
||||
<span>
|
||||
nested
|
||||
</span>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user