-
-
Notifications
You must be signed in to change notification settings - Fork 53
/
Copy pathsnapshot.ts
162 lines (148 loc) · 5.23 KB
/
snapshot.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import path from 'node:path'
import type { AssertionError } from 'node:assert'
import { expect } from '../index.js'
import { SnapshotService } from '../snapshot.js'
interface InlineSnapshotOptions {
inlineSnapshot: string
error: Error
}
/**
* Vitest snapshot client returns a snapshot error with an `actual` and `expected`
* property containing strings of the compared snapshots. In case these don't match
* we use this helper method to return a proper assertion message that contains
* nice color highlighting etc. For that we just re-assert the two strings.
* @param snapshotError error message from snapshot client
* @returns matcher result
*/
function returnSnapshotError (snapshotError: AssertionError) {
/**
* wrap into another try catch block so we can get a better
* assertion message
*/
try {
expect(snapshotError.actual).toBe(snapshotError.expected)
} catch (e) {
return {
pass: false,
message: () => (e as Error).message
}
}
/**
* this should never happen but in case it does we want to
*/
throw snapshotError
}
/**
* Helper method to assert snapshots
* @param received element to snapshot
* @param message optional message on failure
* @returns matcher results
*/
function toMatchSnapshotAssert (received: unknown, message: string, inlineOptions?: InlineSnapshotOptions) {
const snapshotService = SnapshotService.initiate()
try {
snapshotService.client.assert({
received,
message,
filepath: snapshotService.currentFilePath,
name: snapshotService.currentTestName,
/**
* apply inline options if needed
*/
...(inlineOptions ? {
...inlineOptions,
isInline: true
} : {
isInline: false
})
})
return {
pass: true,
message: () => 'Snapshot matches'
}
} catch (e: unknown) {
return returnSnapshotError(e as AssertionError)
}
}
/**
* Asynchronous version of `toMatchSnapshot` that works with WebdriverIO elements.
* @param elem a WebdriverIO element
* @param message optional message on failure
* @returns matcher results
*/
async function toMatchSnapshotAsync (asyncReceived: unknown, message: string, inlineOptions?: InlineSnapshotOptions) {
let received: WebdriverIO.Element | unknown = await asyncReceived
if (received && typeof received === 'object' && 'elementId' in received) {
received = await (received as WebdriverIO.Element).getHTML({
includeSelectorTag: true
})
}
return toMatchSnapshotAssert(received, message, inlineOptions)
}
/**
* We want to keep this method synchronous so that doing snapshots for basic
* elements doesn't require an `await` and matches other framework behavior.
* @param received element to snapshot
* @param message optional message on failure
* @returns matcher results
*/
function toMatchSnapshotHelper(received: unknown, message: string, inlineOptions?: InlineSnapshotOptions) {
const snapshotService = SnapshotService.initiate()
if (!snapshotService.currentFilePath || !snapshotService.currentTestName) {
throw new Error('Snapshot service is not initialized')
}
/**
* allow to match DOM snapshots
*/
if (
received && typeof received === 'object' &&
(
'elementId' in received ||
'then' in received
)
) {
return toMatchSnapshotAsync(received, message, inlineOptions)
}
return toMatchSnapshotAssert(received, message, inlineOptions)
}
export function toMatchSnapshot(received: unknown, message: string) {
return toMatchSnapshotHelper(received, message)
}
export function toMatchInlineSnapshot(received: unknown, inlineSnapshot: string, message: string) {
/**
* When running component/unit tests in the browser we receive a stack trace
* through the `this` scope.
*/
const browserErrorLine: string = this.errorStack
function __INLINE_SNAPSHOT__(inlineSnapshot: string, message: string) {
/**
* create a error object to pass along that helps Vitest's snapshot manager
* to infer the stack trace and locate the inline snapshot
*/
const error = new Error('inline snapshot')
/**
* merge stack traces from browser and node and push the error of the test
* into the stack trace
*/
if (browserErrorLine && error.stack) {
const stack = error.stack.split('\n')
error.stack = [
...stack.slice(0, 4),
browserErrorLine,
...stack.slice(3)
].join('\n')
}
error.stack = error.stack?.split('\n').filter((line) => (
line.includes('__INLINE_SNAPSHOT__') ||
!(
line.includes('__EXTERNAL_MATCHER_TRAP__') ||
line.includes(`expect-webdriverio${path.sep}lib${path.sep}matchers${path.sep}snapshot.js:`)
)
)).join('\n')
return toMatchSnapshotHelper(received, message, {
inlineSnapshot,
error
})
}
return __INLINE_SNAPSHOT__(inlineSnapshot, message)
}