Skip to content

Commit 4a73870

Browse files
committed
feat: add metadata and multiple attachments to annotations
1 parent ed9fc71 commit 4a73870

File tree

3 files changed

+63
-38
lines changed

3 files changed

+63
-38
lines changed

packages/runner/src/context.ts

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import type {
66
SuiteCollector,
77
Test,
88
TestAnnotation,
9-
TestAnnotationLocation,
10-
TestAttachment,
119
TestContext,
1210
WriteableTestContext,
1311
} from './types/tasks'
@@ -157,33 +155,47 @@ export function createTestContext(
157155
)
158156
}
159157

160-
async function annotate(
161-
message: string,
162-
location?: TestAnnotationLocation,
163-
type?: string,
164-
attachment?: TestAttachment,
165-
) {
166-
const annotation: TestAnnotation = {
167-
message,
168-
type: type || 'notice',
158+
function createAnnotation(annotation: TestAnnotation): TestAnnotation {
159+
const stack = findTestFileStackTrace(
160+
test.file.filepath,
161+
new Error('STACK_TRACE').stack!,
162+
)
163+
164+
if (stack) {
165+
annotation.location = {
166+
file: stack.file,
167+
line: stack.line,
168+
column: stack.column,
169+
}
169170
}
170-
if (attachment) {
171+
172+
if (annotation.attachment !== undefined && annotation.attachments !== undefined) {
173+
throw new TypeError('A test annotation cannot have both "attachment" and "attachments"')
174+
}
175+
176+
const attachments = annotation.attachments ?? (annotation.attachment !== undefined ? [annotation.attachment] : [])
177+
178+
for (const attachment of attachments) {
171179
if (attachment.body == null && !attachment.path) {
172180
throw new TypeError(`Test attachment requires "body" or "path" to be set. Both are missing.`)
173181
}
182+
174183
if (attachment.body && attachment.path) {
175184
throw new TypeError(`Test attachment requires only one of "body" or "path" to be set. Both are specified.`)
176185
}
177-
annotation.attachment = attachment
186+
178187
// convert to a string so it's easier to serialise
179188
if (attachment.body instanceof Uint8Array) {
180189
attachment.body = encodeUint8Array(attachment.body)
181190
}
182191
}
183-
if (location) {
184-
annotation.location = location
185-
}
186192

193+
return annotation
194+
}
195+
196+
async function annotate(
197+
annotation: TestAnnotation,
198+
) {
187199
if (!runner.onTestAnnotate) {
188200
throw new Error(`Test runner doesn't support test annotations.`)
189201
}
@@ -200,33 +212,29 @@ export function createTestContext(
200212
throw new Error(`Cannot annotate tests outside of the test run. The test "${test.name}" finished running with the "${test.result.state}" state already.`)
201213
}
202214

203-
const stack = findTestFileStackTrace(
204-
test.file.filepath,
205-
new Error('STACK_TRACE').stack!,
206-
)
207-
208-
let location: undefined | TestAnnotationLocation
215+
if (typeof message === 'object') {
216+
const annotation = createAnnotation(message)
209217

210-
if (stack) {
211-
location = {
212-
file: stack.file,
213-
line: stack.line,
214-
column: stack.column,
215-
}
218+
return recordAsyncAnnotation(test, annotate(annotation))
216219
}
217220

218221
if (typeof type === 'object') {
219-
return recordAsyncAnnotation(
220-
test,
221-
annotate(message, location, undefined, type),
222-
)
223-
}
224-
else {
225-
return recordAsyncAnnotation(
226-
test,
227-
annotate(message, location, type, attachment),
228-
)
222+
const annotation = createAnnotation({
223+
type: 'notice',
224+
message,
225+
attachment: type,
226+
})
227+
228+
return recordAsyncAnnotation(test, annotate(annotation))
229229
}
230+
231+
const annotation = createAnnotation({
232+
type: type ?? 'notice',
233+
message,
234+
attachment,
235+
})
236+
237+
return recordAsyncAnnotation(test, annotate(annotation))
230238
}) as TestContext['annotate']
231239

232240
context.onTestFailed = (handler, timeout) => {

packages/runner/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export type {
4343
Test,
4444
TestAnnotation,
4545
TestAnnotationLocation,
46+
TestAnnotationMetadata,
4647
TestAPI,
4748
TestAttachment,
4849
TestContext,

packages/runner/src/types/tasks.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,11 @@ export interface Test<ExtraContext = object> extends TaskPopulated {
286286
}
287287

288288
export interface TestAttachment {
289+
name?: string
289290
contentType?: string
290291
path?: string
291292
body?: string | Uint8Array
293+
metadata?: Record<string, unknown>
292294
}
293295

294296
export interface TestAnnotationLocation {
@@ -297,11 +299,23 @@ export interface TestAnnotationLocation {
297299
file: string
298300
}
299301

302+
export interface TestAnnotationMetadata {
303+
'internal:toMatchScreenshot'?: {
304+
kind: string
305+
}
306+
[key: string]: unknown
307+
}
308+
300309
export interface TestAnnotation {
310+
title?: string
301311
message: string
302312
type: string
303313
location?: TestAnnotationLocation
304314
attachment?: TestAttachment
315+
/** @experimental */
316+
attachments?: TestAttachment[]
317+
/** @experimental */
318+
metadata?: TestAnnotationMetadata
305319
}
306320

307321
export type Task = Test | Suite | File
@@ -703,6 +717,8 @@ export interface TestContext {
703717
* @see {@link https://vitest.dev/guide/test-context#annotate}
704718
*/
705719
readonly annotate: {
720+
/** @experimental */
721+
(annotation: Omit<TestAnnotation, 'location'>): Promise<TestAnnotation>
706722
(message: string, type?: string, attachment?: TestAttachment): Promise<TestAnnotation>
707723
(message: string, attachment?: TestAttachment): Promise<TestAnnotation>
708724
}

0 commit comments

Comments
 (0)