Skip to content

Commit c0679e8

Browse files
committed
fix unit tests
1 parent 7c6a70d commit c0679e8

11 files changed

+608
-1590
lines changed

src/ParseObject.ts

-2
Original file line numberDiff line numberDiff line change
@@ -2552,7 +2552,6 @@ const DefaultController = {
25522552
const status = responses[index]._status;
25532553
delete responses[index]._status;
25542554
delete responses[index]._headers;
2555-
delete responses[index]._xhr;
25562555
mapIdForPin[objectId] = obj._localId;
25572556
obj._handleSaveResponse(responses[index].success, status);
25582557
} else {
@@ -2620,7 +2619,6 @@ const DefaultController = {
26202619
const status = response._status;
26212620
delete response._status;
26222621
delete response._headers;
2623-
delete response._xhr;
26242622
targetCopy._handleSaveResponse(response, status);
26252623
},
26262624
error => {

src/RESTController.ts

+16-11
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,19 @@ function ajaxIE9(method: string, url: string, data: any, _headers?: any, options
9595
}
9696

9797
const RESTController = {
98-
ajax(method: string, url: string, data: any, headers?: any, options?: FullOptions) {
98+
async ajax(method: string, url: string, data: any, headers?: any, options?: FullOptions) {
9999
if (useXDomainRequest) {
100100
return ajaxIE9(method, url, data, headers, options);
101101
}
102+
if (typeof fetch !== 'function') {
103+
throw new Error('Cannot make a request: Fetch API not found.');
104+
}
102105
const promise = resolvingPromise();
103106
const isIdempotent = CoreManager.get('IDEMPOTENCY') && ['POST', 'PUT'].includes(method);
104107
const requestId = isIdempotent ? uuidv4() : '';
105108
let attempts = 0;
106109

107110
const dispatch = async function () {
108-
if (typeof fetch !== 'function') {
109-
throw new Error('Cannot make a request: Fetch API not found.');
110-
}
111111
const controller = new AbortController();
112112
const { signal } = controller;
113113

@@ -185,16 +185,18 @@ const RESTController = {
185185
} else {
186186
result = await response.json();
187187
}
188-
promise.resolve({ status, response: result, headers: responseHeaders, xhr: response });
188+
promise.resolve({ status, response: result, headers: responseHeaders });
189189
} else if (status >= 400 && status < 500) {
190190
const error = await response.json();
191191
promise.reject(error);
192-
} else if (status >= 500) {
193-
// retry on 5XX
192+
} else if (status >= 500 || status === 0) {
193+
// retry on 5XX or library error
194194
if (++attempts < CoreManager.get('REQUEST_ATTEMPT_LIMIT')) {
195195
// Exponentially-growing random delay
196196
const delay = Math.round(Math.random() * 125 * Math.pow(2, attempts));
197197
setTimeout(dispatch, delay);
198+
} else if (status === 0) {
199+
promise.reject('Unable to connect to the Parse API');
198200
} else {
199201
// After the retry limit is reached, fail
200202
promise.reject(response);
@@ -301,9 +303,9 @@ const RESTController = {
301303

302304
const payloadString = JSON.stringify(payload);
303305
return RESTController.ajax(method, url, payloadString, {}, options).then(
304-
({ response, status, headers, xhr }) => {
306+
({ response, status, headers }) => {
305307
if (options.returnStatus) {
306-
return { ...response, _status: status, _headers: headers, _xhr: xhr };
308+
return { ...response, _status: status, _headers: headers };
307309
} else {
308310
return response;
309311
}
@@ -317,8 +319,8 @@ const RESTController = {
317319
// Transform the error into an instance of ParseError by trying to parse
318320
// the error string as JSON
319321
let error;
320-
if (errorJSON.code || errorJSON.error|| errorJSON.message) {
321-
error = new ParseError(errorJSON.code, errorJSON.error|| errorJSON.message);
322+
if (errorJSON.code || errorJSON.error || errorJSON.message) {
323+
error = new ParseError(errorJSON.code, errorJSON.error || errorJSON.message);
322324
} else {
323325
error = new ParseError(
324326
ParseError.CONNECTION_FAILED,
@@ -327,6 +329,9 @@ const RESTController = {
327329
}
328330
return Promise.reject(error);
329331
},
332+
// Used for testing
333+
_setXHR() {},
334+
_getXHR() {},
330335
};
331336

332337
export default RESTController;

src/Xhr.weapp.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
export const TEXT_FILE_EXTS = /\.(txt|json|html|txt|csv)/;
2-
31
// @ts-ignore
42
function parseResponse(res: wx.RequestSuccessCallbackResult) {
53
let headers = res.header || {};
@@ -43,6 +41,7 @@ export function polyfillFetch() {
4341
const typedGlobal = global as any;
4442
if (typeof typedGlobal.fetch !== 'function') {
4543
typedGlobal.fetch = (url: string, options: any) => {
44+
const TEXT_FILE_EXTS = /\.(txt|json|html|txt|csv)/;
4645
const dataType = url.match(TEXT_FILE_EXTS) ? 'text' : 'arraybuffer';
4746
return new Promise((resolve, reject) => {
4847
// @ts-ignore

src/__tests__/ParseFile-test.js

+14-154
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ const b64Digit = require('../ParseFile').b64Digit;
99

1010
const ParseObject = require('../ParseObject').default;
1111
const CoreManager = require('../CoreManager').default;
12-
const EventEmitter = require('../EventEmitter').default;
13-
14-
const mockHttp = require('http');
15-
const mockHttps = require('https');
12+
const mockFetch = require('./test_helpers/mockFetch');
1613

1714
const mockLocalDatastore = {
1815
_updateLocalIdForObject: jest.fn((_localId, /** @type {ParseObject}*/ object) => {
@@ -491,189 +488,52 @@ describe('FileController', () => {
491488
spy2.mockRestore();
492489
});
493490

494-
it('download with base64 http', async () => {
495-
defaultController._setXHR(null);
496-
const mockResponse = Object.create(EventEmitter.prototype);
497-
EventEmitter.call(mockResponse);
498-
mockResponse.setEncoding = function () {};
499-
mockResponse.headers = {
500-
'content-type': 'image/png',
501-
};
502-
const spy = jest.spyOn(mockHttp, 'get').mockImplementationOnce((uri, cb) => {
503-
cb(mockResponse);
504-
mockResponse.emit('data', 'base64String');
505-
mockResponse.emit('end');
506-
return {
507-
on: function () {},
508-
};
509-
});
510-
511-
const data = await defaultController.download('http://example.com/image.png');
512-
expect(data.base64).toBe('base64String');
513-
expect(data.contentType).toBe('image/png');
514-
expect(mockHttp.get).toHaveBeenCalledTimes(1);
515-
expect(mockHttps.get).toHaveBeenCalledTimes(0);
516-
spy.mockRestore();
517-
});
518-
519-
it('download with base64 http abort', async () => {
520-
defaultController._setXHR(null);
521-
const mockRequest = Object.create(EventEmitter.prototype);
522-
const mockResponse = Object.create(EventEmitter.prototype);
523-
EventEmitter.call(mockRequest);
524-
EventEmitter.call(mockResponse);
525-
mockResponse.setEncoding = function () {};
526-
mockResponse.headers = {
527-
'content-type': 'image/png',
528-
};
529-
const spy = jest.spyOn(mockHttp, 'get').mockImplementationOnce((uri, cb) => {
530-
cb(mockResponse);
531-
return mockRequest;
532-
});
533-
const options = {
534-
requestTask: () => {},
535-
};
536-
defaultController.download('http://example.com/image.png', options).then(data => {
537-
expect(data).toEqual({});
538-
});
539-
mockRequest.emit('abort');
540-
spy.mockRestore();
541-
});
542-
543-
it('download with base64 https', async () => {
544-
defaultController._setXHR(null);
545-
const mockResponse = Object.create(EventEmitter.prototype);
546-
EventEmitter.call(mockResponse);
547-
mockResponse.setEncoding = function () {};
548-
mockResponse.headers = {
549-
'content-type': 'image/png',
550-
};
551-
const spy = jest.spyOn(mockHttps, 'get').mockImplementationOnce((uri, cb) => {
552-
cb(mockResponse);
553-
mockResponse.emit('data', 'base64String');
554-
mockResponse.emit('end');
555-
return {
556-
on: function () {},
557-
};
558-
});
559-
560-
const data = await defaultController.download('https://example.com/image.png');
561-
expect(data.base64).toBe('base64String');
562-
expect(data.contentType).toBe('image/png');
563-
expect(mockHttp.get).toHaveBeenCalledTimes(0);
564-
expect(mockHttps.get).toHaveBeenCalledTimes(1);
565-
spy.mockRestore();
566-
});
567-
568491
it('download with ajax', async () => {
569-
const mockXHR = function () {
570-
return {
571-
DONE: 4,
572-
open: jest.fn(),
573-
send: jest.fn().mockImplementation(function () {
574-
this.response = [61, 170, 236, 120];
575-
this.readyState = 2;
576-
this.onreadystatechange();
577-
this.readyState = 4;
578-
this.onreadystatechange();
579-
}),
580-
getResponseHeader: function () {
581-
return 'image/png';
582-
},
583-
};
584-
};
585-
defaultController._setXHR(mockXHR);
492+
const response = 'hello';
493+
mockFetch([{ status: 200, response }], { 'Content-Length': 64, 'Content-Type': 'image/png' });
586494
const options = {
587495
requestTask: () => {},
588496
};
589497
const data = await defaultController.download('https://example.com/image.png', options);
590-
expect(data.base64).toBe('ParseA==');
498+
expect(data.base64).toBeDefined();
591499
expect(data.contentType).toBe('image/png');
592500
});
593501

594502
it('download with ajax no response', async () => {
595-
const mockXHR = function () {
596-
return {
597-
DONE: 4,
598-
open: jest.fn(),
599-
send: jest.fn().mockImplementation(function () {
600-
this.response = undefined;
601-
this.readyState = 2;
602-
this.onreadystatechange();
603-
this.readyState = 4;
604-
this.onreadystatechange();
605-
}),
606-
getResponseHeader: function () {
607-
return 'image/png';
608-
},
609-
};
610-
};
611-
defaultController._setXHR(mockXHR);
503+
mockFetch([{ status: 200, response: {} }], { 'Content-Length': 0 });
612504
const options = {
613505
requestTask: () => {},
614506
};
615507
const data = await defaultController.download('https://example.com/image.png', options);
616-
expect(data).toEqual({});
508+
expect(data).toEqual({
509+
base64: '',
510+
contentType: undefined,
511+
});
617512
});
618513

619514
it('download with ajax abort', async () => {
620-
const mockXHR = function () {
621-
return {
622-
open: jest.fn(),
623-
send: jest.fn().mockImplementation(function () {
624-
this.response = [61, 170, 236, 120];
625-
this.readyState = 2;
626-
this.onreadystatechange();
627-
}),
628-
getResponseHeader: function () {
629-
return 'image/png';
630-
},
631-
abort: function () {
632-
this.status = 0;
633-
this.response = undefined;
634-
this.readyState = 4;
635-
this.onreadystatechange();
636-
},
637-
};
638-
};
639-
defaultController._setXHR(mockXHR);
515+
mockFetch([], {}, { name: 'AbortError' });
640516
let _requestTask;
641517
const options = {
642518
requestTask: task => (_requestTask = task),
643519
};
644520
defaultController.download('https://example.com/image.png', options).then(data => {
645521
expect(data).toEqual({});
646522
});
523+
expect(_requestTask).toBeDefined();
524+
expect(_requestTask.abort).toBeDefined();
647525
_requestTask.abort();
648526
});
649527

650528
it('download with ajax error', async () => {
651-
const mockXHR = function () {
652-
return {
653-
open: jest.fn(),
654-
send: jest.fn().mockImplementation(function () {
655-
this.onerror('error thrown');
656-
}),
657-
};
658-
};
659-
defaultController._setXHR(mockXHR);
529+
mockFetch([], {}, new Error('error thrown'));
660530
const options = {
661531
requestTask: () => {},
662532
};
663533
try {
664534
await defaultController.download('https://example.com/image.png', options);
665535
} catch (e) {
666-
expect(e).toBe('error thrown');
667-
}
668-
});
669-
670-
it('download with xmlhttprequest unsupported', async () => {
671-
defaultController._setXHR(null);
672-
process.env.PARSE_BUILD = 'browser';
673-
try {
674-
await defaultController.download('https://example.com/image.png');
675-
} catch (e) {
676-
expect(e).toBe('Cannot make a request: No definition of XMLHttpRequest was found.');
536+
expect(e.message).toBe('error thrown');
677537
}
678538
});
679539

0 commit comments

Comments
 (0)