-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Expand file tree
/
Copy pathvitest.extend.ts
More file actions
152 lines (130 loc) · 4.57 KB
/
Copy pathvitest.extend.ts
File metadata and controls
152 lines (130 loc) · 4.57 KB
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
import { expect, Snapshots } from 'vitest';
import { cloneDeepWith } from 'es-toolkit/compat';
import type { FabricObject } from './src/shapes/Object/Object';
import type { TMat2D } from './src/typedefs';
import type { ExtendedOptions, ObjectOptions } from './vitest';
import type { FabricImage } from './dist-extensions';
const SVG_RE = /(SVGID|CLIPPATH|imageCrop)_[0-9]+/gm;
const SVG_XLINK_HREF_RE = /xlink:href="([^"]*)"/gm;
function basename(link: string) {
return link.split(/[\\/]/).pop()?.replace(/"/gm, '') || '';
}
function replaceLinks(value: string) {
return (value.match(SVG_XLINK_HREF_RE) ?? []).reduce(
(acc, match) =>
acc.replace(match, `xlink:href="assets/${basename(match)}"`),
value,
);
}
export function sanitizeSVG(value: string) {
return replaceLinks(value).replace(SVG_RE, 'SVGID');
}
function looksLikeFabricObject(val: unknown): val is FabricObject {
return (
!!val &&
typeof val === 'object' &&
// all FabricObjects implement these three members
typeof (val as any).toObject === 'function' &&
typeof (val as any).render === 'function' &&
typeof (val as any).constructor?.type === 'string'
);
}
export function isJSDOM(): boolean {
return 'jsdom' in globalThis;
}
export const roundSnapshotOptions = {
cloneDeepWith: (value: unknown) => {
if (typeof value === 'number') {
return Math.round(value);
}
},
};
const { toMatchSnapshot } = Snapshots;
expect.extend({
toMatchObjectSnapshot(
received: unknown,
{
cloneDeepWith: customiser,
includeDefaultValues,
...properties
}: ObjectOptions = {},
hint?: string,
) {
let snap = received as Record<string, unknown>;
if (looksLikeFabricObject(received)) {
const restore = received.includeDefaultValues;
received.includeDefaultValues = includeDefaultValues ?? restore;
snap = received.toObject();
received.includeDefaultValues = restore;
}
delete snap.version;
const value = cloneDeepWith(snap, (v, k, obj, stack) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const c = (customiser as any)?.(v, k, obj, stack);
if (c !== undefined) return c;
if ((k === 'width' || k === 'height') && typeof v === 'number')
return Math.round(v * 100) / 100;
// Normalize uid-based ids which vary by test execution order
if (k === 'id' && typeof v === 'number') return 0;
});
return toMatchSnapshot.call(this, value, properties, hint);
},
toMatchSnapshot(
received: unknown,
{ cloneDeepWith: customiser, ...rest }: ExtendedOptions = {},
hint?: string,
) {
const value = customiser ? cloneDeepWith(received, customiser) : received;
return Object.keys(rest).length > 0
? toMatchSnapshot.call(this, value, rest, hint)
: toMatchSnapshot.call(this, value, hint);
},
toMatchSVGSnapshot(received: string, hint?: string) {
return toMatchSnapshot.call(this, sanitizeSVG(received), hint);
},
toSameImageObject(actual: FabricImage, expected: FabricImage) {
const normalizedActual = {
...actual,
// @ts-expect-error -- src is protected in FabricImage class
src: basename(actual.src),
};
const normalizedExpected = {
...expected,
// @ts-expect-error -- src is protected in FabricImage class
src: basename(expected.src),
};
const pass = this.equals(normalizedActual, normalizedExpected);
return {
pass,
message: () =>
pass
? `Expected image object to not equal the reference`
: `Expected image object to equal the reference\nReceived: ${JSON.stringify(normalizedActual)}\nExpected: ${JSON.stringify(normalizedExpected)}`,
};
},
toEqualSVG(actual: string, expected: string) {
const sanitizedActual = sanitizeSVG(actual);
const sanitizedExpected = sanitizeSVG(expected);
const pass = sanitizedActual === sanitizedExpected;
return {
pass,
message: () =>
pass
? `Expected SVG to not equal the normalized reference`
: `Expected SVG to equal the normalized reference\nReceived: ${sanitizedActual}\nExpected: ${sanitizedExpected}`,
};
},
toEqualRoundedMatrix(actual: TMat2D, expected: TMat2D, precision = 10) {
const error = Math.pow(10, -precision);
return {
message: () => {
return `expected ${this.utils.printReceived(
actual,
)} to be rounded to ${this.utils.printExpected(
expected.map((x) => Math.round(x / error) * error),
)}`;
},
pass: actual.every((x, i) => Math.abs(x - expected[i]) < error),
};
},
});