Skip to content

Commit 8bf6d0d

Browse files
committed
Handle unexpected nulls from Expo's file upload system.
1 parent 991a881 commit 8bf6d0d

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

react-native/services/Request/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,8 @@ export class Request implements RequestInterface {
211211

212212
const result = await task.uploadAsync()
213213

214-
if (result === undefined) {
214+
// NOTE: According to Expo's documentation, this should only return a value or undefined, but it has been observed to return null when the task is aborted.
215+
if (result === undefined || result === null) {
215216
throw new AbortError()
216217
} else {
217218
response = result

react-native/services/Request/unit.tsx

+64
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,70 @@ test('get request file response empty external abort', async () => {
10971097
expect(FileSystem.deleteAsync).not.toHaveBeenCalled()
10981098
})
10991099

1100+
test('get request file response empty external abort null', async () => {
1101+
const fetch = jest.fn();
1102+
(global as unknown as { fetch: unknown }).fetch = fetch
1103+
let resolve: (value: null) => void
1104+
const cancelAsync = jest.fn(() => {
1105+
resolve(null)
1106+
})
1107+
const uploadAsync = jest.fn(async () => {
1108+
await new Promise<null>((_resolve) => {
1109+
resolve = _resolve
1110+
})
1111+
});
1112+
(
1113+
FileSystem.createUploadTask as unknown as {
1114+
mockReturnValue: (value: unknown) => void
1115+
}
1116+
).mockReturnValue({
1117+
cancelAsync,
1118+
uploadAsync
1119+
})
1120+
const request = new Request(
1121+
'https://example-base-url.com/example/sub/path/',
1122+
1000,
1123+
() => 'Example Authorization Header'
1124+
)
1125+
const abortController = new AbortController()
1126+
const promise = request.withoutResponse(
1127+
'PUT',
1128+
'example/route',
1129+
{ type: 'file', fileUri: 'Example File Uri' },
1130+
{
1131+
'Example Query Parameter A Key': 'Example Query Parameter A Value',
1132+
'Example Query Parameter B Key': 12.34,
1133+
'Example Query Parameter C Key': false,
1134+
'Example Query Parameter D Key': true
1135+
},
1136+
abortController.signal,
1137+
['244', '123', '89']
1138+
)
1139+
1140+
abortController.abort()
1141+
1142+
await expect(promise).rejects.toEqual(new AbortError())
1143+
1144+
expect(FileSystem.createUploadTask).toBeCalledTimes(1)
1145+
expect(FileSystem.createUploadTask).toBeCalledWith(
1146+
'https://example-base-url.com/example/sub/path/example/route?Example%20Query%20Parameter%20A%20Key=Example%20Query%20Parameter%20A%20Value&Example%20Query%20Parameter%20B%20Key=12.34&Example%20Query%20Parameter%20D%20Key',
1147+
'Example File Uri',
1148+
{
1149+
uploadType: 'Test Binary Content',
1150+
headers: {
1151+
Authorization: 'Example Authorization Header',
1152+
Accept: 'application/json'
1153+
}
1154+
}
1155+
)
1156+
expect(cancelAsync).toBeCalledTimes(1)
1157+
expect(uploadAsync).toBeCalledTimes(1)
1158+
1159+
expect(fetch).not.toHaveBeenCalled()
1160+
expect(FileSystem.downloadAsync).not.toHaveBeenCalled()
1161+
expect(FileSystem.deleteAsync).not.toHaveBeenCalled()
1162+
})
1163+
11001164
test('get request empty response json', async () => {
11011165
const fetch = jest.fn().mockResolvedValue({
11021166
status: 123,

0 commit comments

Comments
 (0)