1// @vitest-environment jsdom
2
3import { stringifyVariables, extractFiles } from './variables';
4import { describe, it, expect } from 'vitest';
5import { Script } from 'vm';
6
7describe('stringifyVariables', () => {
8 it('stringifies objects stabily', () => {
9 expect(stringifyVariables({ b: 'b', a: 'a' })).toBe('{"a":"a","b":"b"}');
10 expect(stringifyVariables({ x: { b: 'b', a: 'a' } })).toBe(
11 '{"x":{"a":"a","b":"b"}}'
12 );
13 });
14
15 it('stringifies arrays', () => {
16 expect(stringifyVariables([1, 2])).toBe('[1,2]');
17 expect(stringifyVariables({ x: [1, 2] })).toBe('{"x":[1,2]}');
18 });
19
20 it('stringifies scalars', () => {
21 expect(stringifyVariables(1)).toBe('1');
22 expect(stringifyVariables('test')).toBe('"test"');
23 expect(stringifyVariables(null)).toBe('null');
24 expect(stringifyVariables(undefined)).toBe('');
25 expect(stringifyVariables(Infinity)).toBe('null');
26 expect(stringifyVariables(1 / 0)).toBe('null');
27 });
28
29 it('returns null for circular structures', () => {
30 const x = { x: null } as any;
31 x.x = x;
32 expect(stringifyVariables(x)).toBe('{"x":null}');
33 });
34
35 it('stringifies dates correctly', () => {
36 const date = new Date('2019-12-11T04:20:00');
37 expect(stringifyVariables(date)).toBe(`"${date.toJSON()}"`);
38 });
39
40 it('stringifies dictionaries (Object.create(null)) correctly', () => {
41 expect(stringifyVariables(Object.create(null))).toBe('{}');
42 });
43
44 it('recovers if the root object is a dictionary (Object.create(null)) and nests a plain object', () => {
45 const root = Object.create(null);
46 root.data = { test: true };
47 expect(stringifyVariables(root)).toBe('{"data":{"test":true}}');
48 });
49
50 it('recovers if the root object contains a dictionary (Object.create(null))', () => {
51 const data = Object.create(null);
52 data.test = true;
53 const root = { data };
54 expect(stringifyVariables(root)).toBe('{"data":{"test":true}}');
55 });
56
57 it('replaces non-plain objects at the root with keyed replacements', () => {
58 expect(stringifyVariables(new (class Test {})())).toMatch(
59 /^{"__key":"\w+"}$/
60 );
61 expect(stringifyVariables(new Map())).toMatch(/^{"__key":"\w+"}$/);
62 });
63
64 it('stringifies files correctly', () => {
65 const file = new File([0] as any, 'test.js');
66 const str = stringifyVariables(file);
67 expect(str).toBe('null');
68 });
69
70 it('stringifies plain objects from foreign JS contexts correctly', () => {
71 const scriptGlobal: typeof globalThis = new Script(
72 'exports = globalThis'
73 ).runInNewContext({}).exports;
74
75 const plain = new scriptGlobal.Function('return { test: true }')();
76 expect(stringifyVariables(plain)).toBe('{"test":true}');
77
78 const data = new scriptGlobal.Function('return new (class Test {})')();
79 expect(stringifyVariables(data)).toMatch(/^{"__key":"\w+"}$/);
80 });
81});
82
83describe('extractFiles', () => {
84 it('extracts files from nested objects', () => {
85 const file = new Blob();
86 expect(extractFiles({ files: { a: file } })).toEqual(
87 new Map([['variables.files.a', file]])
88 );
89 });
90
91 it('extracts files from nested arrays', () => {
92 const file = new Blob();
93 expect(extractFiles({ files: [file] })).toEqual(
94 new Map([['variables.files.0', file]])
95 );
96 });
97});