Skip to content

Commit 7c2f708

Browse files
fenghan34sheremet-va
andauthoredMay 25, 2023
feat: throw error if using inline snapshot inside of test.each or describe.each (#3360)
Co-authored-by: Vladimir <sleuths.slews0s@icloud.com>
1 parent b72ebdb commit 7c2f708

File tree

6 files changed

+62
-7
lines changed

6 files changed

+62
-7
lines changed
 

‎packages/runner/src/suite.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function createSuiteHooks() {
5353
}
5454

5555
// implementations
56-
function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, mode: RunMode, concurrent?: boolean, shuffle?: boolean, suiteOptions?: TestOptions) {
56+
function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, mode: RunMode, concurrent?: boolean, shuffle?: boolean, each?: boolean, suiteOptions?: TestOptions) {
5757
const tasks: (Test | TaskCustom | Suite | SuiteCollector)[] = []
5858
const factoryQueue: (Test | Suite | SuiteCollector)[] = []
5959

@@ -80,6 +80,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
8080
id: '',
8181
type: 'test',
8282
name,
83+
each: this.each,
8384
mode,
8485
suite: undefined!,
8586
fails: this.fails,
@@ -145,6 +146,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
145146
type: 'suite',
146147
name,
147148
mode,
149+
each,
148150
shuffle,
149151
tasks: [],
150152
}
@@ -198,11 +200,12 @@ function createSuite() {
198200
options = { repeats: currentSuite.options.repeats, ...options }
199201
}
200202

201-
return createSuiteCollector(name, factory, mode, this.concurrent, this.shuffle, options)
203+
return createSuiteCollector(name, factory, mode, this.concurrent, this.shuffle, this.each, options)
202204
}
203205

204-
suiteFn.each = function<T>(this: { withContext: () => SuiteAPI }, cases: ReadonlyArray<T>, ...args: any[]) {
206+
suiteFn.each = function<T>(this: { withContext: () => SuiteAPI; setContext: (key: string, value: boolean | undefined) => SuiteAPI }, cases: ReadonlyArray<T>, ...args: any[]) {
205207
const suite = this.withContext()
208+
this.setContext('each', true)
206209

207210
if (Array.isArray(cases) && args.length)
208211
cases = formatTemplateString(cases, args)
@@ -215,6 +218,8 @@ function createSuite() {
215218
? suite(formatTitle(name, items, idx), () => fn(...items), options)
216219
: suite(formatTitle(name, items, idx), () => fn(i), options)
217220
})
221+
222+
this.setContext('each', undefined)
218223
}
219224
}
220225

