Skip to content

Commit b5ff362

Browse files
Lustmoredweaverryan
authored andcommitted
Add tests for passing files to RequestBuilder and make them pass
1 parent 14a640b commit b5ff362

File tree

2 files changed

+85
-11
lines changed

2 files changed

+85
-11
lines changed

src/LiveComponent/assets/src/Backend/RequestBuilder.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,15 @@ export default class {
2727
Accept: 'application/vnd.live-component+html',
2828
};
2929

30+
const totalFiles = Object.entries(files).reduce(
31+
(total, current) => total + current.length,
32+
0
33+
);
34+
3035
const hasFingerprints = Object.keys(children).length > 0;
3136
if (
3237
actions.length === 0 &&
38+
totalFiles === 0 &&
3339
this.willDataFitInUrl(JSON.stringify(props), JSON.stringify(updated), params, JSON.stringify(children), JSON.stringify(updatedPropsFromParent))
3440
) {
3541
params.set('props', JSON.stringify(props));
@@ -51,11 +57,15 @@ export default class {
5157
requestData.children = children;
5258
}
5359

60+
if (
61+
this.csrfToken &&
62+
(actions.length || totalFiles)
63+
) {
64+
fetchOptions.headers['X-CSRF-TOKEN'] = this.csrfToken;
65+
}
66+
5467
if (actions.length > 0) {
5568
// one or more ACTIONs
56-
if (this.csrfToken) {
57-
fetchOptions.headers['X-CSRF-TOKEN'] = this.csrfToken;
58-
}
5969

6070
if (actions.length === 1) {
6171
// simple, single action

src/LiveComponent/assets/test/Backend/RequestBuilder.test.ts

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ describe('buildRequest', () => {
3939
Accept: 'application/vnd.live-component+html',
4040
'X-CSRF-TOKEN': '_the_csrf_token',
4141
});
42-
const body = fetchOptions.body;
42+
const body = <FormData>fetchOptions.body;
4343
expect(body).toBeInstanceOf(FormData);
44-
// @ts-ignore body is already asserted to be FormData
4544
expect(body.get('data')).toEqual(JSON.stringify({
4645
props: { firstName: 'Ryan' },
4746
updated: { firstName: 'Kevin' },
@@ -69,9 +68,8 @@ describe('buildRequest', () => {
6968

7069
expect(url).toEqual('/_components/_batch');
7170
expect(fetchOptions.method).toEqual('POST');
72-
const body = fetchOptions.body;
71+
const body = <FormData>fetchOptions.body;
7372
expect(body).toBeInstanceOf(FormData);
74-
// @ts-ignore body is already asserted to be FormData
7573
expect(body.get('data')).toEqual(JSON.stringify({
7674
props: { firstName: 'Ryan' },
7775
updated: { firstName: 'Kevin' },
@@ -103,9 +101,8 @@ describe('buildRequest', () => {
103101
// no token
104102
Accept: 'application/vnd.live-component+html',
105103
});
106-
const body = fetchOptions.body;
104+
const body = <FormData>fetchOptions.body;
107105
expect(body).toBeInstanceOf(FormData);
108-
// @ts-ignore body is already asserted to be FormData
109106
expect(body.get('data')).toEqual(JSON.stringify({
110107
props: { firstName: 'Ryan'.repeat(1000) },
111108
updated: { firstName: 'Kevin'.repeat(1000) },
@@ -138,14 +135,81 @@ describe('buildRequest', () => {
138135
{}
139136
);
140137

141-
const body = fetchOptions.body;
138+
const body = <FormData>fetchOptions.body;
142139
expect(body).toBeInstanceOf(FormData);
143-
// @ts-ignore body is already asserted to be FormData
144140
expect(body.get('data')).toEqual(JSON.stringify({
145141
props: { firstName: 'Ryan' },
146142
updated: { firstName: 'Kevin' },
147143
propsFromParent: { count: 5 },
148144
args: { sendNotification: '1' },
149145
}));
150146
});
147+
148+
// Helper method for FileList mocking
149+
const getFileList = (length = 1) => {
150+
const blob = new Blob([''], { type: 'text/html' });
151+
// @ts-ignore This is a mock and those are needed to mock a File object
152+
blob['lastModifiedDate'] = '';
153+
// @ts-ignore This is a mock and those are needed to mock a File object
154+
blob['name'] = 'filename';
155+
const file = <File>blob;
156+
const fileList: FileList = {
157+
length: length,
158+
item: () => file
159+
};
160+
for (let i= 0; i < length; ++i) {
161+
fileList[i] = file;
162+
}
163+
return fileList;
164+
};
165+
166+
it('Sends file with request', () => {
167+
const builder = new RequestBuilder('/_components', '_the_csrf_token');
168+
169+
const { url, fetchOptions } = builder.buildRequest(
170+
{ firstName: 'Ryan' },
171+
[],
172+
{},
173+
{},
174+
{},
175+
{ 'file': getFileList()}
176+
);
177+
178+
expect(url).toEqual('/_components');
179+
expect(fetchOptions.method).toEqual('POST');
180+
expect(fetchOptions.headers).toEqual({
181+
Accept: 'application/vnd.live-component+html',
182+
'X-CSRF-TOKEN': '_the_csrf_token',
183+
});
184+
const body = <FormData>fetchOptions.body;
185+
expect(body).toBeInstanceOf(FormData);
186+
expect(body.get('file')).toBeInstanceOf(File);
187+
expect(body.getAll('file').length).toEqual(1);
188+
});
189+
190+
it('Sends multiple files with request', () => {
191+
const builder = new RequestBuilder('/_components', '_the_csrf_token');
192+
193+
const { url, fetchOptions } = builder.buildRequest(
194+
{ firstName: 'Ryan' },
195+
[],
196+
{},
197+
{},
198+
{},
199+
{ 'file[]': getFileList(3), 'otherFile': getFileList()}
200+
);
201+
202+
expect(url).toEqual('/_components');
203+
expect(fetchOptions.method).toEqual('POST');
204+
expect(fetchOptions.headers).toEqual({
205+
Accept: 'application/vnd.live-component+html',
206+
'X-CSRF-TOKEN': '_the_csrf_token',
207+
});
208+
const body = <FormData>fetchOptions.body;
209+
expect(body).toBeInstanceOf(FormData);
210+
expect(body.get('file[]')).toBeInstanceOf(File);
211+
expect(body.getAll('file[]').length).toEqual(3);
212+
expect(body.get('otherFile')).toBeInstanceOf(File);
213+
expect(body.getAll('otherFile').length).toEqual(1);
214+
});
151215
});

0 commit comments

Comments
 (0)