@udt/parser-utils 0.1.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/CHANGELOG.md +13 -0
- package/README.md +78 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +123 -0
- package/coverage/coverage-final.json +5 -0
- package/coverage/extractProperties.ts.html +277 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +161 -0
- package/coverage/index.ts.html +94 -0
- package/coverage/isJsonObject.ts.html +142 -0
- package/coverage/parseData.ts.html +898 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/dist/extractProperties.d.ts +30 -0
- package/dist/extractProperties.js +36 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/isJsonObject.d.ts +16 -0
- package/dist/isJsonObject.js +10 -0
- package/dist/parseData.d.ts +159 -0
- package/dist/parseData.js +87 -0
- package/package.json +29 -0
- package/src/extractProperties.test.ts +33 -0
- package/src/extractProperties.ts +64 -0
- package/src/index.ts +3 -0
- package/src/isJsonObject.test.ts +32 -0
- package/src/isJsonObject.ts +19 -0
- package/src/parseData.test.ts +438 -0
- package/src/parseData.ts +271 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +6 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type PlainObject } from "./isJsonObject.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts specfied properties and their values from
|
|
5
|
+
* an object.
|
|
6
|
+
*
|
|
7
|
+
* A bit like destructuring, except that you can also use
|
|
8
|
+
* regular expressions to match the properties you want to
|
|
9
|
+
* extract.
|
|
10
|
+
*
|
|
11
|
+
* @param object The plain object from which to extract
|
|
12
|
+
* properties.
|
|
13
|
+
* @param propsToExtract An array of names, and/or
|
|
14
|
+
* regular expressions that match the
|
|
15
|
+
* names of the properties to exract.
|
|
16
|
+
* @returns The object containing the extracted properties
|
|
17
|
+
* and their values, and an array of all property
|
|
18
|
+
* names of the input object that were not extracted.
|
|
19
|
+
*/
|
|
20
|
+
export function extractProperties(
|
|
21
|
+
object: PlainObject,
|
|
22
|
+
propsToExtract: (string | RegExp)[]
|
|
23
|
+
): {
|
|
24
|
+
/**
|
|
25
|
+
* Object containg the extract properties
|
|
26
|
+
* and their respective values.
|
|
27
|
+
*/
|
|
28
|
+
extracted: PlainObject;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Array of property names of the input
|
|
32
|
+
* object that were not extracted.
|
|
33
|
+
*/
|
|
34
|
+
remainingProps: string[];
|
|
35
|
+
} {
|
|
36
|
+
const propNamesToExtract = propsToExtract.filter(
|
|
37
|
+
(prop) => typeof prop === "string"
|
|
38
|
+
);
|
|
39
|
+
const propRegexesToExtract = propsToExtract.filter(
|
|
40
|
+
(prop) => prop instanceof RegExp
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const extracted: PlainObject = {};
|
|
44
|
+
const remainingProps: string[] = [];
|
|
45
|
+
Object.getOwnPropertyNames(object).forEach((prop) => {
|
|
46
|
+
if (
|
|
47
|
+
propNamesToExtract.some(
|
|
48
|
+
(propNameToExtract) => propNameToExtract === prop
|
|
49
|
+
) ||
|
|
50
|
+
propRegexesToExtract.some((propRegexToExtract) =>
|
|
51
|
+
propRegexToExtract.test(prop)
|
|
52
|
+
)
|
|
53
|
+
) {
|
|
54
|
+
extracted[prop] = object[prop];
|
|
55
|
+
} else {
|
|
56
|
+
remainingProps.push(prop);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
extracted,
|
|
62
|
+
remainingProps,
|
|
63
|
+
};
|
|
64
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { isPlainObject } from "./isJsonObject.js";
|
|
3
|
+
|
|
4
|
+
describe("isPlainObject()", () => {
|
|
5
|
+
it("Returns true for a genuine object", () => {
|
|
6
|
+
expect(isPlainObject({ foo: "bar" })).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("Returns false for null", () => {
|
|
10
|
+
expect(isPlainObject(null)).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("Returns false for an array", () => {
|
|
14
|
+
expect(isPlainObject([1, 2, 3])).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("Returns false for undefined", () => {
|
|
18
|
+
expect(isPlainObject(undefined)).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("Returns false for a function", () => {
|
|
22
|
+
expect(isPlainObject(function () {})).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("Returns false for a boolean", () => {
|
|
26
|
+
expect(isPlainObject(true)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("Returns false for a number", () => {
|
|
30
|
+
expect(isPlainObject(42)).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A plain object.
|
|
3
|
+
*
|
|
4
|
+
* I.e. `{ ... }`, and not an array or `null`, which
|
|
5
|
+
* JavaScript's `typeof` operator would also return
|
|
6
|
+
* `"object"` for.
|
|
7
|
+
*/
|
|
8
|
+
export type PlainObject = Record<string, unknown>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks whether a value is a plain object.
|
|
12
|
+
*
|
|
13
|
+
* @param value The value to check.
|
|
14
|
+
*
|
|
15
|
+
* @returns `true` if it is a plain object, `false` otherwise.
|
|
16
|
+
*/
|
|
17
|
+
export function isPlainObject(value: unknown): value is PlainObject {
|
|
18
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
|
+
}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
parseData,
|
|
4
|
+
type ParseDesignTokenDataFn,
|
|
5
|
+
type ParseGroupDataFn,
|
|
6
|
+
type IsDesignTokenDataFn,
|
|
7
|
+
type ParserConfig,
|
|
8
|
+
InvalidDataError,
|
|
9
|
+
AddChildFn,
|
|
10
|
+
} from "./parseData.js";
|
|
11
|
+
|
|
12
|
+
interface TestGroup {
|
|
13
|
+
type: "group";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface TestDesignToken {
|
|
17
|
+
type: "token";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface TestParentContext {
|
|
21
|
+
dummyData?: number;
|
|
22
|
+
|
|
23
|
+
// Used to pass in a mock addChild() function for
|
|
24
|
+
// mockParseGroupData() to use for testing
|
|
25
|
+
addChild?: AddChildFn<TestGroup, TestDesignToken>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ParseDataCall {
|
|
29
|
+
type: "group" | "token";
|
|
30
|
+
path: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface AddChildCall {
|
|
34
|
+
type: "addChild";
|
|
35
|
+
name: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Used to track the order in which parseXyzData and addChild
|
|
39
|
+
// functions are called
|
|
40
|
+
let parseDataCalls: (ParseDataCall | AddChildCall)[] = [];
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* For the purpose of these tests, any object with a
|
|
44
|
+
* value property will be considered a design token.
|
|
45
|
+
*
|
|
46
|
+
* Intentionally not using (current) DTCG syntax, to
|
|
47
|
+
* demonstrate that parseData() could be used to parse
|
|
48
|
+
* other formats too, as long as they use the same
|
|
49
|
+
* nested object structure for groups and tokens.
|
|
50
|
+
*/
|
|
51
|
+
const mockIsDesignTokenData = vi.fn<IsDesignTokenDataFn>(
|
|
52
|
+
(data) => data.value !== undefined
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const mockParseGroupData = vi.fn<
|
|
56
|
+
ParseGroupDataFn<TestGroup, TestDesignToken, TestParentContext>
|
|
57
|
+
>((_data, path, contextFromParent) => {
|
|
58
|
+
parseDataCalls.push({
|
|
59
|
+
type: "group",
|
|
60
|
+
path,
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
group: {
|
|
64
|
+
type: "group",
|
|
65
|
+
},
|
|
66
|
+
// pass through contextFromParent
|
|
67
|
+
contextForChildren: contextFromParent,
|
|
68
|
+
|
|
69
|
+
// pass through addChild
|
|
70
|
+
addChild: contextFromParent?.addChild,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const mockParseDesignTokenData = vi.fn<
|
|
75
|
+
ParseDesignTokenDataFn<TestDesignToken, TestParentContext>
|
|
76
|
+
>((_data, path, _contextFromParent) => {
|
|
77
|
+
parseDataCalls.push({ type: "token", path });
|
|
78
|
+
const result: TestDesignToken = {
|
|
79
|
+
type: "token",
|
|
80
|
+
};
|
|
81
|
+
return result;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("parseData()", () => {
|
|
85
|
+
const parserConfig: ParserConfig<
|
|
86
|
+
TestDesignToken,
|
|
87
|
+
TestGroup,
|
|
88
|
+
TestParentContext
|
|
89
|
+
> = {
|
|
90
|
+
isDesignTokenData: mockIsDesignTokenData,
|
|
91
|
+
groupPropsToExtract: [],
|
|
92
|
+
parseGroupData: mockParseGroupData,
|
|
93
|
+
parseDesignTokenData: mockParseDesignTokenData,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
beforeEach(() => {
|
|
97
|
+
// Reset stuff
|
|
98
|
+
parseDataCalls = [];
|
|
99
|
+
parserConfig.groupPropsToExtract = [];
|
|
100
|
+
mockIsDesignTokenData.mockClear();
|
|
101
|
+
mockParseGroupData.mockClear();
|
|
102
|
+
mockParseDesignTokenData.mockClear();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("parsing an empty group object", () => {
|
|
106
|
+
let parsedGroupOrToken: TestGroup | TestDesignToken;
|
|
107
|
+
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
parsedGroupOrToken = parseData({}, parserConfig);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("returns a group", () => {
|
|
113
|
+
expect(parsedGroupOrToken.type).toBe("group");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("calls isDesignTokenData function once", () => {
|
|
117
|
+
expect(mockIsDesignTokenData).toHaveBeenCalledOnce();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("class isDesignTokenData function with input data", () => {
|
|
121
|
+
expect(mockIsDesignTokenData).toHaveBeenCalledWith({});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("calls parseGroupData function once", () => {
|
|
125
|
+
expect(mockParseGroupData).toHaveBeenCalledOnce();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("calls parseGroupData function with empty group and empty path array", () => {
|
|
129
|
+
expect(mockParseGroupData).toHaveBeenCalledWith({}, [], undefined);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("does not call parseDesignTokenData function", () => {
|
|
133
|
+
expect(mockParseDesignTokenData).not.toHaveBeenCalled();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe("parsing a design token object", () => {
|
|
138
|
+
const testTokenData = {
|
|
139
|
+
value: "whatever", // <-- this makes it a token
|
|
140
|
+
other: "thing",
|
|
141
|
+
stuff: 123,
|
|
142
|
+
notAGroup: {},
|
|
143
|
+
};
|
|
144
|
+
let parsedGroupOrToken: TestGroup | TestDesignToken;
|
|
145
|
+
|
|
146
|
+
beforeEach(() => {
|
|
147
|
+
parsedGroupOrToken = parseData(testTokenData, parserConfig);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("returns a design token", () => {
|
|
151
|
+
expect(parsedGroupOrToken.type).toBe("token");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("does not call parseGroupData function", () => {
|
|
155
|
+
expect(mockParseGroupData).not.toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("calls parseDesignTokenData function once", () => {
|
|
159
|
+
expect(mockParseDesignTokenData).toHaveBeenCalledOnce();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("calls parseDesignTokenData function with complete data and empty path array", () => {
|
|
163
|
+
expect(mockParseDesignTokenData).toHaveBeenCalledWith(
|
|
164
|
+
testTokenData,
|
|
165
|
+
[],
|
|
166
|
+
undefined
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe("parsing a group object containing design tokens and nested groups", () => {
|
|
172
|
+
/*
|
|
173
|
+
Contains:
|
|
174
|
+
|
|
175
|
+
- 4 groups: <root>, emptyGroup,
|
|
176
|
+
groupWithTokens, nestedGroup
|
|
177
|
+
|
|
178
|
+
- 5 tokens: nestedToken, anotherNestedToken,
|
|
179
|
+
deeplyNestedToken, token, anotherToken
|
|
180
|
+
*/
|
|
181
|
+
const testData = {
|
|
182
|
+
specialGroupProp: {
|
|
183
|
+
value: "not a token value",
|
|
184
|
+
description: "This is not a group or token!",
|
|
185
|
+
},
|
|
186
|
+
emptyGroup: {},
|
|
187
|
+
groupWithTokens: {
|
|
188
|
+
nestedToken: {
|
|
189
|
+
value: "a",
|
|
190
|
+
},
|
|
191
|
+
anotherNestedToken: {
|
|
192
|
+
value: "b",
|
|
193
|
+
},
|
|
194
|
+
nestedGroup: {
|
|
195
|
+
deeplyNestedToken: {
|
|
196
|
+
value: "x",
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
token: {
|
|
201
|
+
value: 1,
|
|
202
|
+
},
|
|
203
|
+
anotherToken: {
|
|
204
|
+
value: 2,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
beforeEach(() => {
|
|
209
|
+
parserConfig.groupPropsToExtract = ["specialGroupProp"];
|
|
210
|
+
parseData(testData, parserConfig);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("calls parseDesignTokenData function 5 times", () => {
|
|
214
|
+
expect(mockParseDesignTokenData).toHaveBeenCalledTimes(5);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("calls parseGroupData function 4 times", () => {
|
|
218
|
+
expect(mockParseGroupData).toHaveBeenCalledTimes(4);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("only passed extracted group props to the parseGroupData function", () => {
|
|
222
|
+
expect(mockParseGroupData).toHaveBeenNthCalledWith(
|
|
223
|
+
1,
|
|
224
|
+
{ specialGroupProp: testData.specialGroupProp },
|
|
225
|
+
[],
|
|
226
|
+
undefined
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("traverses the data depth-first", () => {
|
|
231
|
+
// Start with root group
|
|
232
|
+
expect(parseDataCalls[0]).toStrictEqual({
|
|
233
|
+
type: "group",
|
|
234
|
+
path: [],
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// "emptyGroup" next
|
|
238
|
+
expect(parseDataCalls[1]).toStrictEqual({
|
|
239
|
+
type: "group",
|
|
240
|
+
path: ["emptyGroup"],
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// "groupWithTokens" next
|
|
244
|
+
expect(parseDataCalls[2]).toStrictEqual({
|
|
245
|
+
type: "group",
|
|
246
|
+
path: ["groupWithTokens"],
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// "nestedToken" next
|
|
250
|
+
expect(parseDataCalls[3]).toStrictEqual({
|
|
251
|
+
type: "token",
|
|
252
|
+
path: ["groupWithTokens", "nestedToken"],
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// "anotherNestedToken" next
|
|
256
|
+
expect(parseDataCalls[4]).toStrictEqual({
|
|
257
|
+
type: "token",
|
|
258
|
+
path: ["groupWithTokens", "anotherNestedToken"],
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// "nestedGroup" next
|
|
262
|
+
expect(parseDataCalls[5]).toStrictEqual({
|
|
263
|
+
type: "group",
|
|
264
|
+
path: ["groupWithTokens", "nestedGroup"],
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// "deeplyNestedToken" next
|
|
268
|
+
expect(parseDataCalls[6]).toStrictEqual({
|
|
269
|
+
type: "token",
|
|
270
|
+
path: ["groupWithTokens", "nestedGroup", "deeplyNestedToken"],
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// "token" next
|
|
274
|
+
expect(parseDataCalls[7]).toStrictEqual({
|
|
275
|
+
type: "token",
|
|
276
|
+
path: ["token"],
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// "anotherToken" next
|
|
280
|
+
expect(parseDataCalls[8]).toStrictEqual({
|
|
281
|
+
type: "token",
|
|
282
|
+
path: ["anotherToken"],
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe("with context data", () => {
|
|
288
|
+
const testData = { group: {}, token: { value: 123 } };
|
|
289
|
+
const testContext: TestParentContext = { dummyData: 42 };
|
|
290
|
+
|
|
291
|
+
beforeEach(() => {
|
|
292
|
+
parseData(testData, parserConfig, testContext);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("passes the context data to outermost parseGroupData call", () => {
|
|
296
|
+
// 1st call is the root group
|
|
297
|
+
expect(mockParseGroupData).toHaveBeenNthCalledWith(
|
|
298
|
+
1,
|
|
299
|
+
{},
|
|
300
|
+
[],
|
|
301
|
+
testContext
|
|
302
|
+
);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it("passes context from parent group to parseGroupData calls for child groups", () => {
|
|
306
|
+
expect(mockParseGroupData).toHaveBeenNthCalledWith(
|
|
307
|
+
2,
|
|
308
|
+
{},
|
|
309
|
+
["group"],
|
|
310
|
+
testContext
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("passes context from parent group to parseDesignTokenData calls for child tokens", () => {
|
|
315
|
+
expect(mockParseDesignTokenData).toHaveBeenCalledWith(
|
|
316
|
+
testData.token,
|
|
317
|
+
["token"],
|
|
318
|
+
testContext
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
describe("using addChild functions", () => {
|
|
324
|
+
const testData = {
|
|
325
|
+
tokenA: { value: 1 },
|
|
326
|
+
tokenB: { value: 2 },
|
|
327
|
+
groupC: { tokenX: { value: 99 }, tokenY: { value: 100 } },
|
|
328
|
+
};
|
|
329
|
+
const mockAddChild = vi.fn<AddChildFn<TestGroup, TestDesignToken>>(
|
|
330
|
+
(name, _child) => {
|
|
331
|
+
parseDataCalls.push({
|
|
332
|
+
type: "addChild",
|
|
333
|
+
name,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
const testContext: TestParentContext = { addChild: mockAddChild };
|
|
338
|
+
|
|
339
|
+
beforeEach(() => {
|
|
340
|
+
mockAddChild.mockClear();
|
|
341
|
+
parseData(testData, parserConfig, testContext);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("calls addChild for every child of every group", () => {
|
|
345
|
+
// Root group contains 3 children: "tokenA", "tokenB" and "groupC"
|
|
346
|
+
// "groupC" contains 2 children: "tokenX" and "tokenY"
|
|
347
|
+
// 3 + 2 = 5
|
|
348
|
+
expect(mockAddChild).toHaveBeenCalledTimes(5);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("adds a nested group to its parent before parsing its children", () => {
|
|
352
|
+
// Steps should be:
|
|
353
|
+
// 0 parse root group
|
|
354
|
+
expect(parseDataCalls[0]).toStrictEqual({ type: "group", path: [] });
|
|
355
|
+
|
|
356
|
+
// 1 parse token "tokenA"
|
|
357
|
+
expect(parseDataCalls[1]).toStrictEqual({
|
|
358
|
+
type: "token",
|
|
359
|
+
path: ["tokenA"],
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// 2 addChild "tokenA" to root group
|
|
363
|
+
expect(parseDataCalls[2]).toStrictEqual({
|
|
364
|
+
type: "addChild",
|
|
365
|
+
name: "tokenA",
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// 3 parse token "tokenB"
|
|
369
|
+
expect(parseDataCalls[3]).toStrictEqual({
|
|
370
|
+
type: "token",
|
|
371
|
+
path: ["tokenB"],
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// 4 addChild "tokenB" to root group
|
|
375
|
+
expect(parseDataCalls[4]).toStrictEqual({
|
|
376
|
+
type: "addChild",
|
|
377
|
+
name: "tokenB",
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// 5 parse group "groupC"
|
|
381
|
+
expect(parseDataCalls[5]).toStrictEqual({
|
|
382
|
+
type: "group",
|
|
383
|
+
path: ["groupC"],
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// 6 addChild "groupC" to root group
|
|
387
|
+
// NOTE that this happens *before* any of groupC's
|
|
388
|
+
// children are parsed.
|
|
389
|
+
expect(parseDataCalls[6]).toStrictEqual({
|
|
390
|
+
type: "addChild",
|
|
391
|
+
name: "groupC",
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// 7 parse token "tokenX"
|
|
395
|
+
expect(parseDataCalls[7]).toStrictEqual({
|
|
396
|
+
type: "token",
|
|
397
|
+
path: ["groupC", "tokenX"],
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// 8 addChild "tokenX" to "groupC"
|
|
401
|
+
expect(parseDataCalls[8]).toStrictEqual({
|
|
402
|
+
type: "addChild",
|
|
403
|
+
name: "tokenX",
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// 9 parse token "tokenY"
|
|
407
|
+
expect(parseDataCalls[9]).toStrictEqual({
|
|
408
|
+
type: "token",
|
|
409
|
+
path: ["groupC", "tokenY"],
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// 10 addChild "tokenY" to "groupC"
|
|
413
|
+
expect(parseDataCalls[10]).toStrictEqual({
|
|
414
|
+
type: "addChild",
|
|
415
|
+
name: "tokenY",
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("throws an InvalidDataError when given a non-object input", () => {
|
|
421
|
+
expect(() => parseData(123, parserConfig)).toThrowError(InvalidDataError);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("throws an InvalidDataError when encountering a non-object child", () => {
|
|
425
|
+
expect(() => parseData({ brokenThing: 123 }, parserConfig)).toThrowError(
|
|
426
|
+
InvalidDataError
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("includes the path and data when throwing an InvalidDataError", () => {
|
|
431
|
+
try {
|
|
432
|
+
parseData({ brokenThing: 123 }, parserConfig);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
expect((error as InvalidDataError).path).toStrictEqual(["brokenThing"]);
|
|
435
|
+
expect((error as InvalidDataError).data).toBe(123);
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
});
|