@@ -229,16 +234,17 @@ function createSuite() {
229234

230235
function createTest(fn: (
231236
(
232-
this: Record<'concurrent' | 'skip' | 'only' | 'todo' | 'fails', boolean | undefined>,
237+
this: Record<'concurrent' | 'skip' | 'only' | 'todo' | 'fails' | 'each', boolean | undefined>,
233238
title: string,
234239
fn?: TestFunction,
235240
options?: number | TestOptions
236241
) => void
237242
)) {
238243
const testFn = fn as any
239244

240-
testFn.each = function<T>(this: { withContext: () => TestAPI }, cases: ReadonlyArray<T>, ...args: any[]) {
245+
testFn.each = function<T>(this: { withContext: () => SuiteAPI; setContext: (key: string, value: boolean | undefined) => SuiteAPI }, cases: ReadonlyArray<T>, ...args: any[]) {
241246
const test = this.withContext()
247+
this.setContext('each', true)
242248

243249
if (Array.isArray(cases) && args.length)
244250
cases = formatTemplateString(cases, args)
@@ -252,6 +258,8 @@ function createTest(fn: (
252258
? test(formatTitle(name, items, idx), () => fn(...items), options)
253259
: test(formatTitle(name, items, idx), () => fn(i), options)
254260
})
261+
262+
this.setContext('each', undefined)
255263
}
256264
}
257265

‎packages/runner/src/types/tasks.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface TaskBase {
99
id: string
1010
name: string
1111
mode: RunMode
12+
each?: boolean
1213
concurrent?: boolean
1314
shuffle?: boolean
1415
suite?: Suite

‎packages/runner/src/utils/chain.ts

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export function createChainable<T extends string, Args extends any[], R = any, E
1616
}
1717
Object.assign(chain, fn)
1818
chain.withContext = () => chain.bind(context)
19+
chain.setContext = (key: T, value: boolean | undefined) => {
20+
context[key] = value
21+
}
1922
for (const key of keys) {
2023
Object.defineProperty(chain, key, {
2124
get() {

‎packages/vitest/src/integrations/snapshot/chai.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,12 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => {
101101
chai.Assertion.prototype,
102102
'toMatchInlineSnapshot',
103103
function __INLINE_SNAPSHOT__(this: Record<string, unknown>, properties?: object, inlineSnapshot?: string, message?: string) {
104+
const test = utils.flag(this, 'vitest-test')
105+
const isInsideEach = test && (test.each || test.suite?.each)
106+
if (isInsideEach)
107+
throw new Error('InlineSnapshot cannot be used inside of test.each or describe.each')
104108
const expected = utils.flag(this, 'object')
105109
const error = utils.flag(this, 'error')
106-
const test = utils.flag(this, 'vitest-test')
107110
if (typeof properties === 'string') {
108111
message = inlineSnapshot
109112
inlineSnapshot = properties
@@ -112,6 +115,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => {
112115
if (inlineSnapshot)
113116
inlineSnapshot = stripSnapshotIndentation(inlineSnapshot)
114117
const errorMessage = utils.flag(this, 'message')
118+
115119
getSnapshotClient().assert({
116120
received: expected,
117121
message,
@@ -144,11 +148,15 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => {
144148
chai.Assertion.prototype,
145149
'toThrowErrorMatchingInlineSnapshot',
146150
function __INLINE_SNAPSHOT__(this: Record<string, unknown>, inlineSnapshot: string, message: string) {
151+
const test = utils.flag(this, 'vitest-test')
152+
const isInsideEach = test && (test.each || test.suite?.each)
153+
if (isInsideEach)
154+
throw new Error('InlineSnapshot cannot be used inside of test.each or describe.each')
147155
const expected = utils.flag(this, 'object')
148156
const error = utils.flag(this, 'error')
149-
const test = utils.flag(this, 'vitest-test')
150157
const promise = utils.flag(this, 'promise') as string | undefined
151158
const errorMessage = utils.flag(this, 'message')
159+
152160
getSnapshotClient().assert({
153161
received: getErrorString(expected, promise),
154162
message,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { describe, expect, test } from 'vitest'
2+
3+
test.each([1])('', () => {
4+
expect('').toMatchInlineSnapshot()
5+
})
6+
7+
describe.each([1])('', () => {
8+
test('', () => {
9+
expect('').toMatchInlineSnapshot()
10+
})
11+
12+
test.each([1])('', () => {
13+
expect('').toMatchInlineSnapshot()
14+
})
15+
16+
test('', () => {
17+
expect(() => {
18+
throw new Error('1')
19+
}).toThrowErrorMatchingInlineSnapshot()
20+
})
21+
22+
test.each([1])('', () => {
23+
expect(() => {
24+
throw new Error('1')
25+
}).toThrowErrorMatchingInlineSnapshot()
26+
})
27+
})

‎test/fails/test/__snapshots__/runner.test.ts.snap

+8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ exports[`should fails > hooks-called.test.ts > hooks-called.test.ts 1`] = `
1515
Error: before all"
1616
`;
1717
18+
exports[`should fails > inline-snapshop-inside-each.test.ts > inline-snapshop-inside-each.test.ts 1`] = `
19+
"Error: InlineSnapshot cannot be used inside of test.each or describe.each
20+
Error: InlineSnapshot cannot be used inside of test.each or describe.each
21+
Error: InlineSnapshot cannot be used inside of test.each or describe.each
22+
Error: InlineSnapshot cannot be used inside of test.each or describe.each
23+
Error: InlineSnapshot cannot be used inside of test.each or describe.each"
24+
`;
25+
1826
exports[`should fails > mock-import-proxy-module.test.ts > mock-import-proxy-module.test.ts 1`] = `"Error: There are some problems in resolving the mocks API."`;
1927
2028
exports[`should fails > nested-suite.test.ts > nested-suite.test.ts 1`] = `"AssertionError: expected true to be false // Object.is equality"`;

0 commit comments

Comments
 (0)
Please sign in to comment.