Skip to content

Commit 676d582

Browse files
committed
dbeaver/pro#7990 tests: add tests for submitForm and objectToFormFields functions
1 parent 1424496 commit 676d582

1 file changed

Lines changed: 253 additions & 0 deletions

File tree

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
* CloudBeaver - Cloud Database Manager
3+
* Copyright (C) 2020-2026 DBeaver Corp and others
4+
*
5+
* Licensed under the Apache License, Version 2.0.
6+
* you may not use this file except in compliance with the License.
7+
*/
8+
import { afterEach, beforeEach, describe, expect, it, vitest } from 'vitest';
9+
import { objectToFormFields, submitForm } from './submitForm.js';
10+
11+
describe('submitForm', () => {
12+
let submitSpy: ReturnType<typeof vitest.spyOn<HTMLFormElement, 'submit'>>;
13+
let capturedForm: HTMLFormElement | null;
14+
15+
// submit() is the moment when the form is fully populated and still attached to the DOM —
16+
// submitForm removes it right after. We snapshot it from document.body here.
17+
const getForm = (): HTMLFormElement => {
18+
if (!capturedForm) {
19+
throw new Error('submit() was not called');
20+
}
21+
return capturedForm;
22+
};
23+
const getInputs = (): HTMLInputElement[] => Array.from(getForm().querySelectorAll('input'));
24+
25+
beforeEach(() => {
26+
document.body.innerHTML = '';
27+
capturedForm = null;
28+
submitSpy = vitest.spyOn(HTMLFormElement.prototype, 'submit').mockImplementation(() => {
29+
capturedForm = document.body.querySelector('form');
30+
});
31+
});
32+
33+
afterEach(() => {
34+
submitSpy.mockRestore();
35+
});
36+
37+
it('should create a form with POST method and target _blank', () => {
38+
submitForm('/api/test', []);
39+
40+
const form = getForm();
41+
expect(form.tagName).toBe('FORM');
42+
expect(form.method.toLowerCase()).toBe('post');
43+
expect(form.action).toContain('/api/test');
44+
expect(form.target).toBe('_blank');
45+
expect(form.style.display).toBe('none');
46+
});
47+
48+
it('should attach the form to document.body before submit', () => {
49+
submitForm('/api/test', []);
50+
51+
// capturedForm is read inside submit(), so being non-null proves the form was in the DOM at that point
52+
expect(capturedForm).not.toBeNull();
53+
});
54+
55+
it('should append hidden inputs for each field', () => {
56+
submitForm('/api/test', [
57+
['name', 'John'],
58+
['age', '30'],
59+
]);
60+
61+
const inputs = getInputs();
62+
expect(inputs).toHaveLength(2);
63+
expect(inputs[0]!.type).toBe('hidden');
64+
expect(inputs[0]!.name).toBe('name');
65+
expect(inputs[0]!.value).toBe('John');
66+
expect(inputs[1]!.type).toBe('hidden');
67+
expect(inputs[1]!.name).toBe('age');
68+
expect(inputs[1]!.value).toBe('30');
69+
});
70+
71+
it('should submit the form and remove it from the DOM', () => {
72+
submitForm('/api/test', [['key', 'value']]);
73+
74+
expect(submitSpy).toHaveBeenCalledTimes(1);
75+
expect(document.body.querySelector('form')).toBeNull();
76+
});
77+
78+
it('should support repeated keys (e.g. for array fields)', () => {
79+
submitForm('/api/test', [
80+
['ids', '1'],
81+
['ids', '2'],
82+
['ids', '3'],
83+
]);
84+
85+
const ids = getInputs().filter(i => i.name === 'ids');
86+
expect(ids).toHaveLength(3);
87+
expect(ids.map(i => i.value)).toEqual(['1', '2', '3']);
88+
});
89+
90+
it('should accept any iterable of key/value pairs', () => {
91+
const fields = new Map([
92+
['a', '1'],
93+
['b', '2'],
94+
]);
95+
96+
submitForm('/api/test', fields);
97+
98+
const inputs = getInputs();
99+
expect(inputs).toHaveLength(2);
100+
expect(inputs.map(i => [i.name, i.value])).toEqual([
101+
['a', '1'],
102+
['b', '2'],
103+
]);
104+
});
105+
106+
it('should also accept a generator', () => {
107+
function* gen(): Generator<readonly [string, string]> {
108+
yield ['x', '1'];
109+
yield ['y', '2'];
110+
}
111+
112+
submitForm('/api/test', gen());
113+
114+
expect(getInputs().map(i => [i.name, i.value])).toEqual([
115+
['x', '1'],
116+
['y', '2'],
117+
]);
118+
});
119+
120+
it('should handle an empty fields iterable', () => {
121+
submitForm('/api/test', []);
122+
123+
expect(getInputs()).toHaveLength(0);
124+
expect(submitSpy).toHaveBeenCalledTimes(1);
125+
expect(document.body.querySelector('form')).toBeNull();
126+
});
127+
});
128+
129+
describe('objectToFormFields', () => {
130+
it('should convert primitive values to string fields', () => {
131+
expect(
132+
objectToFormFields({
133+
name: 'John',
134+
age: 30,
135+
active: true,
136+
}),
137+
).toEqual([
138+
['name', 'John'],
139+
['age', '30'],
140+
['active', 'true'],
141+
]);
142+
});
143+
144+
it('should skip null, undefined, and empty string values', () => {
145+
expect(
146+
objectToFormFields({
147+
a: 'value',
148+
b: null,
149+
c: undefined,
150+
d: '',
151+
e: 'other',
152+
}),
153+
).toEqual([
154+
['a', 'value'],
155+
['e', 'other'],
156+
]);
157+
});
158+
159+
it('should keep falsy non-empty values (0, false)', () => {
160+
expect(
161+
objectToFormFields({
162+
zero: 0,
163+
falseFlag: false,
164+
}),
165+
).toEqual([
166+
['zero', '0'],
167+
['falseFlag', 'false'],
168+
]);
169+
});
170+
171+
it('should expand arrays into repeated fields', () => {
172+
expect(
173+
objectToFormFields({
174+
tags: ['a', 'b', 'c'],
175+
}),
176+
).toEqual([
177+
['tags', 'a'],
178+
['tags', 'b'],
179+
['tags', 'c'],
180+
]);
181+
});
182+
183+
it('should skip null/undefined/empty entries inside arrays', () => {
184+
expect(
185+
objectToFormFields({
186+
ids: [1, null, 2, undefined, '', 3],
187+
}),
188+
).toEqual([
189+
['ids', '1'],
190+
['ids', '2'],
191+
['ids', '3'],
192+
]);
193+
});
194+
195+
it('should flatten nested objects, hoisting inner keys to top level', () => {
196+
expect(
197+
objectToFormFields({
198+
user: {
199+
name: 'John',
200+
age: 30,
201+
},
202+
}),
203+
).toEqual([
204+
['name', 'John'],
205+
['age', '30'],
206+
]);
207+
});
208+
209+
it('should flatten deeply nested objects', () => {
210+
expect(
211+
objectToFormFields({
212+
outer: {
213+
middle: {
214+
leaf: 'value',
215+
},
216+
},
217+
}),
218+
).toEqual([['leaf', 'value']]);
219+
});
220+
221+
it('should handle nested objects mixed with arrays', () => {
222+
expect(
223+
objectToFormFields({
224+
meta: {
225+
tags: ['x', 'y'],
226+
name: 'foo',
227+
},
228+
id: 1,
229+
}),
230+
).toEqual([
231+
['tags', 'x'],
232+
['tags', 'y'],
233+
['name', 'foo'],
234+
['id', '1'],
235+
]);
236+
});
237+
238+
it('should return an empty array for an empty object', () => {
239+
expect(objectToFormFields({})).toEqual([]);
240+
});
241+
242+
it('should return an empty array when all values are skipped', () => {
243+
expect(
244+
objectToFormFields({
245+
a: null,
246+
b: undefined,
247+
c: '',
248+
d: [],
249+
e: [null, '', undefined],
250+
}),
251+
).toEqual([]);
252+
});
253+
});

0 commit comments

Comments
 (0)