vitest-plugin-vis 2.4.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/esm/client/actions/has_image_snapshot_action.d.ts +4 -0
  2. package/esm/client/actions/has_image_snapshot_action.d.ts.map +1 -0
  3. package/esm/client/actions/has_image_snapshot_action.js +6 -0
  4. package/esm/client/actions/load_image_snapshot_results_action.d.ts +3 -0
  5. package/esm/client/actions/load_image_snapshot_results_action.d.ts.map +1 -0
  6. package/esm/client/actions/load_image_snapshot_results_action.js +3 -0
  7. package/esm/client/actions/match_image_snapshot_action.d.ts +6 -0
  8. package/esm/client/actions/match_image_snapshot_action.d.ts.map +1 -0
  9. package/esm/client/actions/match_image_snapshot_action.js +20 -0
  10. package/esm/client/actions/match_page_image_snapshot_action.d.ts +6 -0
  11. package/esm/client/actions/match_page_image_snapshot_action.d.ts.map +1 -0
  12. package/esm/client/actions/match_page_image_snapshot_action.js +9 -0
  13. package/esm/client/auto_snapshot_options.d.ts +29 -0
  14. package/esm/client/auto_snapshot_options.d.ts.map +1 -0
  15. package/esm/client/auto_snapshot_options.js +49 -0
  16. package/esm/client/compare_image_snapshot.d.ts +6 -0
  17. package/esm/client/compare_image_snapshot.d.ts.map +1 -0
  18. package/esm/client/compare_image_snapshot.js +52 -0
  19. package/esm/client/{image_snapshot_matcher.logic.d.ts → compare_image_snapshot.logic.d.ts} +1 -1
  20. package/esm/client/compare_image_snapshot.logic.d.ts.map +1 -0
  21. package/esm/client/expect/to_match_image_snapshot.d.ts.map +1 -1
  22. package/esm/client/expect/to_match_image_snapshot.js +4 -3
  23. package/esm/client/image_resizer.d.ts.map +1 -1
  24. package/esm/client/image_snapshot_options.d.ts +2 -0
  25. package/esm/client/image_snapshot_options.d.ts.map +1 -0
  26. package/esm/client/image_snapshot_options.js +6 -0
  27. package/esm/client/page/augment.d.ts +2 -1
  28. package/esm/client/page/augment.d.ts.map +1 -1
  29. package/esm/client/page/extend.js +2 -1
  30. package/esm/client/page/has_image_snapshot.d.ts +6 -6
  31. package/esm/client/page/has_image_snapshot.d.ts.map +1 -1
  32. package/esm/client/page/has_image_snapshot.js +2 -11
  33. package/esm/client/page/to_match_image_snapshot.d.ts +8 -0
  34. package/esm/client/page/to_match_image_snapshot.d.ts.map +1 -0
  35. package/esm/client/page/to_match_image_snapshot.js +15 -0
  36. package/esm/client/setup/create_vis.d.ts +96 -0
  37. package/esm/client/setup/create_vis.d.ts.map +1 -0
  38. package/esm/{setup → client/setup}/create_vis.js +43 -31
  39. package/esm/client/setup/vis.ctx.d.ts +12 -0
  40. package/esm/client/setup/vis.ctx.d.ts.map +1 -0
  41. package/esm/client/setup/vis.ctx.js +32 -0
  42. package/esm/client/setup/vis.d.ts +5 -0
  43. package/esm/client/setup/vis.d.ts.map +1 -0
  44. package/esm/client/should_take_snapshot.d.ts +2 -9
  45. package/esm/client/should_take_snapshot.d.ts.map +1 -1
  46. package/esm/client/should_take_snapshot.js +1 -11
  47. package/esm/client/snapshot_meta.d.ts +6 -0
  48. package/esm/client/snapshot_meta.d.ts.map +1 -0
  49. package/esm/client/task_id.d.ts +0 -3
  50. package/esm/client/task_id.d.ts.map +1 -1
  51. package/esm/client/task_id.js +0 -3
  52. package/esm/client/vitest_suite_proxy.d.ts +2 -9
  53. package/esm/client/vitest_suite_proxy.d.ts.map +1 -1
  54. package/esm/client-api.d.ts +11 -0
  55. package/esm/client-api.d.ts.map +1 -0
  56. package/esm/client-api.js +8 -0
  57. package/esm/commands.d.ts +0 -1
  58. package/esm/commands.d.ts.map +1 -1
  59. package/esm/commands.js +0 -1
  60. package/esm/config/types.d.ts +22 -22
  61. package/esm/config/types.d.ts.map +1 -1
  62. package/esm/config/vis.d.ts +4 -3
  63. package/esm/config/vis.d.ts.map +1 -1
  64. package/esm/config/vis.js +3 -4
  65. package/esm/index.d.ts +1 -1
  66. package/esm/index.d.ts.map +1 -1
  67. package/esm/index.js +1 -1
  68. package/esm/presets/auto.d.ts +1 -2
  69. package/esm/presets/auto.d.ts.map +1 -1
  70. package/esm/presets/auto.js +8 -4
  71. package/esm/presets/enable.d.ts +1 -2
  72. package/esm/presets/enable.d.ts.map +1 -1
  73. package/esm/presets/enable.js +4 -4
  74. package/esm/presets/manual.d.ts +1 -2
  75. package/esm/presets/manual.d.ts.map +1 -1
  76. package/esm/presets/manual.js +3 -4
  77. package/esm/server/browser_provider/playwright.d.ts.map +1 -1
  78. package/esm/server/browser_provider/playwright.js +9 -3
  79. package/esm/server/browser_provider/types.d.ts +5 -1
  80. package/esm/server/browser_provider/types.d.ts.map +1 -1
  81. package/esm/server/browser_provider/webdriverio.d.ts.map +1 -1
  82. package/esm/server/browser_provider/webdriverio.js +7 -4
  83. package/esm/server/commands/augment.d.ts +11 -0
  84. package/esm/server/commands/augment.d.ts.map +1 -0
  85. package/esm/server/commands/augment.js +1 -0
  86. package/esm/server/commands/commands.d.ts +9 -0
  87. package/esm/server/commands/commands.d.ts.map +1 -0
  88. package/esm/server/commands/commands.js +14 -0
  89. package/esm/server/commands/has_image_snapshot.d.ts +2 -6
  90. package/esm/server/commands/has_image_snapshot.d.ts.map +1 -1
  91. package/esm/server/commands/has_image_snapshot.js +3 -3
  92. package/esm/server/commands/image_snapshot_next_index.d.ts +1 -1
  93. package/esm/server/commands/image_snapshot_next_index.d.ts.map +1 -1
  94. package/esm/server/commands/image_snapshot_next_index.js +2 -2
  95. package/esm/server/commands/load_image_snapshot_results.d.ts +21 -0
  96. package/esm/server/commands/load_image_snapshot_results.d.ts.map +1 -0
  97. package/esm/server/commands/load_image_snapshot_results.js +6 -0
  98. package/esm/server/commands/prepare_image_snapshot_comparison.d.ts +8 -10
  99. package/esm/server/commands/prepare_image_snapshot_comparison.d.ts.map +1 -1
  100. package/esm/server/commands/prepare_image_snapshot_comparison.js +11 -9
  101. package/esm/server/commands/prepare_page_image_snapshot_comparison.d.ts +35 -0
  102. package/esm/server/commands/prepare_page_image_snapshot_comparison.d.ts.map +1 -0
  103. package/esm/server/commands/prepare_page_image_snapshot_comparison.js +27 -0
  104. package/esm/server/commands/setup_vis_suite.d.ts +3 -3
  105. package/esm/server/commands/setup_vis_suite.d.ts.map +1 -1
  106. package/esm/server/commands/setup_vis_suite.js +2 -3
  107. package/esm/server/commands/types.d.ts +3 -0
  108. package/esm/server/commands/types.d.ts.map +1 -0
  109. package/esm/server/commands/types.js +1 -0
  110. package/esm/server/externals/file.d.ts +10 -0
  111. package/esm/server/externals/file.d.ts.map +1 -0
  112. package/esm/server/{file.js → externals/file.js} +12 -4
  113. package/esm/server/{commands/browser_command_context.d.ts → project.d.ts} +8 -3
  114. package/esm/server/project.d.ts.map +1 -0
  115. package/esm/server/{commands/browser_command_context.js → project.js} +2 -2
  116. package/esm/server/snapshot.d.ts +4 -5
  117. package/esm/server/snapshot.d.ts.map +1 -1
  118. package/esm/server/snapshot.js +15 -13
  119. package/esm/server/snapshot_path.d.ts +3 -3
  120. package/esm/server/snapshot_path.d.ts.map +1 -1
  121. package/esm/server/snapshot_path.js +12 -11
  122. package/esm/server/snapshot_writer.d.ts +13 -0
  123. package/esm/server/snapshot_writer.d.ts.map +1 -0
  124. package/esm/server/snapshot_writer.js +15 -0
  125. package/esm/server/suite.d.ts +53 -0
  126. package/esm/server/suite.d.ts.map +1 -0
  127. package/esm/server/suite.js +73 -0
  128. package/esm/server/vis_options.d.ts +18 -0
  129. package/esm/server/vis_options.d.ts.map +1 -0
  130. package/esm/server/vis_options.js +14 -0
  131. package/esm/server/vis_server_context.d.ts +50 -0
  132. package/esm/server/vis_server_context.d.ts.map +1 -0
  133. package/esm/server/{vis_context.ctx.d.ts → vis_server_context.deps.d.ts} +2 -2
  134. package/esm/server/vis_server_context.deps.d.ts.map +1 -0
  135. package/esm/server/{vis_context.ctx.js → vis_server_context.deps.js} +1 -1
  136. package/esm/server/vis_server_context.js +2 -0
  137. package/esm/server/{vis_context.logic.d.ts → vis_server_context.logic.d.ts} +12 -32
  138. package/esm/server/vis_server_context.logic.d.ts.map +1 -0
  139. package/esm/server/vis_server_context.logic.js +79 -0
  140. package/esm/server/{vis_context.types.d.ts → vis_server_context.types.d.ts} +6 -6
  141. package/esm/server/vis_server_context.types.d.ts.map +1 -0
  142. package/esm/server/vis_server_context.types.js +1 -0
  143. package/esm/server-utils.d.ts +3 -0
  144. package/esm/server-utils.d.ts.map +1 -0
  145. package/esm/server-utils.js +2 -0
  146. package/esm/setup.d.ts +2 -2
  147. package/esm/setup.d.ts.map +1 -1
  148. package/esm/setup.js +2 -2
  149. package/esm/shared/asserts.d.ts +2 -0
  150. package/esm/shared/asserts.d.ts.map +1 -0
  151. package/esm/shared/asserts.js +5 -0
  152. package/esm/shared/types.d.ts +22 -21
  153. package/esm/shared/types.d.ts.map +1 -1
  154. package/esm/testing/stubBrowserCommandContext.d.ts +1 -1
  155. package/esm/testing/stubBrowserCommandContext.d.ts.map +1 -1
  156. package/esm/testing/stubBrowserCommandContext.js +3 -13
  157. package/esm/testing/stubSuite.d.ts +10 -0
  158. package/esm/testing/stubSuite.d.ts.map +1 -0
  159. package/esm/testing/stubSuite.js +27 -0
  160. package/esm/testing/stubUserConfig.d.ts +7 -0
  161. package/esm/testing/stubUserConfig.d.ts.map +1 -0
  162. package/esm/testing/stubUserConfig.js +4 -0
  163. package/package.json +29 -28
  164. package/readme.md +409 -110
  165. package/src/client/actions/has_image_snapshot_action.ts +14 -0
  166. package/src/client/actions/load_image_snapshot_results_action.ts +5 -0
  167. package/src/client/actions/match_image_snapshot_action.ts +32 -0
  168. package/src/client/actions/match_page_image_snapshot_action.ts +20 -0
  169. package/src/client/auto_snapshot_options.ts +71 -0
  170. package/src/client/compare_image_snapshot.ts +84 -0
  171. package/src/client/expect/to_match_image_snapshot.ts +4 -3
  172. package/src/client/image_snapshot_options.ts +6 -0
  173. package/src/client/page/augment.ts +2 -1
  174. package/src/client/page/extend.ts +2 -1
  175. package/src/client/page/has_image_snapshot.ts +8 -21
  176. package/src/client/page/to_match_image_snapshot.ts +27 -0
  177. package/src/client/setup/create_vis.ts +227 -0
  178. package/src/client/setup/vis.ctx.ts +33 -0
  179. package/src/client/should_take_snapshot.ts +3 -13
  180. package/src/client/snapshot_meta.ts +7 -0
  181. package/src/client/task_id.ts +0 -3
  182. package/src/client/vitest_suite_proxy.ts +2 -5
  183. package/src/client-api.ts +11 -0
  184. package/src/commands.ts +0 -1
  185. package/src/config/types.ts +32 -27
  186. package/src/config/vis.ts +3 -5
  187. package/src/index.ts +1 -1
  188. package/src/presets/auto.ts +8 -4
  189. package/src/presets/enable.ts +4 -4
  190. package/src/presets/manual.ts +3 -4
  191. package/src/server/browser_provider/playwright.ts +9 -3
  192. package/src/server/browser_provider/types.ts +12 -1
  193. package/src/server/browser_provider/webdriverio.ts +7 -4
  194. package/src/server/commands/augment.ts +16 -0
  195. package/src/server/commands/commands.ts +15 -0
  196. package/src/server/commands/has_image_snapshot.ts +8 -6
  197. package/src/server/commands/image_snapshot_next_index.ts +5 -3
  198. package/src/server/commands/load_image_snapshot_results.ts +32 -0
  199. package/src/server/commands/prepare_image_snapshot_comparison.ts +20 -14
  200. package/src/server/commands/prepare_page_image_snapshot_comparison.ts +75 -0
  201. package/src/server/commands/setup_vis_suite.ts +17 -7
  202. package/src/server/commands/types.ts +9 -0
  203. package/src/server/externals/file.ts +21 -0
  204. package/src/server/{commands/browser_command_context.ts → project.ts} +2 -4
  205. package/src/server/snapshot.ts +23 -14
  206. package/src/server/snapshot_path.ts +15 -13
  207. package/src/server/snapshot_writer.ts +22 -0
  208. package/src/server/suite.ts +96 -0
  209. package/src/server/vis_options.ts +27 -0
  210. package/src/server/{vis_context.ctx.ts → vis_server_context.deps.ts} +1 -1
  211. package/src/server/vis_server_context.logic.ts +112 -0
  212. package/src/server/vis_server_context.ts +3 -0
  213. package/src/server/{vis_context.types.ts → vis_server_context.types.ts} +5 -5
  214. package/src/server-utils.ts +2 -0
  215. package/src/setup.ts +2 -2
  216. package/src/shared/asserts.ts +5 -0
  217. package/src/shared/types.ts +25 -23
  218. package/src/testing/stubBrowserCommandContext.ts +4 -14
  219. package/src/testing/stubSuite.ts +38 -0
  220. package/src/testing/stubUserConfig.ts +6 -0
  221. package/esm/client/image_snapshot_matcher.d.ts +0 -11
  222. package/esm/client/image_snapshot_matcher.d.ts.map +0 -1
  223. package/esm/client/image_snapshot_matcher.js +0 -80
  224. package/esm/client/image_snapshot_matcher.logic.d.ts.map +0 -1
  225. package/esm/client/snapshot_options.d.ts +0 -33
  226. package/esm/client/snapshot_options.d.ts.map +0 -1
  227. package/esm/client/snapshot_options.js +0 -37
  228. package/esm/client.d.ts +0 -8
  229. package/esm/client.d.ts.map +0 -1
  230. package/esm/client.js +0 -5
  231. package/esm/server/commands/browser_command_context.d.ts.map +0 -1
  232. package/esm/server/commands/extend.d.ts +0 -17
  233. package/esm/server/commands/extend.d.ts.map +0 -1
  234. package/esm/server/commands/extend.js +0 -12
  235. package/esm/server/commands/match_image_snapshot.d.ts +0 -18
  236. package/esm/server/commands/match_image_snapshot.d.ts.map +0 -1
  237. package/esm/server/commands/match_image_snapshot.js +0 -81
  238. package/esm/server/file.d.ts +0 -7
  239. package/esm/server/file.d.ts.map +0 -1
  240. package/esm/server/vis_context.ctx.d.ts.map +0 -1
  241. package/esm/server/vis_context.d.ts +0 -56
  242. package/esm/server/vis_context.d.ts.map +0 -1
  243. package/esm/server/vis_context.js +0 -2
  244. package/esm/server/vis_context.logic.d.ts.map +0 -1
  245. package/esm/server/vis_context.logic.js +0 -135
  246. package/esm/server/vis_context.types.d.ts.map +0 -1
  247. package/esm/setup/create_vis.d.ts +0 -53
  248. package/esm/setup/create_vis.d.ts.map +0 -1
  249. package/esm/setup/vis.d.ts +0 -5
  250. package/esm/setup/vis.d.ts.map +0 -1
  251. package/src/client/image_snapshot_matcher.ts +0 -121
  252. package/src/client/snapshot_options.ts +0 -82
  253. package/src/client.ts +0 -7
  254. package/src/server/commands/extend.ts +0 -25
  255. package/src/server/commands/match_image_snapshot.ts +0 -128
  256. package/src/server/file.ts +0 -13
  257. package/src/server/vis_context.logic.ts +0 -198
  258. package/src/server/vis_context.ts +0 -3
  259. package/src/setup/create_vis.ts +0 -154
  260. /package/esm/client/{image_snapshot_matcher.logic.js → compare_image_snapshot.logic.js} +0 -0
  261. /package/esm/{setup → client/setup}/vis.js +0 -0
  262. /package/esm/{server/vis_context.types.js → client/snapshot_meta.js} +0 -0
  263. /package/src/client/{image_snapshot_matcher.logic.ts → compare_image_snapshot.logic.ts} +0 -0
  264. /package/src/{setup → client/setup}/vis.ts +0 -0
