@cronn/lib-file-snapshots 0.1.0 → 0.2.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.
- package/dist/index.d.ts +117 -7
- package/dist/index.js +263 -129
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,125 @@
|
|
|
1
|
-
interface
|
|
2
|
-
|
|
1
|
+
interface SnapshotSerializer {
|
|
2
|
+
/**
|
|
3
|
+
* Returns true when value can be serialized
|
|
4
|
+
*
|
|
5
|
+
* @param value The value to be serialized
|
|
6
|
+
*/
|
|
7
|
+
canSerialize(value: unknown): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Serializes value
|
|
10
|
+
*
|
|
11
|
+
* @param value The value to be serialized
|
|
12
|
+
* @throws {Error} Will throw an error if value cannot be serialized.
|
|
13
|
+
*/
|
|
14
|
+
serialize(value: unknown): SnapshotSerializerResult;
|
|
15
|
+
}
|
|
16
|
+
interface SnapshotSerializerResult {
|
|
17
|
+
/**
|
|
18
|
+
* The serialized value
|
|
19
|
+
*/
|
|
20
|
+
serializedValue: string;
|
|
21
|
+
/**
|
|
22
|
+
* The file extension associated with the serialized value
|
|
23
|
+
*/
|
|
3
24
|
fileExtension: string;
|
|
4
25
|
}
|
|
5
26
|
|
|
27
|
+
declare class CompositeSerializer implements SnapshotSerializer {
|
|
28
|
+
private readonly serializers;
|
|
29
|
+
constructor(serializers: SnapshotSerializer[]);
|
|
30
|
+
canSerialize(value: unknown): boolean;
|
|
31
|
+
serialize(value: unknown): SnapshotSerializerResult;
|
|
32
|
+
}
|
|
33
|
+
|
|
6
34
|
interface JsonSerializerOptions {
|
|
7
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Serializes `undefined` properties in objects. By default, they are omitted.
|
|
37
|
+
*
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
includeUndefinedObjectProperties?: boolean;
|
|
41
|
+
}
|
|
42
|
+
declare class JsonSerializer implements SnapshotSerializer {
|
|
43
|
+
private readonly includeUndefinedObjectProperties;
|
|
44
|
+
constructor(options?: JsonSerializerOptions);
|
|
45
|
+
canSerialize(value: unknown): boolean;
|
|
46
|
+
serialize(value: unknown): SnapshotSerializerResult;
|
|
47
|
+
private normalizeValueRecursive;
|
|
48
|
+
private normalizeValue;
|
|
49
|
+
private normalizeNumber;
|
|
50
|
+
private normalizeArray;
|
|
51
|
+
private normalizeObject;
|
|
52
|
+
private normalizeDate;
|
|
53
|
+
private normalizePlainObject;
|
|
54
|
+
private normalizeMap;
|
|
55
|
+
private serializedValue;
|
|
56
|
+
private assertKeyType;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare class TextSerializer implements SnapshotSerializer {
|
|
60
|
+
canSerialize(value: unknown): value is string;
|
|
61
|
+
serialize(value: unknown): SnapshotSerializerResult;
|
|
62
|
+
private normalizeValue;
|
|
8
63
|
}
|
|
9
|
-
declare function serializeAsJson(value: unknown, options: JsonSerializerOptions): SerializerResult;
|
|
10
64
|
|
|
11
|
-
|
|
65
|
+
interface ValidationFileMatcherConfig {
|
|
66
|
+
/**
|
|
67
|
+
* Base directory for tests
|
|
68
|
+
*
|
|
69
|
+
* The paths of snapshot files will be relative to this directory.
|
|
70
|
+
* @default "."
|
|
71
|
+
*/
|
|
72
|
+
baseDir?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Directory in which golden masters are stored
|
|
75
|
+
*
|
|
76
|
+
* @default "data/test/validation"
|
|
77
|
+
*/
|
|
78
|
+
validationDir?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Directory in which file snapshots from test runs are stored
|
|
81
|
+
*
|
|
82
|
+
* @default "data/test/output"
|
|
83
|
+
*/
|
|
84
|
+
outputDir?: string;
|
|
85
|
+
}
|
|
86
|
+
interface MatchValidationFileOptions {
|
|
87
|
+
/**
|
|
88
|
+
* The full name of the test, including the names of nested describe blocks
|
|
89
|
+
*
|
|
90
|
+
* @example ["test feature", "when x, then y"]
|
|
91
|
+
*/
|
|
92
|
+
testName: string[];
|
|
93
|
+
/**
|
|
94
|
+
* The directory in which the current test is located
|
|
95
|
+
*/
|
|
96
|
+
testDir: string;
|
|
97
|
+
/**
|
|
98
|
+
* Appends `fileSuffix` to the generated snapshot file
|
|
99
|
+
*
|
|
100
|
+
* Should be used whenever having multiple snapshot assertions in a single `test`.
|
|
101
|
+
*/
|
|
102
|
+
fileSuffix?: string;
|
|
103
|
+
/**
|
|
104
|
+
* The serializer to use for the snapshot
|
|
105
|
+
*/
|
|
106
|
+
serializer: SnapshotSerializer;
|
|
107
|
+
}
|
|
108
|
+
interface ValidationFileMatcherResult {
|
|
109
|
+
actual: string;
|
|
110
|
+
expected: string;
|
|
111
|
+
actualFile: string;
|
|
112
|
+
validationFile: string;
|
|
113
|
+
}
|
|
12
114
|
|
|
13
|
-
declare
|
|
115
|
+
declare class ValidationFileMatcher {
|
|
116
|
+
private readonly baseDir;
|
|
117
|
+
private readonly validationDir;
|
|
118
|
+
private readonly outputDir;
|
|
119
|
+
constructor(config?: ValidationFileMatcherConfig);
|
|
120
|
+
matchFileSnapshot(actual: unknown, options: MatchValidationFileOptions): ValidationFileMatcherResult;
|
|
121
|
+
private buildValidationFilePath;
|
|
122
|
+
private writeFileSnapshots;
|
|
123
|
+
}
|
|
14
124
|
|
|
15
|
-
export {
|
|
125
|
+
export { CompositeSerializer, JsonSerializer, TextSerializer, ValidationFileMatcher };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/serializers/composite-serializer.ts
|
|
2
|
+
var CompositeSerializer = class {
|
|
3
|
+
serializers;
|
|
4
|
+
constructor(serializers) {
|
|
5
|
+
this.serializers = serializers;
|
|
6
|
+
}
|
|
7
|
+
canSerialize(value) {
|
|
8
|
+
return this.serializers.some(
|
|
9
|
+
(serializer) => serializer.canSerialize(value)
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
serialize(value) {
|
|
13
|
+
for (const serializer of this.serializers) {
|
|
14
|
+
if (serializer.canSerialize(value)) {
|
|
15
|
+
return serializer.serialize(value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
throw new Error(`Missing serializer for value of type ${typeof value}.`);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/utils/guards.ts
|
|
2
23
|
function isArray(value) {
|
|
3
24
|
return Array.isArray(value);
|
|
4
25
|
}
|
|
@@ -13,157 +34,270 @@ function isPlainObject(value) {
|
|
|
13
34
|
return proto === null || proto === Object.prototype;
|
|
14
35
|
}
|
|
15
36
|
|
|
16
|
-
// src/json-serializer.ts
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
value: serializedValue,
|
|
22
|
-
fileExtension: "json"
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function normalizeValueRecursive(value, options) {
|
|
26
|
-
if (isArray(value)) {
|
|
27
|
-
return normalizeArray(value, options);
|
|
37
|
+
// src/serializers/json-serializer.ts
|
|
38
|
+
var JsonSerializer = class {
|
|
39
|
+
includeUndefinedObjectProperties;
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
this.includeUndefinedObjectProperties = options.includeUndefinedObjectProperties ?? false;
|
|
28
42
|
}
|
|
29
|
-
|
|
30
|
-
return
|
|
43
|
+
canSerialize(value) {
|
|
44
|
+
return true;
|
|
31
45
|
}
|
|
32
|
-
|
|
33
|
-
|
|
46
|
+
serialize(value) {
|
|
47
|
+
const jsonValue = isPlainObject(value) || isArray(value) ? this.normalizeValueRecursive(value) : this.normalizeValue(value);
|
|
48
|
+
const serializedValue = JSON.stringify(jsonValue, void 0, 2);
|
|
49
|
+
return {
|
|
50
|
+
serializedValue,
|
|
51
|
+
fileExtension: "json"
|
|
52
|
+
};
|
|
34
53
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return
|
|
54
|
+
normalizeValueRecursive(value) {
|
|
55
|
+
if (isArray(value)) {
|
|
56
|
+
return this.normalizeArray(value);
|
|
57
|
+
}
|
|
58
|
+
if (isPlainObject(value)) {
|
|
59
|
+
return this.normalizePlainObject(value);
|
|
60
|
+
}
|
|
61
|
+
if (typeof value === "string" || typeof value === "boolean" || value === null) {
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
return this.normalizeValue(value);
|
|
46
65
|
}
|
|
47
|
-
|
|
48
|
-
|
|
66
|
+
normalizeValue(value) {
|
|
67
|
+
if (value === void 0) {
|
|
68
|
+
return this.serializedValue("undefined");
|
|
69
|
+
}
|
|
70
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
71
|
+
return this.serializedValue(typeof value, { value });
|
|
72
|
+
}
|
|
73
|
+
if (typeof value === "number") {
|
|
74
|
+
return this.normalizeNumber(value);
|
|
75
|
+
}
|
|
76
|
+
if (typeof value === "bigint") {
|
|
77
|
+
return this.serializedValue("bigint", { value: value.toString() });
|
|
78
|
+
}
|
|
79
|
+
if (typeof value === "symbol") {
|
|
80
|
+
return this.serializedValue("symbol", { description: value.description });
|
|
81
|
+
}
|
|
82
|
+
if (typeof value === "function") {
|
|
83
|
+
return this.serializedValue("function", { name: value.name });
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === "object") {
|
|
86
|
+
return this.normalizeObject(value);
|
|
87
|
+
}
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Missing JSON normalization for value of type ${typeof value}.`
|
|
90
|
+
);
|
|
49
91
|
}
|
|
50
|
-
|
|
51
|
-
|
|
92
|
+
normalizeNumber(value) {
|
|
93
|
+
if (Number.isNaN(value)) {
|
|
94
|
+
return this.serializedValue("number", { value: "Number.NaN" });
|
|
95
|
+
}
|
|
96
|
+
switch (value) {
|
|
97
|
+
case Number.MIN_VALUE:
|
|
98
|
+
return this.serializedValue("number", { value: "Number.MIN_VALUE" });
|
|
99
|
+
case Number.MAX_VALUE:
|
|
100
|
+
return this.serializedValue("number", { value: "Number.MAX_VALUE" });
|
|
101
|
+
case Number.MIN_SAFE_INTEGER:
|
|
102
|
+
return this.serializedValue("number", {
|
|
103
|
+
value: "Number.MIN_SAFE_INTEGER"
|
|
104
|
+
});
|
|
105
|
+
case Number.MAX_SAFE_INTEGER:
|
|
106
|
+
return this.serializedValue("number", {
|
|
107
|
+
value: "Number.MAX_SAFE_INTEGER"
|
|
108
|
+
});
|
|
109
|
+
case Number.NEGATIVE_INFINITY:
|
|
110
|
+
return this.serializedValue("number", {
|
|
111
|
+
value: "Number.NEGATIVE_INFINITY"
|
|
112
|
+
});
|
|
113
|
+
case Number.POSITIVE_INFINITY:
|
|
114
|
+
return this.serializedValue("number", {
|
|
115
|
+
value: "Number.POSITIVE_INFINITY"
|
|
116
|
+
});
|
|
117
|
+
default:
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
52
120
|
}
|
|
53
|
-
|
|
54
|
-
return
|
|
121
|
+
normalizeArray(value) {
|
|
122
|
+
return value.map((item) => this.normalizeValueRecursive(item));
|
|
55
123
|
}
|
|
56
|
-
|
|
57
|
-
|
|
124
|
+
normalizeObject(value) {
|
|
125
|
+
if (value instanceof Date) {
|
|
126
|
+
return this.normalizeDate(value);
|
|
127
|
+
}
|
|
128
|
+
if (value instanceof Promise) {
|
|
129
|
+
return this.serializedValue("Promise");
|
|
130
|
+
}
|
|
131
|
+
if (value instanceof Set) {
|
|
132
|
+
return this.serializedValue("Set", {
|
|
133
|
+
values: this.normalizeArray(Array.from(value.values()))
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (value instanceof Map) {
|
|
137
|
+
const mapAsObject = this.normalizeMap(value);
|
|
138
|
+
return this.serializedValue("Map", {
|
|
139
|
+
values: this.normalizePlainObject(mapAsObject)
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Missing JSON normalization for object of type ${Object.getPrototypeOf(value)}`
|
|
144
|
+
);
|
|
58
145
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (Number.isNaN(value)) {
|
|
65
|
-
return normalizedValue("number", { value: "Number.NaN" });
|
|
66
|
-
}
|
|
67
|
-
switch (value) {
|
|
68
|
-
case Number.MIN_VALUE:
|
|
69
|
-
return normalizedValue("number", { value: "Number.MIN_VALUE" });
|
|
70
|
-
case Number.MAX_VALUE:
|
|
71
|
-
return normalizedValue("number", { value: "Number.MAX_VALUE" });
|
|
72
|
-
case Number.MIN_SAFE_INTEGER:
|
|
73
|
-
return normalizedValue("number", { value: "Number.MIN_SAFE_INTEGER" });
|
|
74
|
-
case Number.MAX_SAFE_INTEGER:
|
|
75
|
-
return normalizedValue("number", { value: "Number.MAX_SAFE_INTEGER" });
|
|
76
|
-
case Number.NEGATIVE_INFINITY:
|
|
77
|
-
return normalizedValue("number", { value: "Number.NEGATIVE_INFINITY" });
|
|
78
|
-
case Number.POSITIVE_INFINITY:
|
|
79
|
-
return normalizedValue("number", { value: "Number.POSITIVE_INFINITY" });
|
|
80
|
-
default:
|
|
81
|
-
return value;
|
|
146
|
+
normalizeDate(value) {
|
|
147
|
+
if (Number.isNaN(value.getTime())) {
|
|
148
|
+
return this.serializedValue("Date", { value: "Invalid date" });
|
|
149
|
+
}
|
|
150
|
+
return this.serializedValue("Date", { value: value.toISOString() });
|
|
82
151
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
152
|
+
normalizePlainObject(value) {
|
|
153
|
+
const normalizedObject = {};
|
|
154
|
+
for (const [key, propertyValue] of Object.entries(value)) {
|
|
155
|
+
if (propertyValue === void 0 && !this.includeUndefinedObjectProperties) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
this.assertKeyType(key);
|
|
159
|
+
normalizedObject[key] = this.normalizeValueRecursive(propertyValue);
|
|
160
|
+
}
|
|
161
|
+
return normalizedObject;
|
|
90
162
|
}
|
|
91
|
-
|
|
92
|
-
return
|
|
163
|
+
normalizeMap(value) {
|
|
164
|
+
return Array.from(value.entries()).reduce(
|
|
165
|
+
(object, [key, value2]) => {
|
|
166
|
+
this.assertKeyType(key);
|
|
167
|
+
object[key] = value2;
|
|
168
|
+
return object;
|
|
169
|
+
},
|
|
170
|
+
{}
|
|
171
|
+
);
|
|
93
172
|
}
|
|
94
|
-
|
|
95
|
-
return
|
|
96
|
-
values: normalizeArray(Array.from(value.values()), options)
|
|
97
|
-
});
|
|
173
|
+
serializedValue(type, additionalProps = {}) {
|
|
174
|
+
return { $type: type, ...additionalProps };
|
|
98
175
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
});
|
|
176
|
+
assertKeyType(key) {
|
|
177
|
+
if (!(typeof key === "string")) {
|
|
178
|
+
throw new Error(`Key of type ${typeof key} cannot be normalized.`);
|
|
179
|
+
}
|
|
104
180
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return normalizedValue("Date", { value: "Invalid date" });
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// src/serializers/text-serializer.ts
|
|
184
|
+
var TextSerializer = class {
|
|
185
|
+
canSerialize(value) {
|
|
186
|
+
return typeof value === "string";
|
|
112
187
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (propertyValue === void 0 && !options.includeUndefinedObjectProperties) {
|
|
119
|
-
continue;
|
|
188
|
+
serialize(value) {
|
|
189
|
+
if (!this.canSerialize(value)) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`Missing text serialization for value of type ${typeof value}.`
|
|
192
|
+
);
|
|
120
193
|
}
|
|
121
|
-
|
|
122
|
-
|
|
194
|
+
return {
|
|
195
|
+
serializedValue: this.normalizeValue(value),
|
|
196
|
+
fileExtension: "txt"
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
normalizeValue(value) {
|
|
200
|
+
return value.trim();
|
|
123
201
|
}
|
|
124
|
-
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/matcher/validation-file-matcher.ts
|
|
205
|
+
import * as fs2 from "fs";
|
|
206
|
+
import * as path from "path";
|
|
207
|
+
import "vitest";
|
|
208
|
+
|
|
209
|
+
// src/utils/file.ts
|
|
210
|
+
import fs from "fs";
|
|
211
|
+
var NEW_LINE_SEPARATOR = "\n";
|
|
212
|
+
function normalizeFileName(testName) {
|
|
213
|
+
return testName.replaceAll(/\+0/g, "0").replaceAll(/'([\w ]+)'/g, "$1").replaceAll(/[ .:']/g, "_").replaceAll(/,/g, "");
|
|
125
214
|
}
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
(object, [key, value2]) => {
|
|
129
|
-
assertKeyType(key);
|
|
130
|
-
object[key] = value2;
|
|
131
|
-
return object;
|
|
132
|
-
},
|
|
133
|
-
{}
|
|
134
|
-
);
|
|
215
|
+
function mkdirRecursive(path2) {
|
|
216
|
+
fs.mkdirSync(path2, { recursive: true });
|
|
135
217
|
}
|
|
136
|
-
function
|
|
137
|
-
return {
|
|
218
|
+
function readSnapshotFile(path2) {
|
|
219
|
+
return fs.readFileSync(path2, { encoding: "utf8" });
|
|
138
220
|
}
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
|
|
221
|
+
function writeSnapshotFile(file, data, markAsMissing = false) {
|
|
222
|
+
let finalizedData = addTrailingNewLine(data);
|
|
223
|
+
if (markAsMissing) {
|
|
224
|
+
finalizedData = addMissingFileMarker(finalizedData);
|
|
142
225
|
}
|
|
226
|
+
fs.writeFileSync(file, finalizedData, { encoding: "utf8" });
|
|
143
227
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
function serializeAsText(value) {
|
|
147
|
-
return {
|
|
148
|
-
value: normalizeValue2(value),
|
|
149
|
-
fileExtension: "txt"
|
|
150
|
-
};
|
|
228
|
+
function addTrailingNewLine(data) {
|
|
229
|
+
return `${data}${NEW_LINE_SEPARATOR}`;
|
|
151
230
|
}
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
return value.trim();
|
|
155
|
-
}
|
|
156
|
-
throw new Error(
|
|
157
|
-
`Missing text normalization for value of type ${typeof value}.`
|
|
158
|
-
);
|
|
231
|
+
function addMissingFileMarker(data) {
|
|
232
|
+
return `===== missing file =====${NEW_LINE_SEPARATOR}${data}`;
|
|
159
233
|
}
|
|
160
234
|
|
|
161
|
-
// src/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
235
|
+
// src/matcher/validation-file-matcher.ts
|
|
236
|
+
var ValidationFileMatcher = class {
|
|
237
|
+
baseDir;
|
|
238
|
+
validationDir;
|
|
239
|
+
outputDir;
|
|
240
|
+
constructor(config = {}) {
|
|
241
|
+
this.baseDir = config.baseDir ?? ".";
|
|
242
|
+
this.validationDir = config.validationDir ?? "data/test/validation";
|
|
243
|
+
this.outputDir = config.outputDir ?? "data/test/output";
|
|
244
|
+
}
|
|
245
|
+
matchFileSnapshot(actual, options) {
|
|
246
|
+
const { testName, testDir, fileSuffix, serializer } = options;
|
|
247
|
+
if (!serializer.canSerialize(actual)) {
|
|
248
|
+
throw new Error(`Cannot serialize value of type ${typeof actual}`);
|
|
249
|
+
}
|
|
250
|
+
const serializerResult = serializer.serialize(actual);
|
|
251
|
+
const validationFilePath = this.buildValidationFilePath({
|
|
252
|
+
testName,
|
|
253
|
+
testDir,
|
|
254
|
+
fileSuffix,
|
|
255
|
+
fileExtension: serializerResult.fileExtension
|
|
256
|
+
});
|
|
257
|
+
return this.writeFileSnapshots(
|
|
258
|
+
serializerResult.serializedValue,
|
|
259
|
+
validationFilePath
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
buildValidationFilePath(options) {
|
|
263
|
+
const { testName, testDir, fileSuffix, fileExtension } = options;
|
|
264
|
+
const normalizedTestNames = testName.map(normalizeFileName);
|
|
265
|
+
const normalizedFileSuffix = fileSuffix !== void 0 ? `_${normalizeFileName(fileSuffix)}` : "";
|
|
266
|
+
const normalizedTestName = normalizedTestNames.pop();
|
|
267
|
+
const absoluteValidationFilePath = path.join(
|
|
268
|
+
testDir,
|
|
269
|
+
...normalizedTestNames
|
|
270
|
+
);
|
|
271
|
+
const relativeValidationFilePath = path.relative(
|
|
272
|
+
this.baseDir,
|
|
273
|
+
absoluteValidationFilePath
|
|
274
|
+
);
|
|
275
|
+
const validationFileName = `${normalizedTestName}${normalizedFileSuffix}.${fileExtension}`;
|
|
276
|
+
return path.join(relativeValidationFilePath, validationFileName);
|
|
277
|
+
}
|
|
278
|
+
writeFileSnapshots(actual, validationFilePath) {
|
|
279
|
+
const validationFileDir = path.dirname(validationFilePath);
|
|
280
|
+
const testOutputDir = `${this.outputDir}/${validationFileDir}`;
|
|
281
|
+
const actualFile = `${this.outputDir}/${validationFilePath}`;
|
|
282
|
+
const testValidationDir = `${this.validationDir}/${validationFileDir}`;
|
|
283
|
+
const validationFile = `${this.validationDir}/${validationFilePath}`;
|
|
284
|
+
mkdirRecursive(testOutputDir);
|
|
285
|
+
mkdirRecursive(testValidationDir);
|
|
286
|
+
if (!fs2.existsSync(validationFile)) {
|
|
287
|
+
writeSnapshotFile(validationFile, actual, true);
|
|
288
|
+
}
|
|
289
|
+
writeSnapshotFile(actualFile, actual);
|
|
290
|
+
return {
|
|
291
|
+
actual: readSnapshotFile(actualFile),
|
|
292
|
+
expected: readSnapshotFile(validationFile),
|
|
293
|
+
actualFile,
|
|
294
|
+
validationFile
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
};
|
|
165
298
|
export {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
299
|
+
CompositeSerializer,
|
|
300
|
+
JsonSerializer,
|
|
301
|
+
TextSerializer,
|
|
302
|
+
ValidationFileMatcher
|
|
169
303
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cronn/lib-file-snapshots",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The library agnostic core for testing file snapshots",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"file snapshots"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
],
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@biomejs/biome": "1.9.4",
|
|
36
|
-
"@types/node": "22.15.
|
|
36
|
+
"@types/node": "22.15.29",
|
|
37
37
|
"@vitest/coverage-istanbul": "3.1.4",
|
|
38
38
|
"tsup": "8.5.0",
|
|
39
39
|
"typescript": "5.8.3",
|
|
@@ -47,6 +47,6 @@
|
|
|
47
47
|
"test:coverage": "vitest run --coverage",
|
|
48
48
|
"compile": "tsc",
|
|
49
49
|
"build": "tsup",
|
|
50
|
-
"ci": "biome ci . && pnpm run compile
|
|
50
|
+
"ci": "biome ci . && pnpm run compile && pnpm run test:coverage && pnpm run build"
|
|
51
51
|
}
|
|
52
52
|
}
|