slimjson 1.0.2 → 1.0.4
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/.claude/settings.local.json +8 -0
- package/README.md +142 -39
- package/README_EN.md +350 -0
- package/compress-test.js +51 -25
- package/compress.js +423 -410
- package/data/searchGroup.json +96365 -0
- package/package.json +1 -1
- package/test.js +473 -384
package/test.js
CHANGED
|
@@ -31,10 +31,6 @@ const src3 = [
|
|
|
31
31
|
{ "姓名": "张三", "年龄": 19, "家人": [{ "姓名": "张四", "年龄": 40 }] },
|
|
32
32
|
{ "姓名": "李小花", "年龄": 28, "家人": [{ "姓名": "李大国", "年龄": 55 }], "伴侣": { "姓名": "赵明", "年龄": 30 } }
|
|
33
33
|
];
|
|
34
|
-
const src3Normalized = [ // 所有对象补齐缺失 key 为 null
|
|
35
|
-
{ "姓名": "张三", "年龄": 19, "家人": [{ "姓名": "张四", "年龄": 40 }], "伴侣": null },
|
|
36
|
-
{ "姓名": "李小花", "年龄": 28, "家人": [{ "姓名": "李大国", "年龄": 55 }], "伴侣": { "姓名": "赵明", "年龄": 30 } }
|
|
37
|
-
];
|
|
38
34
|
|
|
39
35
|
// 场景4:嵌套对象内部子 key 不全(第1个家人中缺少"关系")
|
|
40
36
|
const src4 = [
|
|
@@ -46,15 +42,6 @@ const src4 = [
|
|
|
46
42
|
]
|
|
47
43
|
}
|
|
48
44
|
];
|
|
49
|
-
const src4Normalized = [
|
|
50
|
-
{
|
|
51
|
-
"姓名": "张三", "年龄": 19,
|
|
52
|
-
"家人": [
|
|
53
|
-
{ "姓名": "张四", "年龄": 40, "关系": null },
|
|
54
|
-
{ "姓名": "李五", "年龄": 41, "关系": "母亲" }
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
];
|
|
58
45
|
|
|
59
46
|
// 场景5:复杂嵌套 — 年级,班级,班主任,其他老师,学生(学生含成绩子对象)
|
|
60
47
|
const src5 = [
|
|
@@ -88,7 +75,23 @@ const src5 = [
|
|
|
88
75
|
}
|
|
89
76
|
];
|
|
90
77
|
|
|
91
|
-
|
|
78
|
+
// decompress 始终返回规范化数据(补齐缺失 key 为 null)
|
|
79
|
+
const src3Decompressed = [
|
|
80
|
+
{ "姓名": "张三", "年龄": 19, "家人": [{ "姓名": "张四", "年龄": 40 }], "伴侣": null },
|
|
81
|
+
{ "姓名": "李小花", "年龄": 28, "家人": [{ "姓名": "李大国", "年龄": 55 }], "伴侣": { "姓名": "赵明", "年龄": 30 } }
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const src4Decompressed = [
|
|
85
|
+
{
|
|
86
|
+
"姓名": "张三", "年龄": 19,
|
|
87
|
+
"家人": [
|
|
88
|
+
{ "姓名": "张四", "年龄": 40, "关系": null },
|
|
89
|
+
{ "姓名": "李五", "年龄": 41, "关系": "母亲" }
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const src5Decompressed = [
|
|
92
95
|
{
|
|
93
96
|
"年级": "一年级",
|
|
94
97
|
"班级": "1班",
|
|
@@ -125,322 +128,463 @@ const src5Normalized = [
|
|
|
125
128
|
|
|
126
129
|
describe('compress / decompress', () => {
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
expect(r1.keys).toBeDefined();
|
|
133
|
-
expect(r1.rows).toBeDefined();
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test('与预期 keys 一致', () => {
|
|
137
|
-
expect(r1.keys).toEqual([
|
|
138
|
-
"姓名", "年龄", { "家人": ["姓名", "年龄"] }, { "伴侣": ["姓名", "年龄"] }
|
|
139
|
-
]);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test('还原一致', () => {
|
|
143
|
-
expect(decompress(r1)).toEqual(src1);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe('样例2:原始类型数组字段', () => {
|
|
148
|
-
const r2 = compress(src2);
|
|
149
|
-
|
|
150
|
-
test('与预期 keys 一致', () => {
|
|
151
|
-
expect(r2.keys).toEqual(["姓名", "班级"]);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('与预期 rows 一致', () => {
|
|
155
|
-
expect(r2.rows).toEqual([
|
|
156
|
-
[["张三", "李四", "王五"], "23班"],
|
|
157
|
-
[["李小花", "张晓", "李旺", "张思"], "24班"]
|
|
158
|
-
]);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test('还原一致', () => {
|
|
162
|
-
expect(decompress(r2)).toEqual(src2);
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe('场景3:顶层缺失字段(后端省略 null)', () => {
|
|
167
|
-
const r3 = compress(src3);
|
|
168
|
-
|
|
169
|
-
test('keys 包含伴侣', () => {
|
|
170
|
-
expect(r3.keys).toEqual(["姓名", "年龄", { "家人": ["姓名", "年龄"] }, { "伴侣": ["姓名", "年龄"] }]);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test('row[0] 伴侣位置为 null', () => {
|
|
174
|
-
expect(r3.rows[0][3]).toBeNull();
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test('row[1] 伴侣正常', () => {
|
|
178
|
-
expect(r3.rows[1][3]).toEqual(["赵明", 30]);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test('还原(null 补齐)', () => {
|
|
182
|
-
expect(decompress(r3)).toEqual(src3Normalized);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
test('张三的伴侣为 null', () => {
|
|
186
|
-
expect(decompress(r3)[0].伴侣).toBeNull();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test('李小花的伴侣正常', () => {
|
|
190
|
-
expect(decompress(r3)[1].伴侣.姓名).toBe("赵明");
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe('场景4:嵌套对象子 key 不全', () => {
|
|
195
|
-
const r4 = compress(src4);
|
|
196
|
-
|
|
197
|
-
test('keys 中家人包含"关系"', () => {
|
|
198
|
-
expect(r4.keys).toEqual(["姓名", "年龄", { "家人": ["姓名", "年龄", "关系"] }]);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test('家人[0] 关系为 null', () => {
|
|
202
|
-
expect(r4.rows[0][2][0][2]).toBeNull();
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
test('家人[1] 关系正常', () => {
|
|
206
|
-
expect(r4.rows[0][2][1][2]).toBe("母亲");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test('还原(null 补齐)', () => {
|
|
210
|
-
expect(decompress(r4)).toEqual(src4Normalized);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test('第1个家人关系为 null', () => {
|
|
214
|
-
expect(decompress(r4)[0].家人[0].关系).toBeNull();
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test('第2个家人关系正常', () => {
|
|
218
|
-
expect(decompress(r4)[0].家人[1].关系).toBe("母亲");
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe('场景5:复杂嵌套(年级/班级/班主任/其他老师/学生+成绩)', () => {
|
|
223
|
-
const r5 = compress(src5);
|
|
224
|
-
|
|
225
|
-
// —— keys ——
|
|
226
|
-
test('keys 顶层包含年级/班级', () => {
|
|
227
|
-
expect(r5.keys.slice(0, 2)).toEqual(["年级", "班级"]);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
test('keys 包含嵌套班主任', () => {
|
|
231
|
-
expect(JSON.stringify(r5.keys[2])).toContain('班主任');
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test('keys 包含嵌套其他老师', () => {
|
|
235
|
-
expect(JSON.stringify(r5.keys[3])).toContain('其他老师');
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
test('keys 包含嵌套学生', () => {
|
|
239
|
-
expect(JSON.stringify(r5.keys[4])).toContain('学生');
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test('keys 完整结构', () => {
|
|
243
|
-
expect(r5.keys).toEqual([
|
|
244
|
-
"年级",
|
|
245
|
-
"班级",
|
|
246
|
-
{ "班主任": ["姓名", "年龄", "科目"] },
|
|
247
|
-
{ "其他老师": ["姓名", "年龄", "科目"] },
|
|
248
|
-
{ "学生": ["姓名", "年龄", "性别", { "成绩": ["语文", "数学", "英语"] }] }
|
|
249
|
-
]);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// —— rows 数量 ——
|
|
253
|
-
test('1班班主任正常', () => {
|
|
254
|
-
expect(r5.rows[0][2]).toEqual(["王老师", 35, "语文"]);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
test('1班其他老师数量=2', () => {
|
|
258
|
-
expect(r5.rows[0][3].length).toBe(2);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
test('1班学生数量=2', () => {
|
|
262
|
-
expect(r5.rows[0][4].length).toBe(2);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
test('2班班主任正常', () => {
|
|
266
|
-
expect(r5.rows[1][2]).toEqual(["赵老师", 42, "数学"]);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test('2班其他老师数量=3', () => {
|
|
270
|
-
expect(r5.rows[1][3].length).toBe(3);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test('2班学生数量=3', () => {
|
|
274
|
-
expect(r5.rows[1][4].length).toBe(3);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// —— 成绩子对象缺失 key 补 null ——
|
|
278
|
-
// 学生 keys: ["姓名", "年龄", "性别", { "成绩": ["语文", "数学", "英语"] }]
|
|
279
|
-
// 学生 row: [姓名, 年龄, 性别, [语文, 数学, 英语]]
|
|
280
|
-
test('小刚英语成绩为 null(缺英语字段)', () => {
|
|
281
|
-
expect(r5.rows[1][4][0][3][2]).toBeNull();
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
test('小刚语文成绩正常', () => {
|
|
285
|
-
expect(r5.rows[1][4][0][3][0]).toBe(78);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
test('小强语文成绩为 null(缺语文字段)', () => {
|
|
289
|
-
expect(r5.rows[1][4][2][3][0]).toBeNull();
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
test('小强数学成绩正常', () => {
|
|
293
|
-
expect(r5.rows[1][4][2][3][1]).toBe(60);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
test('小强英语成绩为 null(缺英语字段)', () => {
|
|
297
|
-
expect(r5.rows[1][4][2][3][2]).toBeNull();
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// —— 还原验证 ——
|
|
301
|
-
test('还原后与 normalized 一致', () => {
|
|
302
|
-
expect(decompress(r5)).toEqual(src5Normalized);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test('1班年级正确', () => {
|
|
306
|
-
expect(decompress(r5)[0]["年级"]).toBe("一年级");
|
|
307
|
-
});
|
|
131
|
+
// —— compress / decompress / roundtrip 测试:trim=false 和 trim=true 两套 ——
|
|
132
|
+
[false, true].forEach(trim => {
|
|
133
|
+
const label = trim ? 'trimTrailingNulls' : '默认';
|
|
134
|
+
const copt = trim ? { trimTrailingNulls: true } : undefined;
|
|
308
135
|
|
|
309
|
-
|
|
310
|
-
expect(decompress(r5)[1]["学生"][0]["成绩"]["英语"]).toBeNull();
|
|
311
|
-
});
|
|
136
|
+
describe(`[${label}]`, () => {
|
|
312
137
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
});
|
|
316
|
-
});
|
|
138
|
+
describe('样例1:基础嵌套', () => {
|
|
139
|
+
const r1 = compress(src1, copt);
|
|
317
140
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
141
|
+
test('compress 不出错', () => {
|
|
142
|
+
expect(r1.keys).toBeDefined();
|
|
143
|
+
expect(r1.rows).toBeDefined();
|
|
144
|
+
});
|
|
322
145
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
146
|
+
test('与预期 keys 一致', () => {
|
|
147
|
+
expect(r1.keys).toEqual([
|
|
148
|
+
"姓名", "年龄", { "家人": ["姓名", "年龄"] }, { "伴侣": ["姓名", "年龄"] }
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
326
151
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
152
|
+
test('还原一致', () => {
|
|
153
|
+
expect(decompress(r1)).toEqual(src1);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
330
156
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
});
|
|
157
|
+
describe('样例2:原始类型数组字段', () => {
|
|
158
|
+
const r2 = compress(src2, copt);
|
|
334
159
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
expect(r.keys).toEqual(['name', 'age']);
|
|
339
|
-
expect(r.rows[1]).toEqual([null, null]);
|
|
340
|
-
expect(r.rows[0]).toEqual(['a', 10]);
|
|
341
|
-
expect(r.rows[2]).toEqual(['b', null]);
|
|
342
|
-
});
|
|
160
|
+
test('与预期 keys 一致', () => {
|
|
161
|
+
expect(r2.keys).toEqual(["姓名", "班级"]);
|
|
162
|
+
});
|
|
343
163
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
});
|
|
164
|
+
test('与预期 rows 一致', () => {
|
|
165
|
+
expect(r2.rows).toEqual([
|
|
166
|
+
[["张三", "李四", "王五"], "23班"],
|
|
167
|
+
[["李小花", "张晓", "李旺", "张思"], "24班"]
|
|
168
|
+
]);
|
|
169
|
+
});
|
|
351
170
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
expect(r.keys).toEqual(['name', 'kids']);
|
|
357
|
-
expect(r.rows[0]).toEqual(['a', [null, { name: 'child' }]]);
|
|
358
|
-
expect(decompress(r)).toEqual(src);
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
test('字段值为 undefined 转 null', () => {
|
|
362
|
-
const src = [{ a: undefined, b: 1 }];
|
|
363
|
-
const r = compress(src);
|
|
364
|
-
expect(r.keys).toEqual(['a', 'b']);
|
|
365
|
-
expect(r.rows[0]).toEqual([null, 1]);
|
|
366
|
-
});
|
|
171
|
+
test('还原一致', () => {
|
|
172
|
+
expect(decompress(r2)).toEqual(src2);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
367
175
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const r = compress(src);
|
|
371
|
-
expect(r.rows[0][1]).toBeNull();
|
|
372
|
-
expect(decompress(r)[0].b).toBeNull();
|
|
373
|
-
});
|
|
176
|
+
describe('场景3:顶层缺失字段(后端省略 null)', () => {
|
|
177
|
+
const r3 = compress(src3, copt);
|
|
374
178
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
// 首元素是对象 → object-array;非对象元素在 buildKeys 收集时被跳过
|
|
379
|
-
expect(r.keys).toEqual(['name', { kids: ['name'] }]);
|
|
380
|
-
// 非对象元素 → buildRow 返回 [null],所以 null 元素也是 [null] 而非 null
|
|
381
|
-
expect(r.rows[0][1]).toEqual([['kid'], [null], [null]]);
|
|
382
|
-
});
|
|
179
|
+
test('keys 包含伴侣', () => {
|
|
180
|
+
expect(r3.keys).toEqual(["姓名", "年龄", { "家人": ["姓名", "年龄"] }, { "伴侣": ["姓名", "年龄"] }]);
|
|
181
|
+
});
|
|
383
182
|
|
|
384
|
-
test('
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
expect(r.rows[2]).toEqual([null]);
|
|
392
|
-
expect(r.rows[3]).toEqual(['b']);
|
|
393
|
-
});
|
|
183
|
+
test(`row[0] 伴侣${trim ? '被 trim' : '为 null'}`, () => {
|
|
184
|
+
if (trim) {
|
|
185
|
+
expect(r3.rows[0].length).toBe(3);
|
|
186
|
+
} else {
|
|
187
|
+
expect(r3.rows[0][3]).toBeNull();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
394
190
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
expect(r.keys).toEqual(['name', 'matrix']);
|
|
399
|
-
expect(r.rows[0]).toEqual(['x', [[1, 2], [3, 4]]]);
|
|
400
|
-
expect(decompress(r)).toEqual(src);
|
|
401
|
-
});
|
|
191
|
+
test('row[1] 伴侣正常', () => {
|
|
192
|
+
expect(r3.rows[1][3]).toEqual(["赵明", 30]);
|
|
193
|
+
});
|
|
402
194
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
195
|
+
test('还原(null 补齐)', () => {
|
|
196
|
+
expect(decompress(r3)).toEqual(src3Decompressed);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('张三的伴侣为 null', () => {
|
|
200
|
+
expect(decompress(r3)[0].伴侣).toBeNull();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('李小花的伴侣正常', () => {
|
|
204
|
+
expect(decompress(r3)[1].伴侣.姓名).toBe("赵明");
|
|
205
|
+
});
|
|
206
|
+
});
|
|
410
207
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
208
|
+
describe('场景4:嵌套对象子 key 不全', () => {
|
|
209
|
+
const r4 = compress(src4, copt);
|
|
210
|
+
|
|
211
|
+
test('keys 中家人包含"关系"', () => {
|
|
212
|
+
expect(r4.keys).toEqual(["姓名", "年龄", { "家人": ["姓名", "年龄", "关系"] }]);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test(`家人[0] 关系${trim ? '被 trim' : '为 null'}`, () => {
|
|
216
|
+
if (trim) {
|
|
217
|
+
expect(r4.rows[0][2][0].length).toBe(2);
|
|
218
|
+
} else {
|
|
219
|
+
expect(r4.rows[0][2][0][2]).toBeNull();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('家人[1] 关系正常', () => {
|
|
224
|
+
expect(r4.rows[0][2][1][2]).toBe("母亲");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('还原(null 补齐)', () => {
|
|
228
|
+
expect(decompress(r4)).toEqual(src4Decompressed);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('第1个家人关系为 null', () => {
|
|
232
|
+
expect(decompress(r4)[0].家人[0].关系).toBeNull();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('第2个家人关系正常', () => {
|
|
236
|
+
expect(decompress(r4)[0].家人[1].关系).toBe("母亲");
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('场景5:复杂嵌套(年级/班级/班主任/其他老师/学生+成绩)', () => {
|
|
241
|
+
const r5 = compress(src5, copt);
|
|
242
|
+
|
|
243
|
+
// —— keys ——
|
|
244
|
+
test('keys 顶层包含年级/班级', () => {
|
|
245
|
+
expect(r5.keys.slice(0, 2)).toEqual(["年级", "班级"]);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('keys 包含嵌套班主任', () => {
|
|
249
|
+
expect(JSON.stringify(r5.keys[2])).toContain('班主任');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('keys 包含嵌套其他老师', () => {
|
|
253
|
+
expect(JSON.stringify(r5.keys[3])).toContain('其他老师');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('keys 包含嵌套学生', () => {
|
|
257
|
+
expect(JSON.stringify(r5.keys[4])).toContain('学生');
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('keys 完整结构', () => {
|
|
261
|
+
expect(r5.keys).toEqual([
|
|
262
|
+
"年级",
|
|
263
|
+
"班级",
|
|
264
|
+
{ "班主任": ["姓名", "年龄", "科目"] },
|
|
265
|
+
{ "其他老师": ["姓名", "年龄", "科目"] },
|
|
266
|
+
{ "学生": ["姓名", "年龄", "性别", { "成绩": ["语文", "数学", "英语"] }] }
|
|
267
|
+
]);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// —— rows 数量 ——
|
|
271
|
+
test('1班班主任正常', () => {
|
|
272
|
+
expect(r5.rows[0][2]).toEqual(["王老师", 35, "语文"]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('1班其他老师数量=2', () => {
|
|
276
|
+
expect(r5.rows[0][3].length).toBe(2);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test('1班学生数量=2', () => {
|
|
280
|
+
expect(r5.rows[0][4].length).toBe(2);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test('2班班主任正常', () => {
|
|
284
|
+
expect(r5.rows[1][2]).toEqual(["赵老师", 42, "数学"]);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('2班其他老师数量=3', () => {
|
|
288
|
+
expect(r5.rows[1][3].length).toBe(3);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('2班学生数量=3', () => {
|
|
292
|
+
expect(r5.rows[1][4].length).toBe(3);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// —— 成绩子对象缺失 key 补 null ——
|
|
296
|
+
test('小刚英语成绩为 null(缺英语字段)', () => {
|
|
297
|
+
expect(decompress(r5)[1]["学生"][0]["成绩"]["英语"]).toBeNull();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test('小刚语文成绩正常', () => {
|
|
301
|
+
expect(r5.rows[1][4][0][3][0]).toBe(78);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test('小强语文成绩为 null(缺语文字段)', () => {
|
|
305
|
+
expect(decompress(r5)[1]["学生"][2]["成绩"]["语文"]).toBeNull();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('小强数学成绩正常', () => {
|
|
309
|
+
const scores = r5.rows[1][4][2][3];
|
|
310
|
+
// trim=true → [null, 60],数学在 index 1
|
|
311
|
+
// trim=false → [null, 60, null],数学在 index 1
|
|
312
|
+
expect(scores[1]).toBe(60);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test('小强英语成绩为 null(缺英语字段)', () => {
|
|
316
|
+
expect(decompress(r5)[1]["学生"][2]["成绩"]["英语"]).toBeNull();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// —— 还原验证 ——
|
|
320
|
+
test('还原后与 decompressed 一致', () => {
|
|
321
|
+
expect(decompress(r5)).toEqual(src5Decompressed);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test('1班年级正确', () => {
|
|
325
|
+
expect(decompress(r5)[0]["年级"]).toBe("一年级");
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test('2班第1个学生英语为 null', () => {
|
|
329
|
+
expect(decompress(r5)[1]["学生"][0]["成绩"]["英语"]).toBeNull();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('2班第3个学生语文为 null', () => {
|
|
333
|
+
expect(decompress(r5)[1]["学生"][2]["成绩"]["语文"]).toBeNull();
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
/* =========================================================
|
|
338
|
+
边界 / 异常
|
|
339
|
+
========================================================= */
|
|
340
|
+
describe('边界 / 异常', () => {
|
|
341
|
+
|
|
342
|
+
test('compress 空数组 → 返回原值', () => {
|
|
343
|
+
expect(compress([], copt)).toEqual([]);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test('compress(null) → 返回原值', () => {
|
|
347
|
+
expect(compress(null, copt)).toBeNull();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test('compress 单个对象 → 自动包裹为数组', () => {
|
|
351
|
+
const obj = { name: '张三', age: 25 };
|
|
352
|
+
const r = compress(obj, copt);
|
|
353
|
+
expect(r.keys).toEqual(['name', 'age']);
|
|
354
|
+
expect(r.rows).toEqual([['张三', 25]]);
|
|
355
|
+
expect(decompress(r)).toEqual([obj]);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test('compress 非数组 → 返回原值', () => {
|
|
359
|
+
expect(compress('hello', copt)).toBe('hello');
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test('源数组含 null 元素', () => {
|
|
363
|
+
const src = [{ name: 'a', age: 10 }, null, { name: 'b' }];
|
|
364
|
+
const r = compress(src, copt);
|
|
365
|
+
expect(r.keys).toEqual(['name', 'age']);
|
|
366
|
+
expect(r.rows[0]).toEqual(['a', 10]);
|
|
367
|
+
expect(r.rows[2]).toEqual(trim ? ['b'] : ['b', null]);
|
|
368
|
+
// null 元素行: [null, null] → trim 后 [],不 trim 仍是 [null, null]
|
|
369
|
+
if (trim) {
|
|
370
|
+
expect(r.rows[1]).toEqual([]);
|
|
371
|
+
} else {
|
|
372
|
+
expect(r.rows[1]).toEqual([null, null]);
|
|
373
|
+
}
|
|
374
|
+
expect(decompress(r)).toEqual([
|
|
375
|
+
{ name: 'a', age: 10 },
|
|
376
|
+
{ name: null, age: null },
|
|
377
|
+
{ name: 'b', age: null }
|
|
378
|
+
]);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test('空对象数组字段', () => {
|
|
382
|
+
const src = [{ name: 'test', items: [] }];
|
|
383
|
+
const r = compress(src, copt);
|
|
384
|
+
expect(r.keys).toEqual(['name', 'items']);
|
|
385
|
+
expect(r.rows[0]).toEqual(['test', []]);
|
|
386
|
+
expect(decompress(r)).toEqual(src);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test('嵌套对象数组中含 null 元素(首元 null→退化为 primitive-array)', () => {
|
|
390
|
+
const src = [{ name: 'a', kids: [null, { name: 'child' }] }];
|
|
391
|
+
const r = compress(src, copt);
|
|
392
|
+
// v[0]===null → 走 primitive-array 分支,不拆解子 key
|
|
393
|
+
expect(r.keys).toEqual(['name', 'kids']);
|
|
394
|
+
expect(r.rows[0]).toEqual(['a', [null, { name: 'child' }]]);
|
|
395
|
+
expect(decompress(r)).toEqual(src);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test('字段值为 undefined 转 null', () => {
|
|
399
|
+
const src = [{ a: undefined, b: 1 }];
|
|
400
|
+
const r = compress(src, copt);
|
|
401
|
+
expect(r.keys).toEqual(['a', 'b']);
|
|
402
|
+
if (trim) {
|
|
403
|
+
expect(r.rows[0]).toEqual([null, 1]); // a 的 null 被 trim
|
|
404
|
+
} else {
|
|
405
|
+
expect(r.rows[0]).toEqual([null, 1]);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test('字段值为 null 在 decompress 中还原', () => {
|
|
410
|
+
const src = [{ a: 1, b: null }];
|
|
411
|
+
const r = compress(src, copt);
|
|
412
|
+
expect(decompress(r)[0].b).toBeNull();
|
|
413
|
+
if (trim) {
|
|
414
|
+
expect(r.rows[0]).toEqual([1]); // b 的 null 被 trim
|
|
415
|
+
} else {
|
|
416
|
+
expect(r.rows[0]).toEqual([1, null]);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test('对象数组中含非对象元素(item typeof!=="object" 分支)', () => {
|
|
421
|
+
const src = [{ name: 'x', kids: [{ name: 'kid' }, 'string', null] }];
|
|
422
|
+
const r = compress(src, copt);
|
|
423
|
+
// 首元素是对象 → object-array;非对象元素在 buildKeys 收集时被跳过
|
|
424
|
+
expect(r.keys).toEqual(['name', { kids: ['name'] }]);
|
|
425
|
+
// 非对象元素 → buildRow 返回 [null]
|
|
426
|
+
// trim 后 [null]→[],不 trim 保持 [null]
|
|
427
|
+
const nonObj = trim ? [] : [null];
|
|
428
|
+
expect(r.rows[0][1]).toEqual([['kid'], nonObj, nonObj]);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test('原数组含数字元素(typeof obj!=="object" 各分支)', () => {
|
|
432
|
+
const src = [42, { name: 'a' }, true, { name: 'b' }];
|
|
433
|
+
const r = compress(src, copt);
|
|
434
|
+
expect(r.keys).toEqual(['name']);
|
|
435
|
+
// 数字/布尔不是对象 → buildRow 返回 [null]
|
|
436
|
+
const nonObj = trim ? [] : [null];
|
|
437
|
+
expect(r.rows[0]).toEqual(nonObj);
|
|
438
|
+
expect(r.rows[1]).toEqual(['a']);
|
|
439
|
+
expect(r.rows[2]).toEqual(nonObj);
|
|
440
|
+
expect(r.rows[3]).toEqual(['b']);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
test('getValueKind:数组首元素也是数组 → primitive-array', () => {
|
|
444
|
+
const src = [{ name: 'x', matrix: [[1, 2], [3, 4]] }];
|
|
445
|
+
const r = compress(src, copt);
|
|
446
|
+
expect(r.keys).toEqual(['name', 'matrix']);
|
|
447
|
+
expect(r.rows[0]).toEqual(['x', [[1, 2], [3, 4]]]);
|
|
448
|
+
expect(decompress(r)).toEqual(src);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
test('普通原始类型数组 [1,2,3] 不拆解', () => {
|
|
452
|
+
const src = [{ name: 'x', scores: [1, 2, 3] }];
|
|
453
|
+
const r = compress(src, copt);
|
|
454
|
+
expect(r.keys).toEqual(['name', 'scores']);
|
|
455
|
+
expect(r.rows[0]).toEqual(['x', [1, 2, 3]]);
|
|
456
|
+
expect(decompress(r)).toEqual(src);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test('非对象元素 + 嵌套 key(buildRow line 108 三元 false 分支)', () => {
|
|
460
|
+
const src = [42, { name: 'a', detail: { x: 1 } }];
|
|
461
|
+
const r = compress(src, copt);
|
|
462
|
+
expect(r.keys).toEqual(['name', { detail: ['x'] }]);
|
|
463
|
+
// 42 不是对象 → 全 null
|
|
464
|
+
expect(r.rows[0]).toEqual(trim ? [] : [null, null]);
|
|
465
|
+
expect(r.rows[1]).toEqual(['a', [1]]);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test('非对象元素 + 对象数组(buildKeys line 73 typeof false 分支)', () => {
|
|
469
|
+
const src = [42, { items: [{ name: 'a' }] }];
|
|
470
|
+
const r = compress(src, copt);
|
|
471
|
+
expect(r.keys).toEqual([{ items: ['name'] }]);
|
|
472
|
+
// 42 不是对象 → buildRow 返回 [null]
|
|
473
|
+
expect(r.rows[0]).toEqual(trim ? [] : [null]);
|
|
474
|
+
expect(r.rows[1]).toEqual([[['a']]]);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test('字段值为 null 且 repValue 为 object-array(line 75 Array.isArray false 分支)', () => {
|
|
478
|
+
const src = [
|
|
479
|
+
{ items: [{ name: 'a' }] }, // items 是 object-array
|
|
480
|
+
{ items: null } // items 为 null → !Array.isArray
|
|
481
|
+
];
|
|
482
|
+
const r = compress(src, copt);
|
|
483
|
+
expect(r.keys).toEqual([{ items: ['name'] }]);
|
|
484
|
+
expect(r.rows[0]).toEqual([[['a']]]);
|
|
485
|
+
expect(r.rows[1]).toEqual(trim ? [] : [null]);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
test('trimTrailingNulls 端到端', () => {
|
|
489
|
+
const src = [
|
|
490
|
+
{ name: '张三', age: 28, profile: { avatar: 'a.jpg', bio: 'Hello' } },
|
|
491
|
+
{ name: '李四', age: 35, profile: { avatar: 'b.jpg', file: null } },
|
|
492
|
+
{ name: '王五' },
|
|
493
|
+
];
|
|
494
|
+
const r = compress(src, copt);
|
|
495
|
+
if (trim) {
|
|
496
|
+
expect(r.rows[0]).toEqual(['张三', 28, ['a.jpg', 'Hello']]);
|
|
497
|
+
expect(r.rows[1]).toEqual(['李四', 35, ['b.jpg']]);
|
|
498
|
+
expect(r.rows[2]).toEqual(['王五']);
|
|
499
|
+
} else {
|
|
500
|
+
expect(r.rows[0]).toEqual(['张三', 28, ['a.jpg', 'Hello', null]]);
|
|
501
|
+
expect(r.rows[1]).toEqual(['李四', 35, ['b.jpg', null, null]]);
|
|
502
|
+
expect(r.rows[2]).toEqual(['王五', null, null]);
|
|
503
|
+
}
|
|
504
|
+
expect(decompress(r)).toEqual([
|
|
505
|
+
{ name: '张三', age: 28, profile: { avatar: 'a.jpg', bio: 'Hello', file: null } },
|
|
506
|
+
{ name: '李四', age: 35, profile: { avatar: 'b.jpg', bio: null, file: null } },
|
|
507
|
+
{ name: '王五', age: null, profile: null },
|
|
508
|
+
]);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
/* =========================================================
|
|
514
|
+
stringify / parse 联用 roundtrip
|
|
515
|
+
========================================================= */
|
|
516
|
+
describe('stringify / parse 联用 roundtrip', () => {
|
|
517
|
+
test('样例1 往返:compress → stringify → parse → decompress', () => {
|
|
518
|
+
const cmp = compress(src1, copt);
|
|
519
|
+
const str = stringify(cmp);
|
|
520
|
+
const restored = parse(str);
|
|
521
|
+
expect(restored).toEqual(cmp);
|
|
522
|
+
expect(decompress(restored)).toEqual(src1);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
test('样例2 往返', () => {
|
|
526
|
+
const cmp = compress(src2, copt);
|
|
527
|
+
const str = stringify(cmp);
|
|
528
|
+
const restored = parse(str);
|
|
529
|
+
expect(restored).toEqual(cmp);
|
|
530
|
+
expect(decompress(restored)).toEqual(src2);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
test('场景3(缺失字段→null)往返', () => {
|
|
534
|
+
const cmp = compress(src3, copt);
|
|
535
|
+
const str = stringify(cmp);
|
|
536
|
+
const restored = parse(str);
|
|
537
|
+
expect(restored).toEqual(cmp);
|
|
538
|
+
expect(decompress(restored)).toEqual(src3Decompressed);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test('场景4(嵌套子 key 不全)往返', () => {
|
|
542
|
+
const cmp = compress(src4, copt);
|
|
543
|
+
const str = stringify(cmp);
|
|
544
|
+
const restored = parse(str);
|
|
545
|
+
expect(restored).toEqual(cmp);
|
|
546
|
+
expect(decompress(restored)).toEqual(src4Decompressed);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
test('场景5(复杂嵌套+缺失成绩)往返', () => {
|
|
550
|
+
const cmp = compress(src5, copt);
|
|
551
|
+
const str = stringify(cmp);
|
|
552
|
+
const restored = parse(str);
|
|
553
|
+
expect(restored).toEqual(cmp);
|
|
554
|
+
expect(decompress(restored)).toEqual(src5Decompressed);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
test('trim 对 rows 长度的影响', () => {
|
|
558
|
+
const src = [
|
|
559
|
+
{ name: 'a', extra: null },
|
|
560
|
+
{ name: 'b', extra: null }
|
|
561
|
+
];
|
|
562
|
+
const cmp = compress(src, copt);
|
|
563
|
+
// trim=true: 尾部 null 被移除,每行长度=1
|
|
564
|
+
// trim=false: 尾部 null 保留,每行长度=2
|
|
565
|
+
if (trim) {
|
|
566
|
+
expect(cmp.rows[0].length).toBe(1);
|
|
567
|
+
expect(cmp.rows[1].length).toBe(1);
|
|
568
|
+
} else {
|
|
569
|
+
expect(cmp.rows[0].length).toBe(2);
|
|
570
|
+
expect(cmp.rows[1].length).toBe(2);
|
|
571
|
+
}
|
|
572
|
+
// stringify 往返正确
|
|
573
|
+
const str = stringify(cmp);
|
|
574
|
+
expect(parse(str)).toEqual(cmp);
|
|
575
|
+
expect(decompress(parse(str))).toEqual([
|
|
576
|
+
{ name: 'a', extra: null },
|
|
577
|
+
{ name: 'b', extra: null }
|
|
578
|
+
]);
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
}); // end [label] describe
|
|
583
|
+
}); // end forEach
|
|
441
584
|
|
|
442
585
|
/* =========================================================
|
|
443
586
|
stringify / parse — 省略 null 文本化 与 还原
|
|
587
|
+
(以下测试不依赖 compress,不需要按 trim 分组)
|
|
444
588
|
========================================================= */
|
|
445
589
|
describe('stringify / parse', () => {
|
|
446
590
|
|
|
@@ -562,59 +706,25 @@ test('原数组含数字元素(typeof obj!=="object" 各分支)', () => {
|
|
|
562
706
|
});
|
|
563
707
|
});
|
|
564
708
|
|
|
565
|
-
// ---- 与 compress/decompress
|
|
709
|
+
// ---- 与 compress/decompress 联用(纯 stringify/parse,不按 trim 分组)----
|
|
566
710
|
describe('与 compress/decompress 联用', () => {
|
|
567
|
-
test('
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
expect(restored).toEqual(cmp);
|
|
572
|
-
expect(decompress(restored)).toEqual(src1);
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
test('样例2 往返', () => {
|
|
576
|
-
const cmp = compress(src2);
|
|
577
|
-
const str = stringify(cmp);
|
|
578
|
-
const restored = parse(str);
|
|
579
|
-
expect(restored).toEqual(cmp);
|
|
580
|
-
expect(decompress(restored)).toEqual(src2);
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
test('场景3(缺失字段→null)往返', () => {
|
|
584
|
-
const cmp = compress(src3);
|
|
585
|
-
const str = stringify(cmp);
|
|
586
|
-
const restored = parse(str);
|
|
587
|
-
expect(restored).toEqual(cmp);
|
|
588
|
-
expect(decompress(restored)).toEqual(src3Normalized);
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
test('场景4(嵌套子 key 不全)往返', () => {
|
|
592
|
-
const cmp = compress(src4);
|
|
593
|
-
const str = stringify(cmp);
|
|
594
|
-
const restored = parse(str);
|
|
595
|
-
expect(restored).toEqual(cmp);
|
|
596
|
-
expect(decompress(restored)).toEqual(src4Normalized);
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
test('场景5(复杂嵌套+缺失成绩)往返', () => {
|
|
600
|
-
const cmp = compress(src5);
|
|
601
|
-
const str = stringify(cmp);
|
|
602
|
-
const restored = parse(str);
|
|
603
|
-
expect(restored).toEqual(cmp);
|
|
604
|
-
expect(decompress(restored)).toEqual(src5Normalized);
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
test('stringify 输出不含 null 关键字的实例(验证省略效果)', () => {
|
|
608
|
-
// 构造一个有大量 null 的压缩结构,验证文本化后不含 "null"
|
|
609
|
-
const src = [
|
|
610
|
-
{ name: 'a', extra: null },
|
|
611
|
-
{ name: 'b', extra: null }
|
|
711
|
+
test('compress → stringify → parse → decompress(顶层缺失字段)', () => {
|
|
712
|
+
const data = [
|
|
713
|
+
{ name: '张三', age: 25, city: '北京' },
|
|
714
|
+
{ name: '李四', age: 30 }
|
|
612
715
|
];
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
716
|
+
const expected = [
|
|
717
|
+
{ name: '张三', age: 25, city: '北京' },
|
|
718
|
+
{ name: '李四', age: 30, city: null }
|
|
719
|
+
];
|
|
720
|
+
const compressed = compress(data);
|
|
721
|
+
const text = stringify(compressed);
|
|
722
|
+
const parsed = parse(text);
|
|
723
|
+
expect(decompress(parsed)).toEqual(expected);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
test('含空格字符串保留引号', () => {
|
|
727
|
+
expect(stringify(['hello world'])).toBe('["hello world"]');
|
|
618
728
|
});
|
|
619
729
|
});
|
|
620
730
|
|
|
@@ -811,27 +921,6 @@ test('原数组含数字元素(typeof obj!=="object" 各分支)', () => {
|
|
|
811
921
|
});
|
|
812
922
|
});
|
|
813
923
|
|
|
814
|
-
describe('与 compress/decompress 联用', () => {
|
|
815
|
-
test('compress → stringify → parse → decompress', () => {
|
|
816
|
-
const data = [
|
|
817
|
-
{ name: '张三', age: 25, city: '北京' },
|
|
818
|
-
{ name: '李四', age: 30 }
|
|
819
|
-
];
|
|
820
|
-
const expected = [
|
|
821
|
-
{ name: '张三', age: 25, city: '北京' },
|
|
822
|
-
{ name: '李四', age: 30, city: null }
|
|
823
|
-
];
|
|
824
|
-
const compressed = compress(data);
|
|
825
|
-
const text = stringify(compressed);
|
|
826
|
-
const parsed = parse(text);
|
|
827
|
-
expect(decompress(parsed)).toEqual(expected);
|
|
828
|
-
});
|
|
829
|
-
|
|
830
|
-
test('含空格字符串保留引号', () => {
|
|
831
|
-
expect(stringify(['hello world'])).toBe('["hello world"]');
|
|
832
|
-
});
|
|
833
|
-
});
|
|
834
|
-
|
|
835
924
|
describe('parse 兼容有/无引号格式', () => {
|
|
836
925
|
test('解析无引号字符串', () => {
|
|
837
926
|
expect(parse('[hello]')).toEqual(['hello']);
|