@@ -0,0 +1,32 @@
1
+ import type { BrowserCommands } from '@vitest/browser/context'
2
+ import type { ImageSnapshotNextIndexCommand } from '../../commands.ts'
3
+ import type { PrepareImageSnapshotComparisonCommand } from '../../server/commands/prepare_image_snapshot_comparison.ts'
4
+ import { assertSnapshotKeyWithoutDash } from '../../shared/asserts.ts'
5
+ import { isBase64String } from '../../shared/base64.ts'
6
+ import type { ToMatchImageSnapshotOptions } from '../../shared/types.ts'
7
+ import { compareImageSnapshot } from '../compare_image_snapshot.ts'
8
+ import { convertElementToCssSelector } from '../selector.ts'
9
+
10
+ export async function matchImageSnapshotAction(
11
+ commands: BrowserCommands & PrepareImageSnapshotComparisonCommand & ImageSnapshotNextIndexCommand,
12
+ taskId: string,
13
+ subject: any,
14
+ options?: ToMatchImageSnapshotOptions<any>,
15
+ ) {
16
+ assertSnapshotKeyWithoutDash(options?.snapshotKey)
17
+
18
+ const info = await commands.prepareImageSnapshotComparison(taskId, parseImageSnapshotSubject(subject), options)
19
+
20
+ if (!info) return
21
+
22
+ return compareImageSnapshot(commands, taskId, info, options)
23
+ }
24
+
25
+ function parseImageSnapshotSubject(subject: any) {
26
+ if (subject instanceof Element) return convertElementToCssSelector(subject)
27
+ if (subject?.['selector']) return subject['selector']
28
+ if (isBase64String(subject)) return subject
29
+ throw new Error(
30
+ `'toMatchImageSnapshot()' expects the subject to be an element, locator, or image encoded in base64 string, but got: ${subject}`,
31
+ )
32
+ }
@@ -0,0 +1,20 @@
1
+ import type { BrowserCommands } from '@vitest/browser/context'
2
+ import type { ImageSnapshotNextIndexCommand } from '../../commands.ts'
3
+ import type { PrepareImageSnapshotComparisonCommand } from '../../server/commands/prepare_image_snapshot_comparison.ts'
4
+ import { assertSnapshotKeyWithoutDash } from '../../shared/asserts.ts'
5
+ import type { ToMatchImageSnapshotOptions } from '../../shared/types.ts'
6
+ import { compareImageSnapshot } from '../compare_image_snapshot.ts'
7
+
8
+ export async function matchPageImageSnapshotAction(
9
+ commands: BrowserCommands & PrepareImageSnapshotComparisonCommand & ImageSnapshotNextIndexCommand,
10
+ taskId: string,
11
+ options?: ToMatchImageSnapshotOptions<any>,
12
+ ) {
13
+ assertSnapshotKeyWithoutDash(options?.snapshotKey)
14
+
15
+ const info = await commands.preparePageImageSnapshotComparison(taskId, options)
16
+
17
+ if (!info) return
18
+
19
+ return compareImageSnapshot(commands, taskId, info, options)
20
+ }
@@ -0,0 +1,71 @@
1
+ import { assertSnapshotKeyWithoutDash } from '../shared/asserts.ts'
2
+ import { NAME } from '../shared/constants.ts'
3
+ import type { ComparisonMethod } from '../shared/types.ts'
4
+ import { ctx } from './ctx.ts'
5
+ import type { SnapshotMeta } from './snapshot_meta.ts'
6
+
7
+ type Suite = { meta: Record<string, any>; suite?: Suite | undefined }
8
+
9
+ export type MetaTask =
10
+ | {
11
+ file?: { meta: Record<string, any> } | undefined
12
+ suite?: Suite | undefined
13
+ meta: Record<string, any>
14
+ }
15
+ | undefined
16
+
17
+ /**
18
+ * Set the snapshot options for auto snapshot.
19
+ *
20
+ * ```ts
21
+ * beforeAll(() => setAutoSnapshotOptions(...))
22
+ * beforeEach(() => setAutoSnapshotOptions(...))
23
+ *
24
+ * it('...', () => {
25
+ * setAutoSnapshotOptions(...)
26
+ * })
27
+ * ```
28
+ */
29
+ export function setAutoSnapshotOptions<M extends ComparisonMethod>(meta: SnapshotMeta<M> | boolean): void {
30
+ if (typeof meta === 'object') {
31
+ assertSnapshotKeyWithoutDash(meta.snapshotKey)
32
+ }
33
+
34
+ const task = getTask()
35
+ if (!task) return
36
+
37
+ task.meta[NAME] = {
38
+ ...task.meta[NAME],
39
+ ...parseMeta(meta),
40
+ }
41
+ }
42
+
43
+ function getTask(): MetaTask | undefined {
44
+ return ctx.getCurrentTest() ?? (ctx.getCurrentSuite()?.tasks?.[0] as any)?.file
45
+ }
46
+
47
+ function parseMeta<M extends ComparisonMethod>(meta: boolean | SnapshotMeta<M>): SnapshotMeta<M> {
48
+ return typeof meta === 'boolean' ? ({ enable: meta } as any) : { enable: true, ...meta }
49
+ }
50
+
51
+ export function extractAutoSnapshotOptions<M extends SnapshotMeta<any> = SnapshotMeta<any>>(
52
+ task: MetaTask,
53
+ ): M | undefined {
54
+ if (!task) return
55
+
56
+ const list: any[] = []
57
+ let current = task
58
+ while (current?.suite) {
59
+ list.unshift(current.suite.meta)
60
+ current = current.suite
61
+ }
62
+ list.unshift(task.file?.meta)
63
+ list.push(task.meta)
64
+ return list.reduce(
65
+ (acc, cur) => {
66
+ const meta = cur?.[NAME]
67
+ return meta ? Object.assign({}, acc, meta) : acc
68
+ },
69
+ { enable: false },
70
+ )
71
+ }
@@ -0,0 +1,84 @@
1
+ import type { BrowserCommands } from '@vitest/browser/context'
2
+ import dedent from 'dedent'
3
+ import { resolve } from 'pathe'
4
+ import type { ImageSnapshotNextIndexCommand } from '../commands.ts'
5
+ import type {
6
+ ImageSnapshotComparisonInfo,
7
+ PrepareImageSnapshotComparisonCommand,
8
+ } from '../server/commands/prepare_image_snapshot_comparison.ts'
9
+ import { compareImage } from '../shared/compare_image.ts'
10
+ import type { ToMatchImageSnapshotOptions } from '../shared/types.ts'
11
+ import { alignImagesToSameSize } from './align_images.ts'
12
+ import { prettifyOptions } from './compare_image_snapshot.logic.ts'
13
+ import { toDataURL, toImageData } from './image_data.ts'
14
+ import { server } from './vitest_browser_context_proxy.ts'
15
+
16
+ export async function compareImageSnapshot(
17
+ commands: BrowserCommands & PrepareImageSnapshotComparisonCommand & ImageSnapshotNextIndexCommand,
18
+ taskId: string,
19
+ info: ImageSnapshotComparisonInfo,
20
+ options?: ToMatchImageSnapshotOptions<any>,
21
+ ) {
22
+ options = { ...info, ...options } as any
23
+
24
+ const baselineImage = await toImageData(info.baseline)
25
+ const resultImage = await toImageData(info.result)
26
+ const [baselineAlignedImage, resultAlignedImage] = alignImagesToSameSize(baselineImage, resultImage)
27
+ const { width, height } = baselineAlignedImage
28
+ const diffImage = new ImageData(width, height)
29
+ const { pass, diffAmount } = compareImage(
30
+ baselineAlignedImage.data,
31
+ resultAlignedImage.data,
32
+ diffImage.data,
33
+ width,
34
+ height,
35
+ options,
36
+ )
37
+ if (pass) {
38
+ if (options?.expectToFail) {
39
+ throw new Error(
40
+ dedent`Snapshot \`${taskId}\` matched but expected to fail.
41
+
42
+ Options: ${prettifyOptions(options)}
43
+ Diff: ${options.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}
44
+
45
+ Expected: ${resolve(info.projectRoot, info.baselinePath)}
46
+ Actual: ${resolve(info.projectRoot, info.resultPath)}`,
47
+ )
48
+ }
49
+ return
50
+ }
51
+ if (server.config.snapshotOptions.updateSnapshot === 'all' && !options?.expectToFail) {
52
+ await writeSnapshot(commands, resolve(info.projectRoot, info.baselinePath), resultImage)
53
+ return
54
+ }
55
+
56
+ await writeSnapshot(commands, resolve(info.projectRoot, info.diffPath), diffImage)
57
+
58
+ throw new Error(
59
+ dedent`Snapshot \`${taskId}\` mismatched
60
+
61
+ ${
62
+ options?.failureThreshold
63
+ ? options?.failureThresholdType === 'percent'
64
+ ? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`
65
+ : `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`
66
+ : `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`
67
+ }${
68
+ baselineImage.width !== resultImage.width || baselineImage.height !== resultImage.height
69
+ ? `\nThe image size changed from ${baselineImage.width}x${baselineImage.height} to ${resultImage.width}x${resultImage.height}.`
70
+ : ''
71
+ }
72
+
73
+ Options: ${prettifyOptions(options)}
74
+
75
+ Expected: ${resolve(info.projectRoot, info.baselinePath)}
76
+ Actual: ${resolve(info.projectRoot, info.resultPath)}
77
+ Difference: ${resolve(info.projectRoot, info.diffPath)}`,
78
+ )
79
+ }
80
+
81
+ async function writeSnapshot(commands: BrowserCommands, path: string, image: ImageData) {
82
+ const content = (await toDataURL(image)).split(',')[1]!
83
+ return commands.writeFile(path, content, { encoding: 'base64' })
84
+ }
@@ -1,8 +1,9 @@
1
1
  import { commands } from '@vitest/browser/context'
2
2
  import type { AsyncExpectationResult } from '@vitest/expect'
3
3
  import type { ToMatchImageSnapshotOptions } from '../../shared/types.ts'
4
+ import { matchImageSnapshotAction } from '../actions/match_image_snapshot_action.ts'
4
5
  import { ctx } from '../ctx.ts'
5
- import { imageSnapshotMatcher } from '../image_snapshot_matcher.ts'
6
+ import { toTaskId } from '../task_id.ts'
6
7
  import { success } from './expectation_result.ts'
7
8
 
8
9
  export function toMatchImageSnapshot(
@@ -21,9 +22,9 @@ export function toMatchImageSnapshot(
21
22
  if (test.concurrent) {
22
23
  throw new Error(
23
24
  '`toMatchImageSnapshot()` cannot be called in a concurrent test because ' +
24
- "concurrent tests run at the same time in the same iframe and affect each other's environment. ",
25
+ "concurrent tests run at the same time in the same iframe and affect each other's environment.",
25
26
  )
26
27
  }
27
28
 
28
- return imageSnapshotMatcher(commands)(test, subject, options).then(() => success)
29
+ return matchImageSnapshotAction(commands, toTaskId(test), subject, options).then(() => success)
29
30
  }
@@ -0,0 +1,6 @@
1
+ // import type { ToMatchImageSnapshotOptions } from '../shared/types.ts'
2
+
3
+ // export function parseImageSnapshotOptions(taskId: string, options: ToMatchImageSnapshotOptions<any> | undefined) {
4
+ // const { snapshotKey, ...rest } = options ?? {}
5
+ // return snapshotKey ? { ...options, snapshotFileId: `${taskId}-${snapshotKey}` } : rest
6
+ // }
@@ -1,5 +1,6 @@
1
1
  import type { HasImageSnapshotAction } from './has_image_snapshot.ts'
2
+ import type { ToMatchImageSnapshotAction } from './to_match_image_snapshot.ts'
2
3
 
3
4
  declare module '@vitest/browser/context' {
4
- interface BrowserPage extends HasImageSnapshotAction {}
5
+ interface BrowserPage extends HasImageSnapshotAction, ToMatchImageSnapshotAction {}
5
6
  }
@@ -1,5 +1,6 @@
1
1
  import { page } from '@vitest/browser/context'
2
2
  import './augment.ts'
3
3
  import { hasImageSnapshot } from './has_image_snapshot.ts'
4
+ import { toMatchImageSnapshot } from './to_match_image_snapshot.ts'
4
5
 
5
- page.extend({ hasImageSnapshot })
6
+ page.extend({ hasImageSnapshot, toMatchImageSnapshot })
@@ -1,16 +1,17 @@
1
1
  import { type BrowserPage, commands } from '@vitest/browser/context'
2
- import type { ImageSnapshotIdOptions } from '../../shared/types.ts'
2
+ import type { ImageSnapshotKeyOptions } from '../../shared/types.ts'
3
+ import { hasImageSnapshotAction } from '../actions/has_image_snapshot_action.ts'
3
4
  import { ctx } from '../ctx.ts'
4
5
  import { toTaskId } from '../task_id.ts'
5
6
 
6
7
  export interface HasImageSnapshotAction {
7
- hasImageSnapshot(this: BrowserPage, options?: ImageSnapshotIdOptions | undefined): Promise<boolean>
8
+ /**
9
+ * Check if the snapshot image exists.
10
+ */
11
+ hasImageSnapshot(this: BrowserPage, options?: ImageSnapshotKeyOptions | undefined): Promise<boolean>
8
12
  }
9
13
 
10
- /**
11
- * Check if the snapshot image exists.
12
- */
13
- export function hasImageSnapshot(this: BrowserPage, options?: ImageSnapshotIdOptions | undefined) {
14
+ export function hasImageSnapshot(this: BrowserPage, options?: ImageSnapshotKeyOptions | undefined) {
14
15
  const test = ctx.getCurrentTest()
15
16
  if (!test) {
16
17
  throw new Error('`hasImageSnapshot()` must be called in a test.')
@@ -22,19 +23,5 @@ export function hasImageSnapshot(this: BrowserPage, options?: ImageSnapshotIdOpt
22
23
  "concurrent tests run at the same time in the same iframe and affect each other's environment. ",
23
24
  )
24
25
  }
25
- const taskId = toTaskId(test)
26
- const isAutoSnapshot = !!test.meta.vis?.isAutoSnapshot
27
- if (options?.customizeSnapshotId) {
28
- return commands
29
- .imageSnapshotNextIndex(taskId)
30
- .then((index) =>
31
- commands.hasImageSnapshot(
32
- taskId,
33
- options.customizeSnapshotId!({ id: taskId, index, isAutoSnapshot }),
34
- isAutoSnapshot,
35
- ),
36
- )
37
- }
38
-
39
- return commands.hasImageSnapshot(taskId, undefined, isAutoSnapshot)
26
+ return hasImageSnapshotAction(commands, toTaskId(test), options)
40
27
  }
@@ -0,0 +1,27 @@
1
+ import { commands } from '@vitest/browser/context'
2
+ import type { PageImageSnapshotOptions, ToMatchImageSnapshotOptions } from '../../shared/types.ts'
3
+ import { matchPageImageSnapshotAction } from '../actions/match_page_image_snapshot_action.ts'
4
+ import { ctx } from '../ctx.ts'
5
+ import { toTaskId } from '../task_id.ts'
6
+
7
+ export interface ToMatchPageImageSnapshotOptions extends ToMatchImageSnapshotOptions, PageImageSnapshotOptions {}
8
+
9
+ export interface ToMatchImageSnapshotAction {
10
+ toMatchImageSnapshot(options?: ToMatchPageImageSnapshotOptions | undefined): Promise<void>
11
+ }
12
+
13
+ export function toMatchImageSnapshot(options?: ToMatchPageImageSnapshotOptions | undefined) {
14
+ const test = ctx.getCurrentTest()
15
+ if (!test) {
16
+ throw new Error('`toMatchImageSnapshot()` must be called in a test.')
17
+ }
18
+
19
+ if (test.concurrent) {
20
+ throw new Error(
21
+ '`toMatchImageSnapshot()` cannot be called in a concurrent test because ' +
22
+ "concurrent tests run at the same time in the same iframe and affect each other's environment.",
23
+ )
24
+ }
25
+
26
+ return matchPageImageSnapshotAction(commands, toTaskId(test), options)
27
+ }
@@ -0,0 +1,227 @@
1
+ import dedent from 'dedent'
2
+ import { afterEach, beforeAll } from 'vitest'
3
+ import type { SetupVisSuiteCommand } from '../../server/commands/setup_vis_suite.ts'
4
+ import type { ComparisonMethod } from '../../shared/types.ts'
5
+ import { extractAutoSnapshotOptions, setAutoSnapshotOptions } from '../auto_snapshot_options.ts'
6
+ import { ctx } from '../ctx.ts'
7
+ import { shouldTakeSnapshot } from '../should_take_snapshot.ts'
8
+ import type { SnapshotMeta } from '../snapshot_meta.ts'
9
+ import { toTaskId } from '../task_id.ts'
10
+
11
+ /**
12
+ * Visual test configuration on the client side.
13
+ */
14
+ export type VisClientConfigurator<GM extends Record<string, any> | unknown = unknown> = {
15
+ /**
16
+ * Setup the visual test configuration.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // Setup with auto snapshot enabled
21
+ * vis().setup({ auto: true })
22
+ *
23
+ * // Setup with auto snapshot disabled
24
+ * vis().setup({ auto: false })
25
+ *
26
+ * // Same as `vis.setup({ auto: false })`
27
+ * vis.setup()
28
+ *
29
+ * // Setup with auto snapshot determined by the test meta
30
+ * vis.setup({
31
+ * auto: async ({ meta }) => meta['darkOnly'],
32
+ * })
33
+ *
34
+ * // Setup with multiple auto snapshots
35
+ * vis.setup({
36
+ * auto: {
37
+ * async light() { document.body.classList.remove('dark') },
38
+ * async dark() { document.body.classList.add('dark') },
39
+ * }
40
+ * })
41
+ * ```
42
+ */
43
+ setup(options?: {
44
+ auto?:
45
+ | boolean
46
+ | (<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(
47
+ options: SnapshotMeta<C> & M & GM,
48
+ ) => boolean | Promise<boolean>)
49
+ | Record<
50
+ string,
51
+ | boolean
52
+ | (<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(
53
+ options: SnapshotMeta<C> & M & GM,
54
+ ) => boolean | Promise<boolean>)
55
+ >
56
+ }): void
57
+ /**
58
+ * @deprecated Use `vis.setup()` instead.
59
+ */
60
+ presets: {
61
+ /**
62
+ * @deprecated Use `vis.setup()` instead.
63
+ *
64
+ * Enable visual testing.
65
+ *
66
+ * auto snapshot is turned off by default.
67
+ * You can specify the test to take a snapshot during `afterEach()` hook with `setAutoSnapshotOptions()`.
68
+ */
69
+ enable(): void
70
+ /**
71
+ * @deprecated Use `vis.setup()` instead.
72
+ *
73
+ * Enable visual testing.
74
+ *
75
+ * `setAutoSnapshotOptions` will have no effect in this preset.
76
+ */
77
+ manual(): void
78
+ /**
79
+ * @deprecated Use `vis.setup()` instead.
80
+ *
81
+ * Enable automatic visual testing.
82
+ *
83
+ * This will take a snapshot after each test.
84
+ */
85
+ auto(): void
86
+ /**
87
+ * @deprecated Use `vis.setup()` instead.
88
+ *
89
+ * Enable automatic visual testing with multiple themes.
90
+ *
91
+ * This will take a snapshot after each test for each theme.
92
+ *
93
+ * @param themes A record of theme names and their setup functions.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * vis().presets.theme({
98
+ * light() { document.body.classList.add('light') },
99
+ * dark() { document.body.classList.add('dark') },
100
+ * })
101
+ * ```
102
+ */
103
+ theme<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(
104
+ themes: Record<
105
+ string,
106
+ boolean | ((options: SnapshotMeta<C> & M & GM) => Promise<boolean> | Promise<void> | boolean | void)
107
+ >,
108
+ ): void
109
+ }
110
+ beforeAll: {
111
+ setup(): Promise<void>
112
+ }
113
+ afterEach: {
114
+ matchImageSnapshot(): Promise<void>
115
+ matchPerTheme<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(
116
+ themes: Record<
117
+ string,
118
+ boolean | ((options: SnapshotMeta<C> & M & GM) => Promise<boolean> | Promise<void> | boolean | void)
119
+ >,
120
+ ): () => Promise<void>
121
+ }
122
+ }
123
+
124
+ export function createVis<GM extends Record<string, any> | unknown = unknown>(commands: SetupVisSuiteCommand) {
125
+ let subject: string | undefined
126
+
127
+ const vis: VisClientConfigurator<GM> = {
128
+ setup(options) {
129
+ if (!options || options.auto === false) {
130
+ beforeAll(vis.beforeAll.setup)
131
+ } else {
132
+ beforeAll(async () => {
133
+ await vis.beforeAll.setup()
134
+ setAutoSnapshotOptions(true)
135
+ })
136
+ }
137
+
138
+ if (typeof options?.auto === 'function') {
139
+ afterEach(
140
+ vis.afterEach.matchPerTheme({
141
+ auto: options.auto,
142
+ }),
143
+ )
144
+ } else if (typeof options?.auto === 'object') {
145
+ afterEach(vis.afterEach.matchPerTheme(options.auto))
146
+ } else {
147
+ afterEach(vis.afterEach.matchImageSnapshot)
148
+ }
149
+ },
150
+ presets: {
151
+ enable() {
152
+ beforeAll(vis.beforeAll.setup)
153
+ afterEach(vis.afterEach.matchImageSnapshot)
154
+ },
155
+ manual() {
156
+ beforeAll(vis.beforeAll.setup)
157
+ },
158
+ auto() {
159
+ beforeAll(async () => {
160
+ await vis.beforeAll.setup()
161
+ setAutoSnapshotOptions(true)
162
+ })
163
+ afterEach(vis.afterEach.matchImageSnapshot)
164
+ },
165
+ theme(themes) {
166
+ beforeAll(async () => {
167
+ await vis.beforeAll.setup()
168
+ setAutoSnapshotOptions(true)
169
+ })
170
+ afterEach(vis.afterEach.matchPerTheme(themes))
171
+ },
172
+ },
173
+ beforeAll: {
174
+ async setup() {
175
+ subject = (await commands.setupVisSuite()).subject
176
+ },
177
+ },
178
+ afterEach: {
179
+ async matchImageSnapshot() {
180
+ return vis.afterEach.matchPerTheme({ async auto() {} })()
181
+ },
182
+ matchPerTheme(themes) {
183
+ return async function matchImageSnapshot() {
184
+ const test = ctx.getCurrentTest()
185
+ if ((test?.result?.errors?.length ?? 0) > 0) return
186
+
187
+ const meta = extractAutoSnapshotOptions(test)
188
+ if (!shouldTakeSnapshot(meta)) return
189
+ const errors: Array<[string, Error]> = []
190
+ for (const themeId in themes) {
191
+ try {
192
+ await new Promise((a) => setTimeout(a, 10))
193
+ const theme = themes[themeId]
194
+ const r = typeof theme === 'function' ? await theme(meta! as any) : theme
195
+ if (r === false) continue
196
+ await test!.context.expect(getSubject(meta?.subject ?? subject)).toMatchImageSnapshot({
197
+ ...meta,
198
+ snapshotKey: meta?.snapshotKey ?? themeId,
199
+ })
200
+ } catch (error) {
201
+ errors.push([themeId, error as Error])
202
+ }
203
+ }
204
+ if (errors.length > 0) {
205
+ if (errors.length === 1) throw errors[0]![1]
206
+ const taskId = toTaskId(test!)
207
+ throw new AggregateError(
208
+ errors,
209
+ dedent`Snapshot \`${taskId}\` mismatched
210
+
211
+ ${errors
212
+ .map(([themeId, error]) => {
213
+ return `Theme \`${themeId}\` failed: ${error.message}`
214
+ })
215
+ .join('\n\n')}`,
216
+ )
217
+ }
218
+ }
219
+ },
220
+ },
221
+ }
222
+ return vis
223
+ }
224
+
225
+ function getSubject(selectors: string | undefined) {
226
+ return selectors ? (document.querySelector(selectors) ?? document.body) : document.body
227
+ }
@@ -0,0 +1,33 @@
1
+ import { commands } from '@vitest/browser/context'
2
+ import { afterEach, beforeAll, beforeEach, vi, type Awaitable, type Suite } from 'vitest'
3
+
4
+ export const ctx = {
5
+ beforeAll,
6
+ beforeEach,
7
+ afterEach,
8
+ commands,
9
+ mock() {
10
+ const beforeAllListeners: Array<(suite: Readonly<Suite | File>) => Awaitable<unknown>> = []
11
+ beforeEach(() => {
12
+ ctx.beforeAll = vi.fn((fn) => {
13
+ beforeAllListeners.push(fn)
14
+ })
15
+ ctx.beforeEach = vi.fn()
16
+ ctx.afterEach = vi.fn()
17
+ ctx.commands = {
18
+ setupVisSuite: vi.fn(),
19
+ } as any
20
+ })
21
+ afterEach(() => {
22
+ ctx.reset()
23
+ })
24
+ return {
25
+ beforeAllListeners,
26
+ }
27
+ },
28
+ reset() {
29
+ ctx.beforeAll = beforeAll
30
+ ctx.beforeEach = beforeEach
31
+ ctx.afterEach = afterEach
32
+ },
33
+ }
@@ -1,15 +1,5 @@
1
- import type { SnapshotMeta } from '../shared/types.ts'
2
- import { ctx } from './ctx.ts'
3
- import { extractAutoSnapshotOptions } from './snapshot_options.ts'
1
+ import type { SnapshotMeta } from './snapshot_meta.ts'
4
2
 
5
- /**
6
- * Determine should snapshot be taken.
7
- *
8
- * not story: false
9
- * no `snapshot` tag: false
10
- * disabled by `!snapshot` tag: false
11
- */
12
- export function shouldTakeSnapshot(meta?: SnapshotMeta<any> | undefined) {
13
- const m = meta ?? extractAutoSnapshotOptions(ctx.getCurrentTest())
14
- return document.body.childElementCount > 0 && m?.enable
3
+ export function shouldTakeSnapshot(meta: SnapshotMeta<any> | undefined) {
4
+ return document.body.childElementCount > 0 && meta?.enable
15
5
  }
@@ -0,0 +1,7 @@
1
+ import type { ComparisonMethod, ImageSnapshotSubjectOptions, ToMatchImageSnapshotOptions } from '../shared/types.ts'
2
+
3
+ export type SnapshotMeta<M extends ComparisonMethod> = ToMatchImageSnapshotOptions<M> &
4
+ ImageSnapshotSubjectOptions & {
5
+ enable?: boolean | undefined
6
+ [key: string]: unknown
7
+ }
@@ -3,9 +3,6 @@ type Task = {
3
3
  suite?: Task | undefined
4
4
  }
5
5
 
6
- /**
7
- * @deprecated not needed anymore. Use `matchImageSnapshot` directly instead.
8
- */
9
6
  export function toTaskId(task: Task) {
10
7
  const l: any[] = []
11
8
  let t = task
@@ -1,5 +1,4 @@
1
- import type { NAME } from '../shared/constants.ts'
2
- import type { SnapshotMeta } from '../shared/types.ts'
1
+ import type { SnapshotTestMeta } from '../shared/types.ts'
3
2
 
4
3
  let vitestSuite: Awaited<typeof import('vitest/suite')>
5
4
 
@@ -9,9 +8,7 @@ if ((globalThis as any).__vitest_browser__) {
9
8
  })
10
9
  }
11
10
 
12
- export type CurrentTest = ReturnType<typeof vitestSuite.getCurrentTest> & {
13
- meta: { [NAME]?: SnapshotMeta<'pixel' | 'ssim'> & { isAutoSnapshot?: boolean | undefined } }
14
- }
11
+ export type CurrentTest = ReturnType<typeof vitestSuite.getCurrentTest> & SnapshotTestMeta
15
12
 
16
13
  export const getCurrentTest = () => vitestSuite?.getCurrentTest() as CurrentTest
17
14
  export const getCurrentSuite = () => vitestSuite?.getCurrentSuite()
@@ -0,0 +1,11 @@
1
+ import './server/commands/augment.ts'
2
+
3
+ export * from './client/actions/has_image_snapshot_action.ts'
4
+ export * from './client/actions/load_image_snapshot_results_action.ts'
5
+ export * from './client/actions/match_image_snapshot_action.ts'
6
+ export { setAutoSnapshotOptions, type MetaTask } from './client/auto_snapshot_options.ts'
7
+ export * from './client/expect/expectation_result.ts'
8
+ export type * from './client/expect/to_match_image_snapshot.types.ts'
9
+ export * from './client/task_id.ts'
10
+ export * from './shared/constants.ts'
11
+ export type * from './shared/types.ts'
package/src/commands.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './server/commands/has_image_snapshot.ts'
2
2
  export * from './server/commands/image_snapshot_next_index.ts'
3
- export * from './server/commands/match_image_snapshot.ts'
4
3
  export * from './server/commands/prepare_image_snapshot_comparison.ts'
5
4
  export * from './server/commands/setup_vis_suite.ts'