@stacks/clarinet-sdk 3.8.1
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.
- package/README.md +91 -0
- package/dist/cjs/common/src/sdkProxyHelpers.d.ts +86 -0
- package/dist/cjs/common/src/sdkProxyHelpers.js +66 -0
- package/dist/cjs/common/src/sdkProxyHelpers.js.map +1 -0
- package/dist/cjs/node/src/index.d.ts +15 -0
- package/dist/cjs/node/src/index.js +68 -0
- package/dist/cjs/node/src/index.js.map +1 -0
- package/dist/cjs/node/src/sdkProxy.d.ts +11 -0
- package/dist/cjs/node/src/sdkProxy.js +98 -0
- package/dist/cjs/node/src/sdkProxy.js.map +1 -0
- package/dist/cjs/node/src/vfs.d.ts +3 -0
- package/dist/cjs/node/src/vfs.js +109 -0
- package/dist/cjs/node/src/vfs.js.map +1 -0
- package/dist/cjs/node/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/esm/common/src/sdkProxyHelpers.d.ts +86 -0
- package/dist/esm/common/src/sdkProxyHelpers.js +61 -0
- package/dist/esm/common/src/sdkProxyHelpers.js.map +1 -0
- package/dist/esm/node/src/index.d.ts +15 -0
- package/dist/esm/node/src/index.js +30 -0
- package/dist/esm/node/src/index.js.map +1 -0
- package/dist/esm/node/src/sdkProxy.d.ts +11 -0
- package/dist/esm/node/src/sdkProxy.js +95 -0
- package/dist/esm/node/src/sdkProxy.js.map +1 -0
- package/dist/esm/node/src/vfs.d.ts +3 -0
- package/dist/esm/node/src/vfs.js +70 -0
- package/dist/esm/node/src/vfs.js.map +1 -0
- package/dist/esm/node/src/vitest/index.d.ts +31 -0
- package/dist/esm/node/src/vitest/index.js +49 -0
- package/dist/esm/node/src/vitest/index.js.map +1 -0
- package/dist/esm/node/tsconfig.tsbuildinfo +1 -0
- package/dist/esm/package.json +1 -0
- package/package.json +73 -0
- package/vitest-helpers/src/clarityValuesMatchers.ts +389 -0
- package/vitest-helpers/src/global.d.ts +20 -0
- package/vitest-helpers/src/vitest.d.ts +28 -0
- package/vitest-helpers/src/vitest.setup.ts +69 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { expect, ExpectStatic, assert } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
Cl,
|
|
4
|
+
ClarityValue,
|
|
5
|
+
ClarityType,
|
|
6
|
+
ResponseOkCV,
|
|
7
|
+
NoneCV,
|
|
8
|
+
SomeCV,
|
|
9
|
+
ResponseErrorCV,
|
|
10
|
+
IntCV,
|
|
11
|
+
UIntCV,
|
|
12
|
+
StringAsciiCV,
|
|
13
|
+
StringUtf8CV,
|
|
14
|
+
ContractPrincipalCV,
|
|
15
|
+
StandardPrincipalCV,
|
|
16
|
+
ListCV,
|
|
17
|
+
TupleCV,
|
|
18
|
+
BufferCV,
|
|
19
|
+
TrueCV,
|
|
20
|
+
FalseCV,
|
|
21
|
+
BooleanCV,
|
|
22
|
+
cvToString,
|
|
23
|
+
} from "@stacks/transactions";
|
|
24
|
+
|
|
25
|
+
import { MatcherState } from "@vitest/expect";
|
|
26
|
+
|
|
27
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
BigInt.prototype.toJSON = function () {
|
|
30
|
+
return this.toString();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function notStr(isNot: boolean) {
|
|
34
|
+
return isNot ? "not " : "";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatMessage(this: MatcherState, received: string, expected: string) {
|
|
38
|
+
return `expected ${received} ${notStr(this.isNot)}to be ${expected}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class ClarityTypeError extends Error {
|
|
42
|
+
actual?: any;
|
|
43
|
+
expected?: any;
|
|
44
|
+
|
|
45
|
+
constructor({ message, actual, expected }: { message: string; actual?: any; expected?: any }) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.actual = actual;
|
|
48
|
+
this.expected = expected;
|
|
49
|
+
|
|
50
|
+
Object.setPrototypeOf(this, ClarityTypeError.prototype);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type ClarityTypetoValue = {
|
|
55
|
+
[ClarityType.OptionalNone]: NoneCV;
|
|
56
|
+
[ClarityType.OptionalSome]: SomeCV;
|
|
57
|
+
[ClarityType.ResponseOk]: ResponseOkCV;
|
|
58
|
+
[ClarityType.ResponseErr]: ResponseErrorCV;
|
|
59
|
+
[ClarityType.BoolTrue]: TrueCV;
|
|
60
|
+
[ClarityType.BoolFalse]: FalseCV;
|
|
61
|
+
[ClarityType.Int]: IntCV;
|
|
62
|
+
[ClarityType.UInt]: UIntCV;
|
|
63
|
+
[ClarityType.StringASCII]: StringAsciiCV;
|
|
64
|
+
[ClarityType.StringUTF8]: StringUtf8CV;
|
|
65
|
+
[ClarityType.PrincipalStandard]: StandardPrincipalCV;
|
|
66
|
+
[ClarityType.PrincipalContract]: ContractPrincipalCV;
|
|
67
|
+
[ClarityType.List]: ListCV;
|
|
68
|
+
[ClarityType.Tuple]: TupleCV;
|
|
69
|
+
[ClarityType.Buffer]: BufferCV;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const ClarityTypeReversed = Object.fromEntries(Object.entries(ClarityType).map(([k, v]) => [v, k]));
|
|
73
|
+
|
|
74
|
+
// the "simple clarity values" are CVs that can't be nested and have `value` property
|
|
75
|
+
type SimpleCV = BooleanCV | IntCV | UIntCV | StringAsciiCV | StringUtf8CV;
|
|
76
|
+
type SimpleCVTypes =
|
|
77
|
+
| ClarityType.BoolFalse
|
|
78
|
+
| ClarityType.BoolTrue
|
|
79
|
+
| ClarityType.Int
|
|
80
|
+
| ClarityType.UInt
|
|
81
|
+
| ClarityType.StringASCII
|
|
82
|
+
| ClarityType.StringUTF8;
|
|
83
|
+
|
|
84
|
+
const validClarityTypes = Object.values(ClarityType).filter(
|
|
85
|
+
(t) => typeof t === "string",
|
|
86
|
+
) as string[];
|
|
87
|
+
|
|
88
|
+
function isClarityValue(input: unknown): input is ClarityValue {
|
|
89
|
+
if (!input || typeof input !== "object") return false;
|
|
90
|
+
if (!("type" in input) || typeof input.type !== "string") return false;
|
|
91
|
+
if (!validClarityTypes.includes(input.type)) return false;
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isClarityValueWithType<T extends ClarityType>(
|
|
97
|
+
input: unknown,
|
|
98
|
+
withType: T,
|
|
99
|
+
): input is ClarityTypetoValue[T] {
|
|
100
|
+
if (!isClarityValue(input)) return false;
|
|
101
|
+
if (input.type !== withType) return false;
|
|
102
|
+
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function checkCVType<T extends ClarityType>(
|
|
107
|
+
actual: unknown,
|
|
108
|
+
expectedType: T,
|
|
109
|
+
isNot: boolean,
|
|
110
|
+
): actual is ClarityTypetoValue[T] {
|
|
111
|
+
const isCV = isClarityValue(actual);
|
|
112
|
+
|
|
113
|
+
if (!isCV) {
|
|
114
|
+
throw new ClarityTypeError({
|
|
115
|
+
message: `actual value must ${notStr(isNot)}be a Clarity "${
|
|
116
|
+
ClarityTypeReversed[expectedType]
|
|
117
|
+
}", received "${typeof actual}"`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const isCVWithType = isClarityValueWithType(actual, expectedType);
|
|
122
|
+
|
|
123
|
+
if (!isCVWithType) {
|
|
124
|
+
// for readability, the error diff is kept short if the developers uses the wrong `expect<ClarityType>`
|
|
125
|
+
// ideally, we should have a way to display short message diffs even if the actual and/or expected data are big lists/tuples/buffers
|
|
126
|
+
|
|
127
|
+
// for now, we make an exception and display the full error message if the actual value is a ResponseErr
|
|
128
|
+
const errorCode = actual.type === ClarityType.ResponseErr ? ` ${Cl.prettyPrint(actual)}` : "";
|
|
129
|
+
|
|
130
|
+
throw new ClarityTypeError({
|
|
131
|
+
// generic and short message
|
|
132
|
+
message: `actual value must ${notStr(isNot)}be a Clarity "${
|
|
133
|
+
ClarityTypeReversed[expectedType]
|
|
134
|
+
}", received "${ClarityTypeReversed[actual.type]}"${errorCode}`,
|
|
135
|
+
actual: ClarityTypeReversed[actual.type],
|
|
136
|
+
expected: ClarityTypeReversed[expectedType],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function errorToAssertionResult(this: MatcherState, err: any) {
|
|
144
|
+
return {
|
|
145
|
+
pass: false,
|
|
146
|
+
message: () => err.message,
|
|
147
|
+
actual: err.actual,
|
|
148
|
+
expected: err.expected,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function simpleAssertion(
|
|
153
|
+
this: MatcherState,
|
|
154
|
+
cvType: SimpleCVTypes,
|
|
155
|
+
actualRaw: unknown,
|
|
156
|
+
expectedRaw: SimpleCV,
|
|
157
|
+
) {
|
|
158
|
+
try {
|
|
159
|
+
const isCV = checkCVType(actualRaw, cvType, this.isNot);
|
|
160
|
+
assert(isCV);
|
|
161
|
+
} catch (e: any) {
|
|
162
|
+
return errorToAssertionResult.call(this, e);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
pass: this.equals(actualRaw, expectedRaw, undefined, true),
|
|
167
|
+
message: () =>
|
|
168
|
+
`expected ${Cl.prettyPrint(actualRaw)} ${notStr(this.isNot)}to be ${Cl.prettyPrint(
|
|
169
|
+
expectedRaw,
|
|
170
|
+
)}`,
|
|
171
|
+
actual: Cl.prettyPrint(actualRaw, 2),
|
|
172
|
+
expected: Cl.prettyPrint(expectedRaw, 2),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const typeToCvMethod = {
|
|
177
|
+
[ClarityType.ResponseOk]: Cl.ok,
|
|
178
|
+
[ClarityType.ResponseErr]: Cl.error,
|
|
179
|
+
[ClarityType.OptionalSome]: Cl.some,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// simple composite types are `ok`, `err`, `some`
|
|
183
|
+
function simpleCompositeAssertion(
|
|
184
|
+
this: MatcherState,
|
|
185
|
+
expectedType: ClarityType.ResponseOk | ClarityType.ResponseErr | ClarityType.OptionalSome,
|
|
186
|
+
actualRaw: unknown,
|
|
187
|
+
expectedValue: ClarityValue | ExpectStatic,
|
|
188
|
+
) {
|
|
189
|
+
try {
|
|
190
|
+
const isCV = checkCVType(actualRaw, expectedType, this.isNot);
|
|
191
|
+
assert(isCV);
|
|
192
|
+
} catch (e: any) {
|
|
193
|
+
return errorToAssertionResult.call(this, e);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const clMethod = typeToCvMethod[expectedType];
|
|
197
|
+
|
|
198
|
+
const expectedIsCV = isClarityValue(expectedValue);
|
|
199
|
+
const expectedOneLine = expectedIsCV
|
|
200
|
+
? Cl.prettyPrint(clMethod(expectedValue))
|
|
201
|
+
: JSON.stringify(expectedValue);
|
|
202
|
+
const expected = expectedIsCV
|
|
203
|
+
? Cl.prettyPrint(clMethod(expectedValue), 2)
|
|
204
|
+
: JSON.stringify(expectedValue);
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
pass: this.equals(actualRaw.value, expectedValue, undefined, true),
|
|
208
|
+
message: () => formatMessage.call(this, Cl.prettyPrint(actualRaw), expectedOneLine),
|
|
209
|
+
actual: Cl.prettyPrint(actualRaw, 2),
|
|
210
|
+
expected,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
expect.extend({
|
|
215
|
+
toHaveClarityType(actual: unknown, expectedType: ClarityType) {
|
|
216
|
+
try {
|
|
217
|
+
const isCV = checkCVType(actual, expectedType, this.isNot);
|
|
218
|
+
assert(isCV);
|
|
219
|
+
} catch (e: any) {
|
|
220
|
+
return errorToAssertionResult.call(this, e);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
pass: true,
|
|
225
|
+
message: () =>
|
|
226
|
+
`actual value must ${notStr(this.isNot)}be a Clarity "${ClarityTypeReversed[expectedType]}"`,
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
toBeBool(actual: unknown, expected: boolean) {
|
|
231
|
+
const expectedType = expected ? ClarityType.BoolTrue : ClarityType.BoolFalse;
|
|
232
|
+
return simpleAssertion.call(this, expectedType, actual, Cl.bool(expected));
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
toBeInt(actual: unknown, expected: number | bigint) {
|
|
236
|
+
return simpleAssertion.call(this, ClarityType.Int, actual, Cl.int(expected));
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
toBeUint(actual: unknown, expected: number | bigint) {
|
|
240
|
+
return simpleAssertion.call(this, ClarityType.UInt, actual, Cl.uint(expected));
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
toBeAscii(actual: unknown, expected: string) {
|
|
244
|
+
return simpleAssertion.call(this, ClarityType.StringASCII, actual, Cl.stringAscii(expected));
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
toBeUtf8(actual: unknown, expected: string) {
|
|
248
|
+
return simpleAssertion.call(this, ClarityType.StringUTF8, actual, Cl.stringUtf8(expected));
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
toBeOk(actual: unknown, expectedValue: ExpectStatic | ClarityValue) {
|
|
252
|
+
return simpleCompositeAssertion.call(this, ClarityType.ResponseOk, actual, expectedValue);
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
toBeErr(actual: unknown, expectedValue: ExpectStatic | ClarityValue) {
|
|
256
|
+
return simpleCompositeAssertion.call(this, ClarityType.ResponseErr, actual, expectedValue);
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
toBeSome(actual: unknown, expectedValue: ExpectStatic | ClarityValue) {
|
|
260
|
+
return simpleCompositeAssertion.call(this, ClarityType.OptionalSome, actual, expectedValue);
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
toBeNone(actual: unknown) {
|
|
264
|
+
const expectedType = ClarityType.OptionalNone;
|
|
265
|
+
try {
|
|
266
|
+
const isCV = checkCVType(actual, expectedType, this.isNot);
|
|
267
|
+
assert(isCV);
|
|
268
|
+
} catch (e: any) {
|
|
269
|
+
return errorToAssertionResult.call(this, e);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const expected = Cl.none();
|
|
273
|
+
return {
|
|
274
|
+
pass: this.equals(actual, expected, undefined, true),
|
|
275
|
+
message: () => formatMessage.call(this, Cl.prettyPrint(actual), Cl.prettyPrint(actual)),
|
|
276
|
+
actual: Cl.prettyPrint(actual, 2),
|
|
277
|
+
expected: Cl.prettyPrint(actual, 2),
|
|
278
|
+
};
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
toBePrincipal(actual: unknown, expectedString: string) {
|
|
282
|
+
const isStandard = !expectedString.includes(".");
|
|
283
|
+
let expected: StandardPrincipalCV | ContractPrincipalCV;
|
|
284
|
+
|
|
285
|
+
const expectedType = isStandard ? ClarityType.PrincipalStandard : ClarityType.PrincipalContract;
|
|
286
|
+
try {
|
|
287
|
+
const isCV = checkCVType(actual, expectedType, this.isNot);
|
|
288
|
+
assert(isCV);
|
|
289
|
+
} catch (e: any) {
|
|
290
|
+
return errorToAssertionResult.call(this, e);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const actualString = cvToString(actual, "tryAscii");
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
expected = isStandard
|
|
297
|
+
? Cl.standardPrincipal(expectedString)
|
|
298
|
+
: Cl.contractPrincipal(...(expectedString.split(".") as [string, string]));
|
|
299
|
+
} catch {
|
|
300
|
+
return {
|
|
301
|
+
pass: false,
|
|
302
|
+
message: () => `expected ${expectedString} ${notStr(this.isNot)}to be a principal`,
|
|
303
|
+
actual: actualString,
|
|
304
|
+
expected: expectedString,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
pass: this.equals(actual, expected, undefined, true),
|
|
310
|
+
message: () => formatMessage.call(this, actualString, expectedString),
|
|
311
|
+
actual: Cl.prettyPrint(actual, 2),
|
|
312
|
+
expected: Cl.prettyPrint(expected, 2),
|
|
313
|
+
};
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
toBeBuff(actual: unknown, expectedRaw: Uint8Array) {
|
|
317
|
+
const expectedType = ClarityType.Buffer;
|
|
318
|
+
try {
|
|
319
|
+
const isCV = checkCVType(actual, expectedType, this.isNot);
|
|
320
|
+
assert(isCV);
|
|
321
|
+
} catch (e: any) {
|
|
322
|
+
return errorToAssertionResult.call(this, e);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const expected = Cl.buffer(expectedRaw);
|
|
326
|
+
return {
|
|
327
|
+
pass: this.equals(actual, expected, undefined, true),
|
|
328
|
+
// note: throw a simple message and rely on `actual` and `expected` to display the diff
|
|
329
|
+
message: () => `the received Buffer does ${this.isNot ? "" : "not "}match the expected one`,
|
|
330
|
+
actual: Cl.prettyPrint(actual, 2),
|
|
331
|
+
expected: Cl.prettyPrint(expected, 2),
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
toBeList(actual: unknown, expectedItems: ExpectStatic[] | ClarityValue[]) {
|
|
336
|
+
const expectedType = ClarityType.List;
|
|
337
|
+
try {
|
|
338
|
+
const isCV = checkCVType(actual, expectedType, this.isNot);
|
|
339
|
+
assert(isCV);
|
|
340
|
+
} catch (e: any) {
|
|
341
|
+
return errorToAssertionResult.call(this, e);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const isListArray = checkIsListArray(expectedItems);
|
|
345
|
+
const expected = isListArray ? Cl.prettyPrint(Cl.list(expectedItems), 2) : expectedItems;
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
pass: this.equals(actual.value, expectedItems, undefined, true),
|
|
349
|
+
// note: throw a simple message and rely on `actual` and `expected` to display the diff
|
|
350
|
+
message: () => `the received List does ${this.isNot ? "" : "not "}match the expected one`,
|
|
351
|
+
actual: Cl.prettyPrint(actual, 2),
|
|
352
|
+
expected,
|
|
353
|
+
};
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
toBeTuple(actual: unknown, expectedData: Record<string, ExpectStatic | ClarityValue>) {
|
|
357
|
+
const expectedType = ClarityType.Tuple;
|
|
358
|
+
try {
|
|
359
|
+
const isCV = checkCVType(actual, expectedType, this.isNot);
|
|
360
|
+
assert(isCV);
|
|
361
|
+
} catch (e: any) {
|
|
362
|
+
return errorToAssertionResult.call(this, e);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const isTupleData = checkIsTupleData(expectedData);
|
|
366
|
+
const expected = isTupleData ? Cl.prettyPrint(Cl.tuple(expectedData), 2) : expectedData;
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
pass: this.equals(actual.value, expectedData, undefined, true),
|
|
370
|
+
// note: throw a simple message and rely on `actual` and `expected` to display the diff
|
|
371
|
+
message: () => `the received Tuple does ${this.isNot ? "" : "not "}match the expected one`,
|
|
372
|
+
actual: Cl.prettyPrint(actual, 2),
|
|
373
|
+
expected,
|
|
374
|
+
};
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// for composite types, matchers need to narrow the type of the expected value
|
|
379
|
+
// to know if it contains AsymmetricMatchers or if it's only ClarityValues
|
|
380
|
+
|
|
381
|
+
function checkIsTupleData(
|
|
382
|
+
expected: Record<string, ExpectStatic | ClarityValue>,
|
|
383
|
+
): expected is Record<string, ClarityValue> {
|
|
384
|
+
return Object.values(expected).every((v) => isClarityValue(v));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function checkIsListArray(expected: ExpectStatic[] | ClarityValue[]): expected is ClarityValue[] {
|
|
388
|
+
return expected.every((v) => isClarityValue(v));
|
|
389
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Simnet } from "../../dist/esm/node/src";
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
var simnet: Simnet;
|
|
5
|
+
var testEnvironment: string;
|
|
6
|
+
var coverageReports: string[];
|
|
7
|
+
var costsReports: string[];
|
|
8
|
+
var options: {
|
|
9
|
+
clarinet: {
|
|
10
|
+
manifestPath: string;
|
|
11
|
+
initBeforeEach: boolean;
|
|
12
|
+
coverage: boolean;
|
|
13
|
+
coverageFilename: string;
|
|
14
|
+
costs: boolean;
|
|
15
|
+
costsFilename: string;
|
|
16
|
+
includeBootContracts: boolean;
|
|
17
|
+
bootContractsPath: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Assertion, AsymmetricMatchersContaining, ExpectStatic } from "vitest";
|
|
2
|
+
import type { ClarityType, ClarityValue } from "@stacks/transactions";
|
|
3
|
+
|
|
4
|
+
interface ClarityValuesMatchers<R = unknown> {
|
|
5
|
+
toHaveClarityType(expectedType: ClarityType): R;
|
|
6
|
+
|
|
7
|
+
toBeOk(expected: ExpectStatic | ClarityValue): R;
|
|
8
|
+
toBeErr(expected: ExpectStatic | ClarityValue): R;
|
|
9
|
+
|
|
10
|
+
toBeSome(expected: ExpectStatic | ClarityValue): R;
|
|
11
|
+
toBeNone(): R;
|
|
12
|
+
|
|
13
|
+
toBeBool(expected: boolean): R;
|
|
14
|
+
toBeInt(rexpected: number | bigint): R;
|
|
15
|
+
toBeUint(expected: number | bigint): R;
|
|
16
|
+
toBeAscii(expected: string): R;
|
|
17
|
+
toBeUtf8(expected: string): R;
|
|
18
|
+
toBePrincipal(expected: string): R;
|
|
19
|
+
toBeBuff(expected: Uint8Array): R;
|
|
20
|
+
|
|
21
|
+
toBeList(expected: ExpectStatic[] | ClarityValue[]): R;
|
|
22
|
+
toBeTuple(expected: Record<string, ExpectStatic | ClarityValue>): R;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare module "vitest" {
|
|
26
|
+
interface Assertion<T = any> extends ClarityValuesMatchers<T> {}
|
|
27
|
+
interface AsymmetricMatchersContaining extends ClarityValuesMatchers<ExpectStatic> {}
|
|
28
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, afterEach, RunnerTask } from "vitest";
|
|
2
|
+
|
|
3
|
+
import "./clarityValuesMatchers";
|
|
4
|
+
|
|
5
|
+
function getFullTestName(task: RunnerTask, names: string[]) {
|
|
6
|
+
const fullNames = [task.name, ...names];
|
|
7
|
+
if (task.suite?.name) {
|
|
8
|
+
return getFullTestName(task.suite, fullNames);
|
|
9
|
+
}
|
|
10
|
+
return fullNames;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
The `initBeforeEach` options controls the initialisation of the session.
|
|
15
|
+
If the session is initialised before each test, the reports are collected after each test.
|
|
16
|
+
If the session is not initialised before each test, it'll be initialized in the `beforeAll`, which
|
|
17
|
+
will run for all test file. In that case reports are collected in the after all.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
beforeEach(async (ctx) => {
|
|
21
|
+
const { coverage, initBeforeEach, manifestPath } = global.options.clarinet;
|
|
22
|
+
|
|
23
|
+
if (initBeforeEach) {
|
|
24
|
+
await simnet.initSession(process.cwd(), manifestPath);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (coverage) {
|
|
28
|
+
const suiteTestNames = getFullTestName(ctx.task, []);
|
|
29
|
+
const fullName = [ctx.task.file?.name || "", ...suiteTestNames].join("__");
|
|
30
|
+
simnet.setCurrentTestName(fullName);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(async (ctx) => {
|
|
35
|
+
const { coverage, costs, initBeforeEach, includeBootContracts, bootContractsPath } =
|
|
36
|
+
global.options.clarinet;
|
|
37
|
+
|
|
38
|
+
if (ctx.task.result?.state === "fail") {
|
|
39
|
+
const stackTrace = simnet.getLastContractCallTrace();
|
|
40
|
+
if (stackTrace) {
|
|
41
|
+
console.log(stackTrace);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (initBeforeEach && (coverage || costs)) {
|
|
46
|
+
const report = simnet.collectReport(includeBootContracts || false, bootContractsPath || "");
|
|
47
|
+
if (coverage) coverageReports.push(report.coverage);
|
|
48
|
+
if (costs) costsReports.push(report.costs);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
beforeAll(async () => {
|
|
53
|
+
const { initBeforeEach, manifestPath } = global.options.clarinet;
|
|
54
|
+
|
|
55
|
+
if (!initBeforeEach) {
|
|
56
|
+
await simnet.initSession(process.cwd(), manifestPath);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterAll(() => {
|
|
61
|
+
const { coverage, costs, initBeforeEach, includeBootContracts, bootContractsPath } =
|
|
62
|
+
global.options.clarinet;
|
|
63
|
+
|
|
64
|
+
if (!initBeforeEach && (coverage || costs)) {
|
|
65
|
+
const report = simnet.collectReport(includeBootContracts || false, bootContractsPath || "");
|
|
66
|
+
if (coverage) coverageReports.push(report.coverage);
|
|
67
|
+
if (costs) costsReports.push(report.costs);
|
|
68
|
+
}
|
|
69
|
+
});
|