prettier import { expect } from 'chai' ; import * as sinon from 'sinon' ; import { ajax , AjaxConfig , AjaxResponse , AjaxError , AjaxTimeoutError } from 'rxjs/ajax' ; import { TestScheduler } from 'rxjs/testing' ; import { noop } from 'rxjs' ; import * as nodeFormData from 'form-data' ; const root = ( typeof globalThis !== 'undefined' && globalThis ) || ( typeof self !== 'undefined' && self ) || global ; if ( typeof root . FormData === 'undefined' ) { root . FormData = nodeFormData as any ; } test describe ( 'ajax' , ( ) => { let rXHR ; let sandbox ; beforeEach ( ( ) => { sandbox = sinon . createSandbox ( ) ; rXHR = root . XMLHttpRequest ; root . XMLHttpRequest = MockXMLHttpRequest ; } ) ; afterEach ( ( ) => { sandbox . restore ( ) ; MockXMLHttpRequest . clearRequest ( ) ; root . XMLHttpRequest = rXHR ; root . XDomainRequest = null ; root . ActiveXObject = null ; } ) ; it ( 'should create default XMLHttpRequest for non CORS' , ( ) => { const obj = { url : '/' , method : '' , } ; ajax ( obj ) . subscribe ( ) ; expect ( MockXMLHttpRequest . mostRecent . withCredentials ) . to . be . false ; } ) ; it ( 'should raise an error if not able to create XMLHttpRequest' , ( ) => { root . XMLHttpRequest = null ; root . ActiveXObject = null ; const obj = { url : '/' , method : '' , } ; ajax ( obj ) . subscribe ( { error : ( err ) => expect ( err ) . to . exist } ) ; } ) ; it ( 'should create XMLHttpRequest for CORS' , ( ) => { const obj = { url : '/' , method : '' , crossDomain : true , withCredentials : true , } ; ajax ( obj ) . subscribe ( ) ; expect ( MockXMLHttpRequest . mostRecent . withCredentials ) . to . be . true ; } ) ; it ( 'should raise an error if not able to create CORS request' , ( ) => { root . XMLHttpRequest = null ; root . XDomainRequest = null ; const obj = { url : '/' , method : '' , crossDomain : true , withCredentials : true , } ; ajax ( obj ) . subscribe ( { error : ( err ) => expect ( err ) . to . exist , } ) ; } ) ; it ( 'should set headers' , ( ) => { const obj = { url : '/talk-to-me-goose' , headers : { 'Content-Type' : 'kenny/loggins' , 'Fly-Into-The' : 'Dangah Zone!' , 'Take-A-Ride-Into-The' : 'Danger ZoooOoone!' , } , method : '' , } ; ajax ( obj ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/talk-to-me-goose' ) ; expect ( request . requestHeaders ) . to . deep . equal ( { 'content-type' : 'kenny/loggins' , 'fly-into-the' : 'Dangah Zone!' , 'take-a-ride-into-the' : 'Danger ZoooOoone!' , 'x-requested-with' : 'XMLHttpRequest' , } ) ; expect ( obj . headers ) . to . deep . equal ( { 'Content-Type' : 'kenny/loggins' , 'Fly-Into-The' : 'Dangah Zone!' , 'Take-A-Ride-Into-The' : 'Danger ZoooOoone!' , } ) ; } ) ; describe ( 'ajax XSRF cookie in custom header' , ( ) => { beforeEach ( ( ) => { ( global as any ) . document = { cookie : 'foo=bar' , } as Document ; } ) ; afterEach ( ( ) => { delete ( global as any ) . document ; } ) ; it ( 'should send the cookie with a custom header to the same domain' , ( ) => { const obj = { url : '/some/path' , xsrfCookieName : 'foo' , xsrfHeaderName : 'Custom-Header-Name' , } ; ajax ( obj ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/some/path' ) ; expect ( request . requestHeaders ) . to . deep . equal ( { 'Custom-Header-Name' : 'bar' , 'x-requested-with' : 'XMLHttpRequest' , } ) ; } ) ; it ( 'should send the cookie cross-domain with a custom header when withCredentials is set' , ( ) => { const obj = { url : 'https://some.subresouce.net/some/page' , xsrfCookieName : 'foo' , xsrfHeaderName : 'Custom-Header-Name' , crossDomain : true , withCredentials : true , } ; ajax ( obj ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( 'https://some.subresouce.net/some/page' ) ; expect ( request . requestHeaders ) . to . deep . equal ( { 'Custom-Header-Name' : 'bar' , } ) ; } ) ; it ( 'should not send the cookie cross-domain with a custom header when withCredentials is not set' , ( ) => { const obj = { url : 'https://some.subresouce.net/some/page' , xsrfCookieName : 'foo' , xsrfHeaderName : 'Custom-Header-Name' , crossDomain : true , } ; ajax ( obj ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( 'https://some.subresouce.net/some/page' ) ; expect ( request . requestHeaders ) . to . deep . equal ( { } ) ; } ) ; it ( 'should not send the cookie if there is no xsrfHeaderName option' , ( ) => { const obj = { url : '/some/page' , xsrfCookieName : 'foo' , } ; ajax ( obj ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/some/page' ) ; expect ( request . requestHeaders ) . to . deep . equal ( { 'x-requested-with' : 'XMLHttpRequest' , } ) ; } ) ; } ) ; it ( 'should set the X-Requested-With if crossDomain is false' , ( ) => { ajax ( { url : '/test/monkey' , method : 'GET' , crossDomain : false , } ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . requestHeaders ) . to . deep . equal ( { 'x-requested-with' : 'XMLHttpRequest' , } ) ; } ) ; it ( 'should NOT set the X-Requested-With if crossDomain is true' , ( ) => { ajax ( { url : '/test/monkey' , method : 'GET' , crossDomain : true , } ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . requestHeaders ) . to . not . have . key ( 'x-requested-with' ) ; } ) ; it ( 'should not alter user-provided X-Requested-With header, even if crossDomain is true' , ( ) => { ajax ( { url : '/test/monkey' , method : 'GET' , crossDomain : true , headers : { 'x-requested-with' : 'Custom-XMLHttpRequest' , } , } ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . requestHeaders [ 'x-requested-with' ] ) . to . equal ( 'Custom-XMLHttpRequest' ) ; } ) ; it ( 'should not set default Content-Type header when no body is sent' , ( ) => { const obj = { url : '/talk-to-me-goose' , method : 'GET' , } ; ajax ( obj ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/talk-to-me-goose' ) ; expect ( request . requestHeaders ) . to . not . have . keys ( 'Content-Type' ) ; } ) ; it ( 'should error if createXHR throws' , ( ) => { let error ; ajax ( { url : '/flibbertyJibbet' , responseType : 'text' , createXHR : ( ) => { throw new Error ( 'wokka wokka' ) ; } , } ) . subscribe ( { next : ( ) => { throw new Error ( 'should not next' ) ; } , error : ( err ) => { error = err ; } , complete : ( ) => { throw new Error ( 'should not complete' ) ; } , } ) ; expect ( error ) . to . be . an ( 'error' , 'wokka wokka' ) ; } ) ; it ( 'should error if send request throws' , ( done ) => { const expected = new Error ( 'xhr send failure' ) ; ajax ( { url : '/flibbertyJibbet' , responseType : 'text' , method : '' , createXHR : ( ) => { const ret = new MockXMLHttpRequest ( ) ; ret . send = ( ) => { throw expected ; } ; return ret as any ; } , } ) . subscribe ( { next : ( ) => { done ( new Error ( 'should not be called' ) ) ; } , error : ( e ) => { expect ( e ) . to . be . equal ( expected ) ; done ( ) ; } , complete : ( ) => { done ( new Error ( 'should not be called' ) ) ; } , } ) ; } ) ; it ( 'should succeed on 200' , ( ) => { const expected = { foo : 'bar' } ; let result ; let complete = false ; ajax ( { url : '/flibbertyJibbet' , method : '' , } ) . subscribe ( { next : ( x ) => { result = x ; } , complete : ( ) => { complete = true ; } , } ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; MockXMLHttpRequest . mostRecent . respondWith ( { status : 200 , responseText : JSON . stringify ( expected ) , } ) ; expect ( result ! . xhr ) . exist ; expect ( result ! . response ) . to . deep . equal ( { foo : 'bar' } ) ; expect ( complete ) . to . be . true ; } ) ; it ( 'should fail if fails to parse response in older IE' , ( ) => { let error ; const obj = { url : '/flibbertyJibbet' , method : '' , } ; MockXMLHttpRequest . noResponseProp = true ; ajax ( obj ) . subscribe ( { next : ( ) => { throw new Error ( 'should not next' ) ; } , error : ( err ) => { error = err ; } , complete : ( ) => { throw new Error ( 'should not complete' ) ; } , } ) ; MockXMLHttpRequest . mostRecent . respondWith ( { status : 207 , responseText : 'Wee! I am text, but should be valid JSON!' , } ) ; expect ( error instanceof SyntaxError ) . to . be . true ; expect ( error . message ) . to . equal ( 'Unexpected token W in JSON at position 0' ) ; } ) ; it ( 'should fail on 404' , ( ) => { let error ; const obj = { url : '/flibbertyJibbet' , responseType : 'text' , method : '' , } ; ajax ( obj ) . subscribe ( { next : ( ) => { throw new Error ( 'should not next' ) ; } , error : ( err ) => { error = err ; } , complete : ( ) => { throw new Error ( 'should not complete' ) ; } , } ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; MockXMLHttpRequest . mostRecent . respondWith ( { status : 404 , responseText : 'Wee! I am text!' , } ) ; expect ( error instanceof AjaxError ) . to . be . true ; expect ( error . name ) . to . equal ( 'AjaxError' ) ; expect ( error . message ) . to . equal ( 'ajax error 404' ) ; expect ( error . status ) . to . equal ( 404 ) ; } ) ; it ( 'should succeed on 300' , ( ) => { let result ; let complete = false ; const obj = { url : '/flibbertyJibbet' , responseType : 'text' , method : '' , } ; ajax ( obj ) . subscribe ( { next : ( x ) => { result = x ; } , complete : ( ) => { complete = true ; } , } ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; MockXMLHttpRequest . mostRecent . respondWith ( { status : 300 , responseText : 'Wee! I am text!' , } ) ; expect ( result ! . xhr ) . exist ; expect ( result ! . response ) . to . deep . equal ( 'Wee! I am text!' ) ; expect ( complete ) . to . be . true ; } ) ; it ( 'should not fail if fails to parse error response' , ( ) => { let error ; const obj = { url : '/flibbertyJibbet' , responseType : 'json' , method : '' , } ; ajax ( obj ) . subscribe ( { next : ( ) => { throw new Error ( 'should not next' ) ; } , error : ( err ) => { error = err ; } , complete : ( ) => { throw new Error ( 'should not complete' ) ; } , } ) ; MockXMLHttpRequest . mostRecent . respondWith ( { status : 404 , responseText : 'Unparsable as json' , } ) ; expect ( error instanceof AjaxError ) . to . be . true ; expect ( error . response ) . to . be . null ; } ) ; it ( 'should succeed no settings' , ( ) => { const expected = JSON . stringify ( { foo : 'bar' } ) ; ajax ( '/flibbertyJibbet' ) . subscribe ( { next : ( x ) => { expect ( x . status ) . to . equal ( 200 ) ; expect ( x . xhr . method ) . to . equal ( 'GET' ) ; expect ( x . xhr . responseText ) . to . equal ( expected ) ; } , error : ( ) => { throw 'should not have been called' ; } , } ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; MockXMLHttpRequest . mostRecent . respondWith ( { status : 200 , responseText : expected , } ) ; } ) ; it ( 'should fail no settings' , ( ) => { const expected = JSON . stringify ( { foo : 'bar' } ) ; ajax ( '/flibbertyJibbet' ) . subscribe ( { next : ( ) => { throw 'should not have been called' ; } , error : ( x ) => { expect ( x . status ) . to . equal ( 500 ) ; expect ( x . xhr . method ) . to . equal ( 'GET' ) ; expect ( x . xhr . responseText ) . to . equal ( expected ) ; } , complete : ( ) => { throw 'should not have been called' ; } , } ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; MockXMLHttpRequest . mostRecent . respondWith ( { status : 500 , responseText : expected , } ) ; } ) ; it ( 'should create an asynchronous request' , ( ) => { const obj = { url : '/flibbertyJibbet' , responseType : 'text' , timeout : 10 , } ; ajax ( obj ) . subscribe ( { next : ( x ) => { expect ( x . status ) . to . equal ( 200 ) ; expect ( x . xhr . method ) . to . equal ( 'GET' ) ; expect ( x . xhr . async ) . to . equal ( true ) ; expect ( x . xhr . timeout ) . to . equal ( 10 ) ; expect ( x . xhr . responseType ) . to . equal ( 'text' ) ; } , error : ( ) => { throw 'should not have been called' ; } , } ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/flibbertyJibbet' ) ; request . respondWith ( { status : 200 , responseText : 'Wee! I am text!' , } ) ; } ) ; it ( 'should error on timeout of asynchronous request' , ( ) => { const rxTestScheduler = new TestScheduler ( noop ) ; const obj = { url : '/flibbertyJibbet' , responseType : 'text' , timeout : 10 , } ; ajax ( obj ) . subscribe ( { next : ( ) => { throw 'should not have been called' ; } , error : ( e ) => { expect ( e . status ) . to . equal ( 0 ) ; expect ( e . xhr . method ) . to . equal ( 'GET' ) ; expect ( e . xhr . async ) . to . equal ( true ) ; expect ( e . xhr . timeout ) . to . equal ( 10 ) ; expect ( e . xhr . responseType ) . to . equal ( 'text' ) ; } , } ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/flibbertyJibbet' ) ; rxTestScheduler . schedule ( ( ) => { request . respondWith ( { status : 200 , responseText : 'Wee! I am text!' , } ) ; } , 1000 ) ; rxTestScheduler . flush ( ) ; } ) ; it ( 'should create a synchronous request' , ( ) => { const obj = { url : '/flibbertyJibbet' , responseType : 'text' , timeout : 10 , async : false , } ; ajax ( obj ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; expect ( mockXHR . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( mockXHR . async ) . to . be . false ; mockXHR . respondWith ( { status : 200 , responseText : 'Wee! I am text!' , } ) ; } ) ; describe ( 'ajax request body' , ( ) => { it ( 'can take string body' , ( ) => { const obj = { url : '/flibbertyJibbet' , method : 'POST' , body : 'foobar' , } ; ajax ( obj ) . subscribe ( ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( MockXMLHttpRequest . mostRecent . data ) . to . equal ( 'foobar' ) ; } ) ; it ( 'can take FormData body' , ( ) => { const body = new root . FormData ( ) ; const obj = { url : '/flibbertyJibbet' , method : 'POST' , body : body , } ; ajax ( obj ) . subscribe ( ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( MockXMLHttpRequest . mostRecent . data ) . to . equal ( body ) ; expect ( MockXMLHttpRequest . mostRecent . requestHeaders ) . to . deep . equal ( { 'x-requested-with' : 'XMLHttpRequest' , } ) ; } ) ; it ( 'should send the URLSearchParams straight through to the body' , ( ) => { const body = new URLSearchParams ( { '🌟' : '🚀' , } ) ; const obj = { url : '/flibbertyJibbet' , method : 'POST' , body : body , } ; ajax ( obj ) . subscribe ( ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( MockXMLHttpRequest . mostRecent . data ) . to . equal ( body ) ; } ) ; it ( 'should send by JSON' , ( ) => { const body = { '🌟' : '🚀' , } ; const obj = { url : '/flibbertyJibbet' , method : 'POST' , body : body , } ; ajax ( obj ) . subscribe ( ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( MockXMLHttpRequest . mostRecent . data ) . to . equal ( '{"🌟":"🚀"}' ) ; } ) ; it ( 'should send json body not mattered on case-sensitivity of HTTP headers' , ( ) => { const body = { hello : 'world' , } ; const requestObj = { url : '/flibbertyJibbet' , method : '' , body : body , headers : { 'cOnTeNt-TyPe' : 'application/json;charset=UTF-8' , } , } ; ajax ( requestObj ) . subscribe ( ) ; expect ( MockXMLHttpRequest . mostRecent . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( MockXMLHttpRequest . mostRecent . data ) . to . equal ( '{"hello":"world"}' ) ; } ) ; it ( 'should error if send request throws' , ( done ) => { const expected = new Error ( 'xhr send failure' ) ; const obj = { url : '/flibbertyJibbet' , responseType : 'text' , method : '' , body : 'foobar' , createXHR : ( ) => { const ret = new MockXMLHttpRequest ( ) ; ret . send = ( ) => { throw expected ; } ; return ret as any ; } , } ; ajax ( obj ) . subscribe ( { next : ( ) => { done ( new Error ( 'should not be called' ) ) ; } , error : ( e ) => { expect ( e ) . to . be . equal ( expected ) ; done ( ) ; } , complete : ( ) => { done ( new Error ( 'should not be called' ) ) ; } , } ) ; } ) ; } ) ; describe ( 'ajax.get' , ( ) => { it ( 'should succeed on 200' , ( ) => { const expected = { foo : 'bar' } ; let result ; let complete = false ; ajax . get ( '/flibbertyJibbet' ) . subscribe ( { next : ( x ) => { result = x . response ; } , complete : ( ) => { complete = true ; } , } ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/flibbertyJibbet' ) ; request . respondWith ( { status : 200 , responseText : JSON . stringify ( expected ) , } ) ; expect ( result ) . to . deep . equal ( expected ) ; expect ( complete ) . to . be . true ; } ) ; it ( 'should succeed on 204 No Content' , ( ) => { let result ; let complete = false ; ajax . get ( '/flibbertyJibbet' ) . subscribe ( { next : ( x ) => { result = x . response ; } , complete : ( ) => { complete = true ; } , } ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/flibbertyJibbet' ) ; request . respondWith ( { status : 204 , responseText : '' , } ) ; expect ( result ) . to . be . null ; expect ( complete ) . to . be . true ; } ) ; it ( 'should able to select json response via getJSON' , ( ) => { const expected = { foo : 'bar' } ; let result ; let complete = false ; ajax . getJSON ( '/flibbertyJibbet' ) . subscribe ( { next : ( x ) => { result = x ; } , complete : ( ) => { complete = true ; } , } ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . url ) . to . equal ( '/flibbertyJibbet' ) ; request . respondWith ( { status : 200 , responseText : JSON . stringify ( expected ) , } ) ; expect ( result ) . to . deep . equal ( expected ) ; expect ( complete ) . to . be . true ; } ) ; } ) ; describe ( 'ajax.post' , ( ) => { it ( 'should succeed on 200' , ( ) => { const expected = { foo : 'bar' , hi : 'there you' } ; let result ; let complete = false ; ajax . post ( '/flibbertyJibbet' , expected ) . subscribe ( { next : ( x ) => { result = x ; } , complete : ( ) => { complete = true ; } , } ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . method ) . to . equal ( 'POST' ) ; expect ( request . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( request . requestHeaders ) . to . deep . equal ( { 'content-type' : 'application/json;charset=utf-8' , 'x-requested-with' : 'XMLHttpRequest' , } ) ; request . respondWith ( { status : 200 , responseText : JSON . stringify ( expected ) , } ) ; expect ( request . data ) . to . equal ( JSON . stringify ( expected ) ) ; expect ( result ! . response ) . to . deep . equal ( expected ) ; expect ( complete ) . to . be . true ; } ) ; it ( 'should succeed on 204 No Content' , ( ) => { let result ; let complete = false ; ajax . post ( '/flibbertyJibbet' , undefined ) . subscribe ( { next : ( x ) => { result = x ; } , complete : ( ) => { complete = true ; } , } ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . method ) . to . equal ( 'POST' ) ; expect ( request . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( request . requestHeaders ) . to . deep . equal ( { 'x-requested-with' : 'XMLHttpRequest' , } ) ; request . respondWith ( { status : 204 , responseText : '' , } ) ; expect ( result ! . response ) . to . be . null ; expect ( complete ) . to . be . true ; } ) ; it ( 'should allow partial progressSubscriber ' , function ( ) { const spy = sinon . spy ( ) ; const progressSubscriber = { next : spy , } ; ajax ( { url : '/flibbertyJibbet' , progressSubscriber , } ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; request . respondWith ( { status : 200 , responseText : JSON . stringify ( { } ) , } , { uploadProgressTimes : 3 } ) ; expect ( spy ) . to . be . called . callCount ( 4 ) ; } ) ; it ( 'should emit progress event when progressSubscriber is specified' , function ( ) { const spy = sinon . spy ( ) ; const progressSubscriber = < any > { next : spy , error : ( ) => { } , complete : ( ) => { } , } ; ajax ( { url : '/flibbertyJibbet' , progressSubscriber , } ) . subscribe ( ) ; const request = MockXMLHttpRequest . mostRecent ; request . respondWith ( { status : 200 , responseText : JSON . stringify ( { } ) , } , { uploadProgressTimes : 3 } ) ; expect ( spy ) . to . be . called . callCount ( 4 ) ; } ) ; } ) ; describe ( 'ajax.patch' , ( ) => { it ( 'should create an AjaxObservable with correct options' , ( ) => { const expected = { foo : 'bar' , hi : 'there you' } ; let result ; let complete = false ; ajax . patch ( '/flibbertyJibbet' , expected ) . subscribe ( { next : ( x ) => { result = x ; } , complete : ( ) => { complete = true ; } , } ) ; const request = MockXMLHttpRequest . mostRecent ; expect ( request . method ) . to . equal ( 'PATCH' ) ; expect ( request . url ) . to . equal ( '/flibbertyJibbet' ) ; expect ( request . requestHeaders ) . to . deep . equal ( { 'content-type' : 'application/json;charset=utf-8' , 'x-requested-with' : 'XMLHttpRequest' , } ) ; request . respondWith ( { status : 200 , responseText : JSON . stringify ( expected ) , } ) ; expect ( request . data ) . to . equal ( JSON . stringify ( expected ) ) ; expect ( result ! . response ) . to . deep . equal ( expected ) ; expect ( complete ) . to . be . true ; } ) ; } ) ; describe ( 'ajax error classes' , ( ) => { describe ( 'AjaxError' , ( ) => { it ( 'should extend Error class' , ( ) => { const error = new AjaxError ( 'Test error' , new XMLHttpRequest ( ) , { url : '/' , method : 'GET' , responseType : 'json' , headers : { } , withCredentials : false , async : true , timeout : 0 , crossDomain : false , } ) ; expect ( error ) . to . be . an . instanceOf ( Error ) ; } ) ; } ) ; describe ( 'AjaxTimeoutError' , ( ) => { it ( 'should extend Error class' , ( ) => { const error = new AjaxTimeoutError ( new XMLHttpRequest ( ) , { url : '/' , method : 'GET' , responseType : 'json' , headers : { } , withCredentials : false , async : true , timeout : 0 , crossDomain : false , } ) ; expect ( error ) . to . be . an . instanceOf ( Error ) ; } ) ; it ( 'should extend AjaxError class' , ( ) => { const error = new AjaxTimeoutError ( new XMLHttpRequest ( ) , { url : '/' , method : 'GET' , responseType : 'json' , headers : { } , withCredentials : false , async : true , timeout : 0 , crossDomain : false , } ) ; expect ( error ) . to . be . an . instanceOf ( AjaxError ) ; } ) ; } ) ; } ) ; it ( 'should error if aborted early' , ( ) => { let thrown = null ; ajax ( { method : 'GET' , url : '/flibbertyJibbett' , } ) . subscribe ( { next : ( ) => { throw new Error ( 'should not be called' ) ; } , error : ( err ) => { thrown = err ; } , } ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; expect ( thrown ) . to . be . null ; mockXHR . triggerEvent ( 'abort' , { type : 'abort' } ) ; expect ( thrown ) . to . be . an . instanceOf ( AjaxError ) ; expect ( thrown . message ) . to . equal ( 'aborted' ) ; } ) ; describe ( 'with includeDownloadProgress' , ( ) => { it ( 'should emit download progress' , ( ) => { const results = [ ] ; ajax ( { method : 'GET' , url : '/flibbertyJibbett' , includeDownloadProgress : true , } ) . subscribe ( { next : ( value ) => results . push ( value ) , complete : ( ) => results . push ( 'done' ) , } ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , total : 5 , loaded : 5 , responseText : JSON . stringify ( { boo : 'I am a ghost' } ) , } , { uploadProgressTimes : 5 , downloadProgressTimes : 5 } ) ; const request = { async : true , body : undefined , crossDomain : false , headers : { 'x-requested-with' : 'XMLHttpRequest' , } , includeDownloadProgress : true , method : 'GET' , responseType : 'json' , timeout : 0 , url : '/flibbertyJibbett' , withCredentials : false , } ; expect ( results ) . to . deep . equal ( [ { type : 'download_loadstart' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 0 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'loadstart' , loaded : 0 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 1 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 1 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 2 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 2 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 3 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 3 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 4 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 4 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 5 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 5 , total : 5 } , } , { type : 'download_load' , loaded : 5 , total : 5 , request , originalEvent : { type : 'load' , loaded : 5 , total : 5 } , xhr : mockXHR , response : { boo : 'I am a ghost' } , responseHeaders : { } , responseType : 'json' , status : 200 , } , 'done' , ] ) ; } ) ; it ( 'should emit upload and download progress' , ( ) => { const results = [ ] ; ajax ( { method : 'GET' , url : '/flibbertyJibbett' , includeUploadProgress : true , includeDownloadProgress : true , } ) . subscribe ( { next : ( value ) => results . push ( value ) , complete : ( ) => results . push ( 'done' ) , } ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , total : 5 , loaded : 5 , responseText : JSON . stringify ( { boo : 'I am a ghost' } ) , } , { uploadProgressTimes : 5 , downloadProgressTimes : 5 } ) ; const request = { async : true , body : undefined , crossDomain : false , headers : { 'x-requested-with' : 'XMLHttpRequest' , } , includeUploadProgress : true , includeDownloadProgress : true , method : 'GET' , responseType : 'json' , timeout : 0 , url : '/flibbertyJibbett' , withCredentials : false , } ; expect ( results ) . to . deep . equal ( [ { type : 'upload_loadstart' , loaded : 0 , total : 5 , request , status : 0 , response : undefined , responseHeaders : { } , responseType : 'json' , xhr : mockXHR , originalEvent : { type : 'loadstart' , loaded : 0 , total : 5 } , } , { type : 'upload_progress' , loaded : 1 , total : 5 , request , status : 0 , response : undefined , responseHeaders : { } , responseType : 'json' , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 1 , total : 5 } , } , { type : 'upload_progress' , loaded : 2 , total : 5 , request , status : 0 , response : undefined , responseHeaders : { } , responseType : 'json' , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 2 , total : 5 } , } , { type : 'upload_progress' , loaded : 3 , total : 5 , request , status : 0 , response : undefined , responseHeaders : { } , responseType : 'json' , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 3 , total : 5 } , } , { type : 'upload_progress' , loaded : 4 , total : 5 , request , status : 0 , response : undefined , responseHeaders : { } , responseType : 'json' , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 4 , total : 5 } , } , { type : 'upload_progress' , loaded : 5 , total : 5 , request , status : 0 , response : undefined , responseHeaders : { } , responseType : 'json' , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 5 , total : 5 } , } , { type : 'upload_load' , loaded : 5 , total : 5 , request , status : 0 , response : undefined , responseHeaders : { } , responseType : 'json' , xhr : mockXHR , originalEvent : { type : 'load' , loaded : 5 , total : 5 } , } , { type : 'download_loadstart' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 0 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'loadstart' , loaded : 0 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 1 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 1 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 2 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 2 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 3 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 3 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 4 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 4 , total : 5 } , } , { type : 'download_progress' , responseHeaders : { } , responseType : 'json' , response : undefined , loaded : 5 , total : 5 , request , status : 0 , xhr : mockXHR , originalEvent : { type : 'progress' , loaded : 5 , total : 5 } , } , { type : 'download_load' , loaded : 5 , total : 5 , request , originalEvent : { type : 'load' , loaded : 5 , total : 5 } , xhr : mockXHR , response : { boo : 'I am a ghost' } , responseHeaders : { } , responseType : 'json' , status : 200 , } , 'done' , ] ) ; } ) ; } ) ; it ( 'should return an object that allows access to response headers' , ( ) => { const sentResponseHeaders = { 'content-type' : 'application/json' , 'x-custom-header' : 'test' , 'x-headers-are-fun' : '<whatever/> {"weird": "things"}' , } ; ajax ( { method : 'GET' , url : '/whatever' , } ) . subscribe ( ( response ) => { expect ( response . responseHeaders ) . to . deep . equal ( sentResponseHeaders ) ; } ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , headers : sentResponseHeaders , responseText : JSON . stringify ( { iam : 'tired' , and : 'should go to bed' , but : 'I am doing open source for no good reason' } ) , } ) ; expect ( mockXHR . getAllResponseHeaders ( ) ) . to . equal ( `content-type: application/json
x-custom-header: test
x-headers-are-fun: <whatever/> {"weird": "things"}` ) ; } ) ; describe ( 'with queryParams' , ( ) => { it ( 'should allow passing of search queryParams as a dictionary' , ( ) => { ajax ( { method : 'GET' , url : '/whatever' , queryParams : { foo : 'bar' , whatever : '123' } , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?foo=bar&whatever=123' ) ; } ) ; it ( 'should allow passing of search queryParams as an entries array' , ( ) => { ajax ( { method : 'GET' , url : '/whatever' , queryParams : [ [ 'foo' , 'bar' ] , [ 'whatever' , '123' ] , ] , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?foo=bar&whatever=123' ) ; } ) ; it ( 'should allow passing of search queryParams as a string' , ( ) => { ajax ( { method : 'GET' , url : '/whatever' , queryParams : '?foo=bar&whatever=123' , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?foo=bar&whatever=123' ) ; } ) ; it ( 'should allow passing of search queryParams as a URLSearchParams object' , ( ) => { const queryParams = new URLSearchParams ( ) ; queryParams . set ( 'foo' , 'bar' ) ; queryParams . set ( 'whatever' , '123' ) ; ajax ( { method : 'GET' , url : '/whatever' , queryParams , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?foo=bar&whatever=123' ) ; } ) ; it ( 'should not screw things up if there is an existing search string in the url passed' , ( ) => { ajax ( { method : 'GET' , url : '/whatever?jays_face=is+a+param&lol=haha' , queryParams : { foo : 'bar' , whatever : '123' } , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?jays_face=is+a+param&lol=haha&foo=bar&whatever=123' ) ; } ) ; it ( 'should overwrite existing args from existing search strings in the url passed' , ( ) => { ajax ( { method : 'GET' , url : '/whatever?terminator=2&uncle_bob=huh' , queryParams : { uncle_bob : '...okayyyyyyy' , movie_quote : 'yes' } , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?terminator=2&uncle_bob=...okayyyyyyy&movie_quote=yes' ) ; } ) ; it ( 'should properly encode values' , ( ) => { ajax ( { method : 'GET' , url : '/whatever' , queryParams : { 'this is a weird param name' : '?#* value here rofl !!!' } , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?this+is+a+weird+param+name=%3F%23*+value+here+rofl+%21%21%21' ) ; } ) ; it ( 'should handle dictionaries that have numbers, booleans, and arrays of numbers, strings or booleans' , ( ) => { ajax ( { method : 'GET' , url : '/whatever' , queryParams : { a : 123 , b : true , c : [ 'one' , 'two' , 'three' ] , d : [ 1 , 3 , 3 , 7 ] , e : [ true , false , true ] } , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?a=123&b=true&c=one%2Ctwo%2Cthree&d=1%2C3%2C3%2C7&e=true%2Cfalse%2Ctrue' ) ; } ) ; it ( 'should handle entries that have numbers, booleans, and arrays of numbers, strings or booleans' , ( ) => { ajax ( { method : 'GET' , url : '/whatever' , queryParams : [ [ 'a' , 123 ] , [ 'b' , true ] , [ 'c' , [ 'one' , 'two' , 'three' ] ] , [ 'd' , [ 1 , 3 , 3 , 7 ] ] , [ 'e' , [ true , false , true ] ] , ] , } ) . subscribe ( ) ; const mockXHR = MockXMLHttpRequest . mostRecent ; mockXHR . respondWith ( { status : 200 , responseText : JSON . stringify ( { whatever : 'I want' } ) , } ) ; expect ( mockXHR . url ) . to . equal ( '/whatever?a=123&b=true&c=one%2Ctwo%2Cthree&d=1%2C3%2C3%2C7&e=true%2Cfalse%2Ctrue' ) ; } ) ; } ) ; } ) ; class MockXHREventTarget { private registry = new Map < string , Set < ( e : ProgressEvent ) => void > > ( ) ; addEventListener ( type , handler ) { let handlers = this . registry . get ( type ) ; if ( ! handlers ) { this . registry . set ( type , ( handlers = new Set ( ) ) ) ; } handlers . add ( handler ) ; } removeEventListener ( type , handler ) { this . registry . get ( type ) ?. delete ( handler ) ; } dispatchEvent ( event ) { const { type } = event ; const handlers = this . registry . get ( type ) ; if ( handlers ) { for ( const handler of handlers ) { handler ( event ) ; } } } } class MockXMLHttpRequest extends MockXHREventTarget { static readonly DONE = 4 ; /**
     * Set to `true` to test IE code paths.
     */ static noResponseProp = false ; private static requests : Array < MockXMLHttpRequest > = [ ] ; private static recentRequest : MockXMLHttpRequest ; static get mostRecent ( ) : MockXMLHttpRequest { return MockXMLHttpRequest . recentRequest ; } static get allRequests ( ) : Array < MockXMLHttpRequest > { return MockXMLHttpRequest . requests ; } static clearRequest ( ) { MockXMLHttpRequest . noResponseProp = false ; MockXMLHttpRequest . requests . length = 0 ; MockXMLHttpRequest . recentRequest = null ! ; } protected responseType : string = '' ; private readyState : number = 0 ; /**
     * Used to test if `open` was called with `async` true or false.
     */ public async : boolean = true ; protected status : any ; protected responseText : string | undefined ; protected response : any = undefined ; url : any ; method : any ; data : any ; requestHeaders : any = { } ; withCredentials : boolean = false ; onreadystatechange : ( e : ProgressEvent ) => any ; onerror : ( e : ErrorEvent ) => any ; onprogress : ( e : ProgressEvent ) => any ; ontimeout : ( e : ProgressEvent ) => any ; upload : XMLHttpRequestUpload = new MockXHREventTarget ( ) as any ; constructor ( ) { super ( ) ; MockXMLHttpRequest . recentRequest = this ; MockXMLHttpRequest . requests . push ( this ) ; if ( MockXMLHttpRequest . noResponseProp ) { delete this [ 'response' ] ; } } timeout : number ; send ( data ) { this . data = data ; if ( this . timeout && this . timeout > 0 ) { setTimeout ( ( ) => { if ( this . readyState != 4 ) { this . readyState = 4 ; this . status = 0 ; this . triggerEvent ( 'readystatechange' ) ; this . triggerEvent ( 'timeout' ) ; } } , this . timeout ) ; } } abort ( ) { } open ( method , url , async ) { this . method = method ; this . url = url ; this . async = async ; this . readyState = 1 ; this . triggerEvent ( 'readystatechange' ) ; const originalProgressHandler = this . upload . onprogress ; Object . defineProperty ( this . upload , 'progress' , { get ( ) { return originalProgressHandler ; } , } ) ; } setRequestHeader ( key , value ) { this . requestHeaders [ key ] = value ; } private _responseHeaders : any ; getAllResponseHeaders ( ) { return this . _responseHeaders ? Object . entries ( this . _responseHeaders ) . map ( ( entryParts ) => entryParts . join ( ': ' ) ) . join ( '\n' ) : '' ; } respondWith ( response , config ? ) { const { uploadProgressTimes = 0 , downloadProgressTimes = 0 } = config ?? { } ; if ( uploadProgressTimes ) { this . triggerUploadEvent ( 'loadstart' , { type : 'loadstart' , total : uploadProgressTimes , loaded : 0 } ) ; for ( let i = 1 ; i <= uploadProgressTimes ; i ++ ) { this . triggerUploadEvent ( 'progress' , { type : 'progress' , total : uploadProgressTimes , loaded : i } ) ; } this . triggerUploadEvent ( 'load' , { type : 'load' , total : uploadProgressTimes , loaded : uploadProgressTimes } ) ; } if ( downloadProgressTimes ) { this . triggerEvent ( 'loadstart' , { type : 'loadstart' , total : downloadProgressTimes , loaded : 0 } ) ; for ( let i = 1 ; i <= downloadProgressTimes ; i ++ ) { this . triggerEvent ( 'progress' , { type : 'progress' , total : downloadProgressTimes , loaded : i } ) ; } } this . _responseHeaders = response . headers ; this . readyState = 4 ; this . status = response . status || 200 ; this . responseText = response . responseText ; switch ( this . responseType ) { case 'json' : try { this . response = JSON . parse ( response . responseText ! ) ; } catch ( err ) { this . response = null ; } break ; case 'arraybuffer' : case 'document' : case 'blob' : throw new Error ( 'Test harness does not support the responseType: ' + this . responseType ) ; case 'text' : case '' : default : this . response = response . responseText ; break ; } if ( MockXMLHttpRequest . noResponseProp ) { delete this [ 'response' ] ; } this . triggerEvent ( 'load' , { type : 'load' , total : response . total ?? 0 , loaded : response . loaded ?? 0 } ) ; this . triggerEvent ( 'readystatechange' , { type : 'readystatechange' } ) ; } triggerEvent ( this , name , eventObj ? ) { const e = eventObj || { type : name } ; this . dispatchEvent ( { type : name , ... eventObj } ) ; if ( this [ 'on' + name ] ) { this [ 'on' + name ] ( e ) ; } } triggerUploadEvent ( this , name , eventObj ? ) { const e = eventObj || { } ; this . upload . dispatchEvent ( { type : name , ... eventObj } ) ; if ( this . upload [ 'on' + name ] ) { this . upload [ 'on' + name ] ( e ) ; } } }