旧版报表、仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1467 lines
49 KiB

var IEorEdge = /Edge\//.test(navigator.userAgent) || /MSIE/.test(navigator.userAgent)
var Chrome = /Chrome\//.test(navigator.userAgent) && !IEorEdge
var Safari = /Safari\//.test(navigator.userAgent) && !IEorEdge && !Chrome
var support = {
url: (function(url) {
try {
return new URL(url).toString() === url
} catch (e) {
return false
}
})('http://example.com/'),
blob:
'FileReader' in self &&
'Blob' in self &&
(function() {
try {
new Blob()
return true
} catch (e) {
return false
}
})(),
formData: 'FormData' in self,
arrayBuffer: 'ArrayBuffer' in self,
aborting: 'signal' in new Request(''),
permanentRedirect: !/Trident/.test(navigator.userAgent)
}
function readBlobAsText(blob) {
if ('FileReader' in self) {
return new Promise(function(resolve, reject) {
var reader = new FileReader()
reader.onload = function() {
resolve(reader.result)
}
reader.onerror = function() {
reject(reader.error)
}
reader.readAsText(blob)
})
} else if ('FileReaderSync' in self) {
return new FileReaderSync().readAsText(blob)
} else {
throw new ReferenceError('FileReader is not defined')
}
}
function readBlobAsBytes(blob) {
if ('FileReader' in self) {
return new Promise(function(resolve, reject) {
var reader = new FileReader()
reader.onload = function() {
var view = new Uint8Array(reader.result)
resolve(Array.prototype.slice.call(view))
}
reader.onerror = function() {
reject(reader.error)
}
reader.readAsArrayBuffer(blob)
})
} else if ('FileReaderSync' in self) {
return new FileReaderSync().readAsArrayBuffer(blob)
} else {
throw new ReferenceError('FileReader is not defined')
}
}
function arrayBufferFromText(text) {
var buf = new ArrayBuffer(text.length)
var view = new Uint8Array(buf)
for (var i = 0; i < text.length; i++) {
view[i] = text.charCodeAt(i)
}
return buf
}
function readArrayBufferAsText(buf) {
var view = new Uint8Array(buf)
var chars = new Array(view.length)
for (var i = 0; i < view.length; i++) {
chars[i] = String.fromCharCode(view[i])
}
return chars.join('')
}
var preservedGlobals = {}
var keepGlobals = ['fetch', 'Headers', 'Request', 'Response']
var exercise = ['polyfill']
// If native fetch implementation exists, replace it with the polyfilled
// version at first. The native implementation will be restored before the
// additional `native` pass of the test suite.
if (!self.fetch.polyfill) {
keepGlobals.forEach(function(name) {
preservedGlobals[name] = self[name]
self[name] = WHATWGFetch[name]
})
exercise.push('native')
}
var slice = Array.prototype.slice
function featureDependent(testOrSuite, condition) {
// eslint-disable-next-line no-invalid-this
;(condition ? testOrSuite : testOrSuite.skip).apply(this, slice.call(arguments, 2))
}
exercise.forEach(function(exerciseMode) {
suite(exerciseMode, function() {
if (exerciseMode === 'native') {
suiteSetup(function() {
keepGlobals.forEach(function(name) {
self[name] = preservedGlobals[name]
})
})
}
var nativeChrome = Chrome && exerciseMode === 'native'
var nativeSafari = Safari && exerciseMode === 'native'
var nativeEdge = /Edge\//.test(navigator.userAgent) && exerciseMode === 'native'
var firefox = navigator.userAgent.match(/Firefox\/(\d+)/)
var brokenFF = firefox && firefox[1] <= 56 && exerciseMode === 'native'
var polyfillFirefox = firefox && exerciseMode === 'polyfill'
var omitSafari =
Safari && exerciseMode === 'native' && navigator.userAgent.match(/Version\/(\d+\.\d+)/)[1] <= '11.1'
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
function testBodyExtract(factory) {
suite('body extract', function() {
var expected = 'Hello World!'
var inputs = [['type USVString', expected]]
if (support.blob) {
inputs.push(['type Blob', new Blob([expected])])
}
if (support.arrayBuffer) {
inputs = inputs.concat([
['type ArrayBuffer', arrayBufferFromText(expected)],
['type TypedArray', new Uint8Array(arrayBufferFromText(expected))],
['type DataView', new DataView(arrayBufferFromText(expected))]
])
}
inputs.forEach(function(input) {
var typeLabel = input[0],
body = input[1]
suite(typeLabel, function() {
featureDependent(test, support.blob, 'consume as blob', function() {
var r = factory(body)
return r
.blob()
.then(readBlobAsText)
.then(function(text) {
assert.equal(text, expected)
})
})
test('consume as text', function() {
var r = factory(body)
return r.text().then(function(text) {
assert.equal(text, expected)
})
})
featureDependent(test, support.arrayBuffer, 'consume as array buffer', function() {
var r = factory(body)
return r
.arrayBuffer()
.then(readArrayBufferAsText)
.then(function(text) {
assert.equal(text, expected)
})
})
})
})
})
}
// https://fetch.spec.whatwg.org/#headers-class
suite('Headers', function() {
test('constructor copies headers', function() {
var original = new Headers()
original.append('Accept', 'application/json')
original.append('Accept', 'text/plain')
original.append('Content-Type', 'text/html')
var headers = new Headers(original)
assert.equal(headers.get('Accept'), 'application/json, text/plain')
assert.equal(headers.get('Content-type'), 'text/html')
})
test('constructor works with arrays', function() {
var array = [['Content-Type', 'text/xml'], ['Breaking-Bad', '<3']]
var headers = new Headers(array)
assert.equal(headers.get('Content-Type'), 'text/xml')
assert.equal(headers.get('Breaking-Bad'), '<3')
})
test('headers are case insensitive', function() {
var headers = new Headers({Accept: 'application/json'})
assert.equal(headers.get('ACCEPT'), 'application/json')
assert.equal(headers.get('Accept'), 'application/json')
assert.equal(headers.get('accept'), 'application/json')
})
test('appends to existing', function() {
var headers = new Headers({Accept: 'application/json'})
assert.isFalse(headers.has('Content-Type'))
headers.append('Content-Type', 'application/json')
assert.isTrue(headers.has('Content-Type'))
assert.equal(headers.get('Content-Type'), 'application/json')
})
test('appends values to existing header name', function() {
var headers = new Headers({Accept: 'application/json'})
headers.append('Accept', 'text/plain')
assert.equal(headers.get('Accept'), 'application/json, text/plain')
})
test('sets header name and value', function() {
var headers = new Headers()
headers.set('Content-Type', 'application/json')
assert.equal(headers.get('Content-Type'), 'application/json')
})
test('returns null on no header found', function() {
var headers = new Headers()
assert.isNull(headers.get('Content-Type'))
})
test('has headers that are set', function() {
var headers = new Headers()
headers.set('Content-Type', 'application/json')
assert.isTrue(headers.has('Content-Type'))
})
test('deletes headers', function() {
var headers = new Headers()
headers.set('Content-Type', 'application/json')
assert.isTrue(headers.has('Content-Type'))
headers.delete('Content-Type')
assert.isFalse(headers.has('Content-Type'))
assert.isNull(headers.get('Content-Type'))
})
test('converts field name to string on set and get', function() {
var headers = new Headers()
headers.set(1, 'application/json')
assert.isTrue(headers.has('1'))
assert.equal(headers.get(1), 'application/json')
})
test('converts field value to string on set and get', function() {
var headers = new Headers()
headers.set('Content-Type', 1)
headers.set('X-CSRF-Token', undefined)
assert.equal(headers.get('Content-Type'), '1')
assert.equal(headers.get('X-CSRF-Token'), 'undefined')
})
test('throws TypeError on invalid character in field name', function() {
assert.throws(function() {
new Headers({'[Accept]': 'application/json'})
}, TypeError)
assert.throws(function() {
new Headers({'Accept:': 'application/json'})
}, TypeError)
assert.throws(function() {
var headers = new Headers()
headers.set({field: 'value'}, 'application/json')
}, TypeError)
assert.throws(function() {
new Headers({'': 'application/json'})
}, TypeError)
})
featureDependent(test, !brokenFF, 'is iterable with forEach', function() {
var headers = new Headers()
headers.append('Accept', 'application/json')
headers.append('Accept', 'text/plain')
headers.append('Content-Type', 'text/html')
var results = []
headers.forEach(function(value, key, object) {
results.push({value: value, key: key, object: object})
})
assert.equal(results.length, 2)
assert.deepEqual({key: 'accept', value: 'application/json, text/plain', object: headers}, results[0])
assert.deepEqual({key: 'content-type', value: 'text/html', object: headers}, results[1])
})
test('forEach accepts second thisArg argument', function() {
var headers = new Headers({Accept: 'application/json'})
var thisArg = 42
headers.forEach(function() {
assert.equal(this, thisArg)
}, thisArg)
})
featureDependent(test, !brokenFF, 'is iterable with keys', function() {
var headers = new Headers()
headers.append('Accept', 'application/json')
headers.append('Accept', 'text/plain')
headers.append('Content-Type', 'text/html')
var iterator = headers.keys()
assert.deepEqual({done: false, value: 'accept'}, iterator.next())
assert.deepEqual({done: false, value: 'content-type'}, iterator.next())
assert.deepEqual({done: true, value: undefined}, iterator.next())
})
featureDependent(test, !brokenFF, 'is iterable with values', function() {
var headers = new Headers()
headers.append('Accept', 'application/json')
headers.append('Accept', 'text/plain')
headers.append('Content-Type', 'text/html')
var iterator = headers.values()
assert.deepEqual({done: false, value: 'application/json, text/plain'}, iterator.next())
assert.deepEqual({done: false, value: 'text/html'}, iterator.next())
assert.deepEqual({done: true, value: undefined}, iterator.next())
})
featureDependent(test, !brokenFF, 'is iterable with entries', function() {
var headers = new Headers()
headers.append('Accept', 'application/json')
headers.append('Accept', 'text/plain')
headers.append('Content-Type', 'text/html')
var iterator = headers.entries()
assert.deepEqual({done: false, value: ['accept', 'application/json, text/plain']}, iterator.next())
assert.deepEqual({done: false, value: ['content-type', 'text/html']}, iterator.next())
assert.deepEqual({done: true, value: undefined}, iterator.next())
})
})
// https://fetch.spec.whatwg.org/#request-class
suite('Request', function() {
test('construct with string url', function() {
var request = new Request('https://fetch.spec.whatwg.org/')
assert.equal(request.url, 'https://fetch.spec.whatwg.org/')
})
featureDependent(test, support.url, 'construct with URL instance', function() {
var url = new URL('https://fetch.spec.whatwg.org/')
url.pathname = 'cors'
var request = new Request(url)
assert.equal(request.url, 'https://fetch.spec.whatwg.org/cors')
})
test('construct with non-Request object', function() {
var url = {
toString: function() {
return 'https://fetch.spec.whatwg.org/'
}
}
var request = new Request(url)
assert.equal(request.url, 'https://fetch.spec.whatwg.org/')
})
test('construct with Request', function() {
var request1 = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out',
headers: {
accept: 'application/json',
'Content-Type': 'text/plain'
}
})
var request2 = new Request(request1)
return request2.text().then(function(body2) {
assert.equal(body2, 'I work out')
assert.equal(request2.method, 'POST')
assert.equal(request2.url, 'https://fetch.spec.whatwg.org/')
assert.equal(request2.headers.get('accept'), 'application/json')
assert.equal(request2.headers.get('content-type'), 'text/plain')
return request1.text().then(
function() {
assert(false, 'original request body should have been consumed')
},
function(error) {
assert(error instanceof TypeError, 'expected TypeError for already read body')
}
)
})
})
test('construct with Request and override headers', function() {
var request1 = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out',
headers: {
accept: 'application/json',
'X-Request-ID': '123'
}
})
var request2 = new Request(request1, {
headers: {'x-test': '42'}
})
assert.equal(request2.headers.get('accept'), undefined)
assert.equal(request2.headers.get('x-request-id'), undefined)
assert.equal(request2.headers.get('x-test'), '42')
})
test('construct with Request and override body', function() {
var request1 = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out',
headers: {
'Content-Type': 'text/plain'
}
})
var request2 = new Request(request1, {
body: '{"wiggles": 5}',
headers: {'Content-Type': 'application/json'}
})
return request2.json().then(function(data) {
assert.equal(data.wiggles, 5)
assert.equal(request2.headers.get('content-type'), 'application/json')
})
})
featureDependent(test, !nativeChrome, 'construct with used Request body', function() {
var request1 = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out'
})
return request1.text().then(function() {
assert.throws(function() {
new Request(request1)
}, TypeError)
})
})
test('GET should not have implicit Content-Type', function() {
var req = new Request('https://fetch.spec.whatwg.org/')
assert.equal(req.headers.get('content-type'), undefined)
})
test('POST with blank body should not have implicit Content-Type', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post'
})
assert.equal(req.headers.get('content-type'), undefined)
})
test('construct with string body sets Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out'
})
assert.equal(req.headers.get('content-type'), 'text/plain;charset=UTF-8')
})
featureDependent(test, support.blob, 'construct with Blob body and type sets Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: new Blob(['test'], {type: 'image/png'})
})
assert.equal(req.headers.get('content-type'), 'image/png')
})
test('construct with body and explicit header uses header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: {'Content-Type': 'image/png'},
body: 'I work out'
})
assert.equal(req.headers.get('content-type'), 'image/png')
})
featureDependent(test, support.blob, 'construct with Blob body and explicit Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: {'Content-Type': 'image/png'},
body: new Blob(['test'], {type: 'text/plain'})
})
assert.equal(req.headers.get('content-type'), 'image/png')
})
featureDependent(test, !IEorEdge, 'construct with URLSearchParams body sets Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: new URLSearchParams('a=1&b=2')
})
assert.equal(req.headers.get('content-type'), 'application/x-www-form-urlencoded;charset=UTF-8')
})
featureDependent(
test,
!IEorEdge,
'construct with URLSearchParams body and explicit Content-Type header',
function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: {'Content-Type': 'image/png'},
body: new URLSearchParams('a=1&b=2')
})
assert.equal(req.headers.get('content-type'), 'image/png')
}
)
test('construct with unsupported body type', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: {}
})
assert.equal(req.headers.get('content-type'), 'text/plain;charset=UTF-8')
return req.text().then(function(bodyText) {
assert.equal(bodyText, '[object Object]')
})
})
test('construct with null body', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post'
})
assert.isNull(req.headers.get('content-type'))
return req.text().then(function(bodyText) {
assert.equal(bodyText, '')
})
})
test('clone GET request', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
headers: {'content-type': 'text/plain'}
})
var clone = req.clone()
assert.equal(clone.url, req.url)
assert.equal(clone.method, 'GET')
assert.equal(clone.headers.get('content-type'), 'text/plain')
assert.notEqual(clone.headers, req.headers)
assert.isFalse(req.bodyUsed)
})
test('clone POST request', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: {'content-type': 'text/plain'},
body: 'I work out'
})
var clone = req.clone()
assert.equal(clone.method, 'POST')
assert.equal(clone.headers.get('content-type'), 'text/plain')
assert.notEqual(clone.headers, req.headers)
assert.equal(req.bodyUsed, false)
return Promise.all([clone.text(), req.clone().text()]).then(function(bodies) {
assert.deepEqual(bodies, ['I work out', 'I work out'])
})
})
featureDependent(test, !nativeChrome, 'clone with used Request body', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out'
})
return req.text().then(function() {
assert.throws(function() {
req.clone()
}, TypeError)
})
})
testBodyExtract(function(body) {
return new Request('', {method: 'POST', body: body})
})
featureDependent(test, !omitSafari, 'credentials defaults to same-origin', function() {
var request = new Request('')
assert.equal(request.credentials, 'same-origin')
})
test('credentials is overridable', function() {
var request = new Request('', {credentials: 'omit'})
assert.equal(request.credentials, 'omit')
})
})
// https://fetch.spec.whatwg.org/#response-class
suite('Response', function() {
test('default status is 200 OK', function() {
var res = new Response()
assert.equal(res.status, 200)
assert.equal(res.statusText, 'OK')
assert.isTrue(res.ok)
})
test('default status is 200 OK when an explicit undefined status code is passed', function() {
var res = new Response('', {status: undefined})
assert.equal(res.status, 200)
assert.equal(res.statusText, 'OK')
assert.isTrue(res.ok)
})
testBodyExtract(function(body) {
return new Response(body)
})
test('creates Headers object from raw headers', function() {
var r = new Response('{"foo":"bar"}', {headers: {'content-type': 'application/json'}})
assert.equal(r.headers instanceof Headers, true)
return r.json().then(function(json) {
assert.equal(json.foo, 'bar')
return json
})
})
test('always creates a new Headers instance', function() {
var headers = new Headers({'x-hello': 'world'})
var res = new Response('', {headers: headers})
assert.equal(res.headers.get('x-hello'), 'world')
assert.notEqual(res.headers, headers)
})
test('clone text response', function() {
var res = new Response('{"foo":"bar"}', {
headers: {'content-type': 'application/json'}
})
var clone = res.clone()
assert.notEqual(clone.headers, res.headers, 'headers were cloned')
assert.equal(clone.headers.get('content-type'), 'application/json')
return Promise.all([clone.json(), res.json()]).then(function(jsons) {
assert.deepEqual(jsons[0], jsons[1], 'json of cloned object is the same as original')
})
})
featureDependent(test, support.blob, 'clone blob response', function() {
var req = new Request(new Blob(['test']))
req.clone()
assert.equal(req.bodyUsed, false)
})
test('error creates error Response', function() {
var r = Response.error()
assert(r instanceof Response)
assert.equal(r.status, 0)
assert.equal(r.statusText, '')
assert.equal(r.type, 'error')
})
test('redirect creates redirect Response', function() {
var r = Response.redirect('https://fetch.spec.whatwg.org/', 301)
assert(r instanceof Response)
assert.equal(r.status, 301)
assert.equal(r.headers.get('Location'), 'https://fetch.spec.whatwg.org/')
})
test('construct with string body sets Content-Type header', function() {
var r = new Response('I work out')
assert.equal(r.headers.get('content-type'), 'text/plain;charset=UTF-8')
})
featureDependent(test, support.blob, 'construct with Blob body and type sets Content-Type header', function() {
var r = new Response(new Blob(['test'], {type: 'text/plain'}))
assert.equal(r.headers.get('content-type'), 'text/plain')
})
test('construct with body and explicit header uses header', function() {
var r = new Response('I work out', {
headers: {
'Content-Type': 'text/plain'
}
})
assert.equal(r.headers.get('content-type'), 'text/plain')
})
test('init object as first argument', function() {
var r = new Response({
status: 201,
headers: {
'Content-Type': 'text/html'
}
})
assert.equal(r.status, 200)
assert.equal(r.headers.get('content-type'), 'text/plain;charset=UTF-8')
return r.text().then(function(bodyText) {
assert.equal(bodyText, '[object Object]')
})
})
test('null as first argument', function() {
var r = new Response(null)
assert.isNull(r.headers.get('content-type'))
return r.text().then(function(bodyText) {
assert.equal(bodyText, '')
})
})
})
// https://fetch.spec.whatwg.org/#body-mixin
suite('Body mixin', function() {
featureDependent(suite, support.blob, 'arrayBuffer', function() {
test('resolves arrayBuffer promise', function() {
return fetch('/hello')
.then(function(response) {
return response.arrayBuffer()
})
.then(function(buf) {
assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
assert.equal(buf.byteLength, 2)
})
})
test('arrayBuffer handles binary data', function() {
return fetch('/binary')
.then(function(response) {
return response.arrayBuffer()
})
.then(function(buf) {
assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
assert.equal(buf.byteLength, 256, 'buf.byteLength is correct')
var view = new Uint8Array(buf)
for (var i = 0; i < 256; i++) {
assert.equal(view[i], i)
}
})
})
test('arrayBuffer handles utf-8 data', function() {
return fetch('/hello/utf8')
.then(function(response) {
return response.arrayBuffer()
})
.then(function(buf) {
assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
assert.equal(buf.byteLength, 5, 'buf.byteLength is correct')
var octets = Array.prototype.slice.call(new Uint8Array(buf))
assert.deepEqual(octets, [104, 101, 108, 108, 111])
})
})
test('arrayBuffer handles utf-16le data', function() {
return fetch('/hello/utf16le')
.then(function(response) {
return response.arrayBuffer()
})
.then(function(buf) {
assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
assert.equal(buf.byteLength, 10, 'buf.byteLength is correct')
var octets = Array.prototype.slice.call(new Uint8Array(buf))
assert.deepEqual(octets, [104, 0, 101, 0, 108, 0, 108, 0, 111, 0])
})
})
test('rejects arrayBuffer promise after body is consumed', function() {
return fetch('/hello')
.then(function(response) {
assert.equal(response.bodyUsed, false)
response.blob()
assert.equal(response.bodyUsed, true)
return response.arrayBuffer()
})
.catch(function(error) {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
})
})
})
featureDependent(suite, support.blob, 'blob', function() {
test('resolves blob promise', function() {
return fetch('/hello')
.then(function(response) {
return response.blob()
})
.then(function(blob) {
assert(blob instanceof Blob, 'blob is a Blob instance')
assert.equal(blob.size, 2)
})
})
test('blob handles binary data', function() {
return fetch('/binary')
.then(function(response) {
return response.blob()
})
.then(function(blob) {
assert(blob instanceof Blob, 'blob is a Blob instance')
assert.equal(blob.size, 256, 'blob.size is correct')
})
})
test('blob handles utf-8 data', function() {
return fetch('/hello/utf8')
.then(function(response) {
return response.blob()
})
.then(readBlobAsBytes)
.then(function(octets) {
assert.equal(octets.length, 5, 'blob.size is correct')
assert.deepEqual(octets, [104, 101, 108, 108, 111])
})
})
test('blob handles utf-16le data', function() {
return fetch('/hello/utf16le')
.then(function(response) {
return response.blob()
})
.then(readBlobAsBytes)
.then(function(octets) {
assert.equal(octets.length, 10, 'blob.size is correct')
assert.deepEqual(octets, [104, 0, 101, 0, 108, 0, 108, 0, 111, 0])
})
})
test('rejects blob promise after body is consumed', function() {
return fetch('/hello')
.then(function(response) {
assert(response.blob, 'Body does not implement blob')
assert.equal(response.bodyUsed, false)
response.text()
assert.equal(response.bodyUsed, true)
return response.blob()
})
.catch(function(error) {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
})
})
})
featureDependent(suite, support.formData, 'formData', function() {
test('post sets content-type header', function() {
return fetch('/request', {
method: 'post',
body: new FormData()
})
.then(function(response) {
return response.json()
})
.then(function(json) {
assert.equal(json.method, 'POST')
assert(/^multipart\/form-data;/.test(json.headers['content-type']))
})
})
featureDependent(test, !nativeChrome && !nativeEdge, 'formData rejects after body was consumed', function() {
return fetch('/json')
.then(function(response) {
assert(response.formData, 'Body does not implement formData')
response.formData()
return response.formData()
})
.catch(function(error) {
if (error instanceof chai.AssertionError) {
throw error
} else {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
}
})
})
featureDependent(
test,
!nativeChrome && !nativeSafari && !nativeEdge,
'parses form encoded response',
function() {
return fetch('/form')
.then(function(response) {
return response.formData()
})
.then(function(form) {
assert(form instanceof FormData, 'Parsed a FormData object')
})
}
)
})
suite('json', function() {
test('parses json response', function() {
return fetch('/json')
.then(function(response) {
return response.json()
})
.then(function(json) {
assert.equal(json.name, 'Hubot')
assert.equal(json.login, 'hubot')
})
})
test('rejects json promise after body is consumed', function() {
return fetch('/json')
.then(function(response) {
assert(response.json, 'Body does not implement json')
assert.equal(response.bodyUsed, false)
response.text()
assert.equal(response.bodyUsed, true)
return response.json()
})
.catch(function(error) {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
})
})
featureDependent(test, !polyfillFirefox, 'handles json parse error', function() {
return fetch('/json-error')
.then(function(response) {
return response.json()
})
.catch(function(error) {
if (!IEorEdge) assert(error instanceof Error, 'JSON exception is an Error instance')
assert(error.message, 'JSON exception has an error message')
})
})
})
suite('text', function() {
test('handles 204 No Content response', function() {
return fetch('/empty')
.then(function(response) {
assert.equal(response.status, 204)
return response.text()
})
.then(function(body) {
assert.equal(body, '')
})
})
test('resolves text promise', function() {
return fetch('/hello')
.then(function(response) {
return response.text()
})
.then(function(text) {
assert.equal(text, 'hi')
})
})
test('rejects text promise after body is consumed', function() {
return fetch('/hello')
.then(function(response) {
assert(response.text, 'Body does not implement text')
assert.equal(response.bodyUsed, false)
response.text()
assert.equal(response.bodyUsed, true)
return response.text()
})
.catch(function(error) {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
})
})
})
})
suite('fetch method', function() {
suite('promise resolution', function() {
test('resolves promise on 500 error', function() {
return fetch('/boom')
.then(function(response) {
assert.equal(response.status, 500)
assert.equal(response.ok, false)
return response.text()
})
.then(function(body) {
assert.equal(body, 'boom')
})
})
test.skip('rejects promise for network error', function() {
return fetch('/error')
.then(function(response) {
assert(false, 'HTTP status ' + response.status + ' was treated as success')
})
.catch(function(error) {
assert(error instanceof TypeError, 'Rejected with Error')
})
})
test('rejects when Request constructor throws', function() {
return fetch('/request', {method: 'GET', body: 'invalid'})
.then(function() {
assert(false, 'Invalid Request init was accepted')
})
.catch(function(error) {
assert(error instanceof TypeError, 'Rejected with Error')
})
})
})
suite('request', function() {
test('sends headers', function() {
return fetch('/request', {
headers: {
Accept: 'application/json',
'X-Test': '42'
}
})
.then(function(response) {
return response.json()
})
.then(function(json) {
assert.equal(json.headers['accept'], 'application/json')
assert.equal(json.headers['x-test'], '42')
})
})
test('with Request as argument', function() {
var request = new Request('/request', {
headers: {
Accept: 'application/json',
'X-Test': '42'
}
})
return fetch(request)
.then(function(response) {
return response.json()
})
.then(function(json) {
assert.equal(json.headers['accept'], 'application/json')
assert.equal(json.headers['x-test'], '42')
})
})
test('reusing same Request multiple times', function() {
var request = new Request('/request', {
headers: {
Accept: 'application/json',
'X-Test': '42'
}
})
var responses = []
return fetch(request)
.then(function(response) {
responses.push(response)
return fetch(request)
})
.then(function(response) {
responses.push(response)
return fetch(request)
})
.then(function(response) {
responses.push(response)
return Promise.all(
responses.map(function(r) {
return r.json()
})
)
})
.then(function(jsons) {
jsons.forEach(function(json) {
assert.equal(json.headers['accept'], 'application/json')
assert.equal(json.headers['x-test'], '42')
})
})
})
featureDependent(suite, support.arrayBuffer, 'ArrayBuffer', function() {
test('ArrayBuffer body', function() {
return fetch('/request', {
method: 'post',
body: arrayBufferFromText('name=Hubot')
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'name=Hubot')
})
})
test('DataView body', function() {
return fetch('/request', {
method: 'post',
body: new DataView(arrayBufferFromText('name=Hubot'))
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'name=Hubot')
})
})
test('TypedArray body', function() {
return fetch('/request', {
method: 'post',
body: new Uint8Array(arrayBufferFromText('name=Hubot'))
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'name=Hubot')
})
})
})
featureDependent(test, !IEorEdge, 'sends URLSearchParams body', function() {
return fetch('/request', {
method: 'post',
body: new URLSearchParams('a=1&b=2')
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'a=1&b=2')
})
})
})
featureDependent(suite, exerciseMode !== 'native' || support.aborting, 'aborting', function() {
test('initially aborted signal', function() {
var controller = new AbortController()
controller.abort()
return fetch('/request', {
signal: controller.signal
}).then(
function() {
assert.ok(false)
},
function(error) {
if (!IEorEdge) assert.instanceOf(error, WHATWGFetch.DOMException)
assert.equal(error.name, 'AbortError')
}
)
})
test('initially aborted signal within Request', function() {
var controller = new AbortController()
controller.abort()
var request = new Request('/request', {signal: controller.signal})
return fetch(request).then(
function() {
assert.ok(false)
},
function(error) {
assert.equal(error.name, 'AbortError')
}
)
})
test('mid-request', function() {
var controller = new AbortController()
setTimeout(function() {
controller.abort()
}, 30)
return fetch('/slow?_=' + new Date().getTime(), {
signal: controller.signal
}).then(
function() {
assert.ok(false)
},
function(error) {
assert.equal(error.name, 'AbortError')
}
)
})
test('mid-request within Request', function() {
var controller = new AbortController()
var request = new Request('/slow?_=' + new Date().getTime(), {signal: controller.signal})
setTimeout(function() {
controller.abort()
}, 30)
return fetch(request).then(
function() {
assert.ok(false)
},
function(error) {
assert.equal(error.name, 'AbortError')
}
)
})
test('abort multiple with same signal', function() {
var controller = new AbortController()
setTimeout(function() {
controller.abort()
}, 30)
return Promise.all([
fetch('/slow?_=' + new Date().getTime(), {
signal: controller.signal
}).then(
function() {
assert.ok(false)
},
function(error) {
assert.equal(error.name, 'AbortError')
}
),
fetch('/slow?_=' + new Date().getTime(), {
signal: controller.signal
}).then(
function() {
assert.ok(false)
},
function(error) {
assert.equal(error.name, 'AbortError')
}
)
])
})
})
suite('response', function() {
test('populates body', function() {
return fetch('/hello')
.then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
return response.text()
})
.then(function(body) {
assert.equal(body, 'hi')
})
})
test('parses headers', function() {
return fetch('/headers?' + new Date().getTime()).then(function(response) {
assert.equal(response.headers.get('Date'), 'Mon, 13 Oct 2014 21:02:27 GMT')
assert.equal(response.headers.get('Content-Type'), 'text/html; charset=utf-8')
})
})
})
// https://fetch.spec.whatwg.org/#methods
suite('HTTP methods', function() {
test('supports HTTP GET', function() {
return fetch('/request', {
method: 'get'
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'GET')
assert.equal(request.data, '')
})
})
test('GET with body throws TypeError', function() {
assert.throw(function() {
new Request('', {
method: 'get',
body: 'invalid'
})
}, TypeError)
})
test('HEAD with body throws TypeError', function() {
assert.throw(function() {
new Request('', {
method: 'head',
body: 'invalid'
})
}, TypeError)
})
test('supports HTTP POST', function() {
return fetch('/request', {
method: 'post',
body: 'name=Hubot'
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'name=Hubot')
})
})
test('supports HTTP PUT', function() {
return fetch('/request', {
method: 'put',
body: 'name=Hubot'
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'PUT')
assert.equal(request.data, 'name=Hubot')
})
})
test('supports HTTP PATCH', function() {
return fetch('/request', {
method: 'PATCH',
body: 'name=Hubot'
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'PATCH')
assert.equal(request.data, 'name=Hubot')
})
})
test('supports HTTP DELETE', function() {
return fetch('/request', {
method: 'delete'
})
.then(function(response) {
return response.json()
})
.then(function(request) {
assert.equal(request.method, 'DELETE')
assert.equal(request.data, '')
})
})
})
// https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
suite('Atomic HTTP redirect handling', function() {
test('handles 301 redirect response', function() {
return fetch('/redirect/301')
.then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
})
.then(function(body) {
assert.equal(body, 'hi')
})
})
test('handles 302 redirect response', function() {
return fetch('/redirect/302')
.then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
})
.then(function(body) {
assert.equal(body, 'hi')
})
})
test('handles 303 redirect response', function() {
return fetch('/redirect/303')
.then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
})
.then(function(body) {
assert.equal(body, 'hi')
})
})
test('handles 307 redirect response', function() {
return fetch('/redirect/307')
.then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
})
.then(function(body) {
assert.equal(body, 'hi')
})
})
featureDependent(test, support.permanentRedirect, 'handles 308 redirect response', function() {
return fetch('/redirect/308')
.then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
})
.then(function(body) {
assert.equal(body, 'hi')
})
})
})
// https://fetch.spec.whatwg.org/#concept-request-credentials-mode
suite('credentials mode', function() {
setup(function() {
return fetch('/cookie?name=foo&value=reset', {credentials: 'same-origin'})
})
featureDependent(suite, exerciseMode === 'native', 'omit', function() {
test('does not accept cookies with omit credentials', function() {
return fetch('/cookie?name=foo&value=bar', {credentials: 'omit'})
.then(function() {
return fetch('/cookie?name=foo', {credentials: 'same-origin'})
})
.then(function(response) {
return response.text()
})
.then(function(data) {
assert.equal(data, 'reset')
})
})
test('does not send cookies with omit credentials', function() {
return fetch('/cookie?name=foo&value=bar')
.then(function() {
return fetch('/cookie?name=foo', {credentials: 'omit'})
})
.then(function(response) {
return response.text()
})
.then(function(data) {
assert.equal(data, '')
})
})
})
suite('same-origin', function() {
test('send cookies with same-origin credentials', function() {
return fetch('/cookie?name=foo&value=bar', {credentials: 'same-origin'})
.then(function() {
return fetch('/cookie?name=foo', {credentials: 'same-origin'})
})
.then(function(response) {
return response.text()
})
.then(function(data) {
assert.equal(data, 'bar')
})
})
})
suite('include', function() {
test('send cookies with include credentials', function() {
return fetch('/cookie?name=foo&value=bar', {credentials: 'include'})
.then(function() {
return fetch('/cookie?name=foo', {credentials: 'include'})
})
.then(function(response) {
return response.text()
})
.then(function(data) {
assert.equal(data, 'bar')
})
})
})
})
})
})
})