blob: d8241227e6327538a451b4eb2f5b8123e235c03e [file] [log] [blame]
if (self.importScripts) {
importScripts('../resources/fetch-test-helpers.js');
importScripts('/streams/resources/rs-utils.js');
}
function decode(chunks) {
var decoder = new TextDecoder();
var result = '';
for (var chunk of chunks) {
result += decoder.decode(chunk, {stream: true});
}
result += decoder.decode(new Uint8Array(0));
return result;
}
test(function() {
var response = new Response();
assert_equals(response.type, 'default',
'Default Response.type should be \'default\'');
assert_equals(response.url, '', 'Response.url should be the empty string');
assert_false(response.redirected, 'Response.redirected should be false.');
assert_equals(response.status, 200,
'Default Response.status should be 200');
assert_true(response.ok, 'Default Response.ok must be true');
assert_equals(response.statusText, 'OK',
'Default Response.statusText should be \'OK\'');
assert_equals(size(response.headers), 0,
'Default Response should not have any header.');
if (self.internals) {
var urlList = self.internals.getInternalResponseURLList(response);
assert_equals(urlList.length, 0,
'The URL list of Default Response should be empty.');
}
response.status = 394;
response.statusText = 'Sesame Street';
assert_equals(response.status, 200, 'Response.status should be readonly');
assert_true(response.ok, 'Response.ok must remain unchanged ' +
'when Response.status is attempted ' +
'unsuccessfully to change');
assert_equals(response.statusText, 'OK',
'Response.statusText should be readonly');
response.ok = false;
assert_true(response.ok, 'Response.ok must be readonly');
assert_equals(response.body, null, 'Response with null body');
var cloned = response.clone();
assert_equals(response.body, null, 'Cloning a null body response: src');
assert_equals(cloned.body, null, 'Closing a null body response: dest');
}, 'Response default value test');
test(() => {
// No exception is thrown due to null body status.
var response = new Response(undefined, {status: 204});
assert_equals(response.body, null,
'Response.body should be null when passing undefined.');
assert_equals(response.status, 204,
'Response.status is set even when body is omitted.');
}, 'Construct a Response with null body using undefined.');
test(() => {
// No exception is thrown due to null body status.
var response = new Response(null, {status: 204});
assert_equals(response.body, null,
'Response.body should be null when passing null.');
assert_equals(response.status, 204,
'Response.status is set even when null body is passed.');
}, 'Construct a Response with null body using null.');
test(function() {
var headersInit = new Headers;
headersInit.set('X-Fetch-Test', 'test');
var responses =
[new Response(new Blob(), {status: 303,
statusText: 'tEst',
headers: {'X-Fetch-Test': 'test'}}),
new Response(new Blob(), {status: 303,
statusText: 'tEst',
headers: [['X-Fetch-Test', 'test']]}),
new Response(new Blob(), {status: 303,
statusText: 'tEst',
headers: headersInit})];
responses = responses.concat(
responses.map(function(r) {return r.clone();}));
responses.forEach(function(response) {
assert_equals(response.status, 303, 'Response.status should match');
assert_false(response.ok, 'Response.ok must be false for 303');
assert_equals(response.statusText, 'tEst',
'Response.statusText should match');
assert_true(response.headers instanceof Headers,
'Response.headers should be Headers');
assert_equals(size(response.headers), 1,
'Response.headers size should match');
assert_equals(response.headers.get('X-Fetch-Test'),
'test',
'X-Fetch-Test of Response.headers should match');
});
}, 'Response constructor test');
test(function() {
var response = new Response(new Blob(['dummy'], {type: 'audio/wav'}));
assert_equals(size(response.headers), 1,
'Response.headers should have Content-Type');
assert_equals(response.headers.get('Content-Type'), 'audio/wav',
'Content-Type of Response.headers should be set');
if (self.internals) {
var urlList = self.internals.getInternalResponseURLList(response);
assert_equals(urlList.length, 0,
'The URL list of generated Response should be empty.');
}
response = new Response(new Blob(['dummy'], {type: 'audio/wav'}),
{
headers: {
'Content-Type': 'text/html; charset=UTF-8'
}
});
assert_equals(size(response.headers), 1,
'Response.headers should have Content-Type');
assert_equals(response.headers.get('Content-Type'),
'text/html; charset=UTF-8',
'Content-Type of Response.headers should be overridden');
response = new Response(new Blob(['dummy']));
assert_equals(size(response.headers), 0,
'Response.headers must not have Content-Type ' +
'for Blob with type = empty string (1)');
response = new Response(new Blob(['dummy'], {type: ''}));
assert_equals(size(response.headers), 0,
'Response.headers must not have Content-Type ' +
'for Blob with type = empty string (2)');
}, 'Response content type test');
test(function() {
[0, 1, 100, 101, 199, 600, 700].forEach(function(status) {
assert_throws({name: 'RangeError'},
function() {
new Response(new Blob(), {status: status});
},
'new Response with status = ' + status +
' should throw');
});
[204, 205, 304].forEach(function(status) {
assert_throws({name: 'TypeError'},
function() {
new Response(new Blob(), {status: status});
},
'new Response with null body status = ' + status +
' and body is non-null should throw');
});
[300, 0, 304, 305, 306, 309, 500].forEach(function(status) {
assert_throws({name: 'RangeError'},
function() {
Response.redirect('https://www.example.com/test.html',
status);
},
'Response.redirect() with invalid status = ' + status +
' should throw');
});
assert_throws(
{name: 'TypeError'},
function() {
Response.redirect('https://', 301);
},
'Response.redirect() with invalid URL https:// ' +
' and status 301 should throw');
INVALID_URLS.forEach(function(url) {
assert_throws(
{name: 'TypeError'},
function() {
Response.redirect(url);
},
'Response.redirect() with invalid URL ' + url +
' and default status value should throw');
});
assert_throws(
{name: 'TypeError'},
function() {
Response.redirect('https://', 300);
},
'Response.redirect() with invalid URL https:// ' +
' and invalid status 300 should throw TypeError');
[200, 300, 400, 500, 599].forEach(function(status) {
var response = new Response(new Blob(), {status: status});
assert_equals(response.status, status, 'Response.status should match');
if (200 <= status && status <= 299)
assert_true(response.ok, 'Response.ok must be true for ' + status);
else
assert_false(response.ok, 'Response.ok must be false for ' + status);
});
INVALID_HEADER_NAMES.forEach(function(name) {
assert_throws(
{name: 'TypeError'},
function() {
var obj = {};
obj[name] = 'a';
new Response(new Blob(), {headers: obj});
},
'new Response with headers with an invalid name (' + name +
') should throw');
assert_throws(
{name: 'TypeError'},
function() {
new Response(new Blob(), {headers: [[name, 'a']]});
},
'new Response with headers with an invalid name (' + name +
') should throw');
});
INVALID_HEADER_VALUES.forEach(function(value) {
assert_throws(
{name: 'TypeError'},
function() {
new Response(new Blob(),
{headers: {'X-Fetch-Test': value}});
},
'new Response with headers with an invalid value should throw');
assert_throws(
{name: 'TypeError'},
function() {
new Response(new Blob(),
{headers: [['X-Fetch-Test', value]]});
},
'new Response with headers with an invalid value should throw');
});
VALID_REASON_PHRASE.forEach(function(text) {
// new Response() must succeed with a valid statusText.
var response = new Response(new Blob(), {statusText: text});
assert_equals(response.statusText, text,
'Response.statusText must match: ' + text);
});
INVALID_REASON_PHRASE.forEach(function(text) {
assert_throws(
{name: 'TypeError'},
function() {
new Response(new Blob(), {statusText: text});
},
'new Response with invalid statusText (' + text +
') must throw');
});
}, 'Response throw error test');
promise_test(function(t) {
var res = new Response('hello');
res.body.cancel();
return res.body.getReader().read().then(function(r) {
assert_true(r.done);
});
}, 'Cancel body stream on Response');
promise_test(function(t) {
return new Response().text().then(text => {
assert_equals(text.constructor, String);
assert_equals(text, '');
});
}, 'call text() on null body response');
promise_test(function(t) {
return new Response().arrayBuffer().then(buffer => {
assert_equals(buffer.constructor, ArrayBuffer);
assert_equals(buffer.byteLength, 0);
});
}, 'call arrayBuffer() on null body response');
promise_test(function(t) {
return new Response().blob().then(blob => {
assert_equals(blob.constructor, Blob);
assert_equals(blob.size, 0);
assert_equals(blob.type, '');
});
}, 'call blob() on null body response');
promise_test(function(t) {
return new Response().formData().then(unreached_fulfillment(t), e => {
assert_equals(e.constructor, TypeError);
});
}, 'call formData() on null body response');
promise_test(function(t) {
return new Response().json().then(unreached_fulfillment(t), e => {
assert_equals(e.constructor, SyntaxError);
});
}, 'call json() on null body response');
promise_test(function(t) {
var res = new Response('hello');
var body = res.body;
var clone = res.clone();
assert_false(res.bodyUsed);
assert_false(clone.bodyUsed);
assert_not_equals(res.body, body);
assert_not_equals(res.body, clone.body);
assert_not_equals(body, clone.body);
assert_throws({name: 'TypeError'}, function() { body.getReader(); });
return Promise.all([res.text(), clone.text()]).then(function(r) {
assert_equals(r[0], 'hello');
assert_equals(r[1], 'hello');
});
}, 'Clone on Response (text)');
promise_test(function(t) {
var res = new Response('hello');
var body = res.body;
var clone = res.clone();
assert_false(res.bodyUsed);
assert_false(clone.bodyUsed);
assert_not_equals(res.body, body);
assert_not_equals(res.body, clone.body);
assert_not_equals(body, clone.body);
assert_throws({name: 'TypeError'}, function() { body.getReader(); });
return Promise.all(
[readableStreamToArray(res.body), readableStreamToArray(clone.body)])
.then(r => {
assert_equals(decode(r[0]), 'hello');
assert_equals(decode(r[1]), 'hello');
});
}, 'Clone on Response (manual read)');
test(() => {
var res = new Response('hello');
res.body.cancel();
assert_true(res.bodyUsed);
assert_throws({name: 'TypeError'}, () => res.clone());
}, 'Used => clone');
test(() => {
var res = new Response('hello');
const reader = res.body.getReader();
assert_false(res.bodyUsed);
assert_throws({name: 'TypeError'}, () => res.clone());
reader.releaseLock();
}, 'Locked => clone');
// Tests for MIME types.
promise_test(function(t) {
var res = new Response(new Blob(['']));
return res.blob()
.then(function(blob) {
assert_equals(blob.type, '');
assert_equals(res.headers.get('Content-Type'), null);
});
}, 'MIME type for Blob');
promise_test(function(t) {
var res = new Response(new Blob(['hello'], {type: 'Text/Plain'}));
return res.blob()
.then(function(blob) {
assert_equals(blob.type, 'text/plain');
assert_equals(blob.size, 5);
assert_equals(res.headers.get('Content-Type'), 'text/plain');
});
}, 'MIME type for Blob with non-empty type');
promise_test(function(t) {
var res = new Response(new FormData());
return res.blob()
.then(function(blob) {
assert_equals(blob.type.indexOf('multipart/form-data; boundary='),
0);
assert_equals(res.headers.get('Content-Type')
.indexOf('multipart/form-data; boundary='),
0);
});
}, 'MIME type for FormData');
promise_test(function(t) {
var res = new Response('');
return res.blob()
.then(function(blob) {
assert_equals(blob.type, 'text/plain;charset=utf-8');
assert_equals(res.headers.get('Content-Type'),
'text/plain;charset=UTF-8');
});
}, 'MIME type for USVString');
promise_test(function(t) {
var res = new Response(new Blob([''], {type: 'Text/Plain'}),
{headers: [['Content-Type', 'Text/Html']]});
res = res.clone();
return res.blob()
.then(function(blob) {
assert_equals(blob.type, 'text/html');
assert_equals(res.headers.get('Content-Type'), 'Text/Html');
});
}, 'Extract a MIME type with clone');
promise_test(function(t) {
var res = new Response(new Blob([''], {type: 'Text/Plain'}));
res.headers.set('Content-Type', 'Text/Html');
return res.blob()
.then(function(blob) {
assert_equals(blob.type, 'text/plain');
assert_equals(res.headers.get('Content-Type'), 'Text/Html');
});
},
'MIME type unchanged if headers are modified after Response() constructor');
// The following three tests follow different code paths in Body::readAsync().
promise_test(function(t) {
var res = new Response(new Blob([''], {type: 'Text/Plain'}),
{headers: [['Content-Type', 'Text/Html']]});
return res.blob()
.then(function(blob) {
assert_equals(blob.type, 'text/html');
assert_equals(res.headers.get('Content-Type'), 'Text/Html');
});
}, 'Extract a MIME type (1)');
promise_test(function() {
var response = Response.error();
return response.text().then(function(text) {
assert_equals(response.type, 'error');
assert_equals(response.url, '', 'url must be the empty string');
assert_equals(response.status, 0, 'status is always 0');
assert_false(response.ok);
assert_equals(response.statusText, '',
'status message is always the empty byte sequence');
assert_equals(size(response.headers), 0,
'header list is always empty.');
assert_equals(text, '',
'body is always null');
});
}, 'Response.error()');
promise_test(function() {
var response = Response.redirect('https://www.example.com/test.html');
return response.text().then(function(text) {
assert_equals(response.status, 302,
'default value of status is always 302');
assert_equals(response.headers.get('location'),
'https://www.example.com/test.html',
'Location header should be correct absoulte URL');
assert_throws({name: 'TypeError'},
function() {
response.headers.append('Accept-Language', 'test');
},
'response.headers must throw since guard is immutable');
});
}, 'Response.redirect() with default status value');
promise_test(function() {
var response = Response.redirect('https://www.example.com/test.html',
301);
return response.text().then(function(text) {
assert_equals(response.status, 301,
'value of status is 301');
assert_equals(response.headers.get('location'),
'https://www.example.com/test.html',
'Location header should be correct absoulte URL');
assert_equals(size(response.headers), 1,
'Response.redirect().headers must contain ' +
'a Location header only');
});
}, 'Response.redirect() with 301');
test(function() {
['http://ex\x0aample.com',
'http://ex\x0dample.com'].forEach(function(url) {
assert_equals(Response.redirect(url).headers.get('Location'),
'http://example.com/',
'Location header value must not contain CR or LF');
});
}, 'Response.redirect() with URLs with CR or LF');
test(() => {
var controller;
var stream = new ReadableStream({start: c => controller = c});
var response = new Response(stream);
assert_equals(response.body, stream);
}, 'Response constructed with a stream');
promise_test(() => {
var controller;
var stream = new ReadableStream({start: c => controller = c});
controller.enqueue(new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]));
controller.enqueue(new Uint8Array([0x77, 0x6f, 0x72, 0x6c, 0x64]));
controller.close();
assert_false(stream.locked);
var response = new Response(stream);
var p = response.text().then(t => {
assert_equals(t, 'helloworld');
});
assert_true(stream.locked);
return p;
}, 'Response constructed with a stream having bytes');
promise_test(() => {
var response = new Response('helloworld');
return readableStreamToArray(response.body).then(chunks => {
const decoder = new TextDecoder('utf-8');
let r = '';
for (const chunk of chunks) {
r += decoder.decode(chunk, {stream: true});
}
r += decoder.decode();
assert_equals(r, 'helloworld');
});
}, 'Response constructed with a String / Read from body stream');
promise_test(() => {
var controller;
var stream = new ReadableStream({start: c => controller = c});
controller.enqueue(new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]));
controller.enqueue(new Uint8Array([0x77, 0x6f, 0x72, 0x6c, 0x64]));
controller.close();
var response = new Response(stream);
return readableStreamToArray(response.body).then(chunks => {
var decoder = new TextDecoder('utf-8');
var r = '';
for (var chunk of chunks) {
r += decoder.decode(chunk, {stream: true});
}
r += decoder.decode();
assert_equals(r, 'helloworld');
});
}, 'Response constructed with a stream / Read from body stream');
promise_test(t => {
var controller;
var stream = new ReadableStream({start: c => controller = c});
setTimeout(() => {
controller.enqueue(new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]));
controller.enqueue(new Uint8Array([0x77, 0x6f, 0x72, 0x6c, 0x64]));
controller.error();
}, 1);
var response = new Response(stream);
return promise_rejects(t, TypeError(), response.text());
}, 'Response constructed with an errored stream');
promise_test(t => {
var controller;
var stream = new ReadableStream({start: c => controller = c});
stream.getReader();
var response = new Response(stream);
return promise_rejects(t, TypeError(), response.text());
}, 'Response constructed with a locked stream');
promise_test(t => {
var controller;
var stream = new ReadableStream({start: c => controller = c});
setTimeout(() => controller.enqueue(), 1);
var response = new Response(stream);
return promise_rejects(t, TypeError(), response.text());
}, 'Response constructed stream with an undefined chunk');
promise_test(t => {
var controller;
var stream = new ReadableStream({start: c => controller = c});
setTimeout(() => controller.enqueue(null), 1);
var response = new Response(stream);
return promise_rejects(t, TypeError(), response.text());
}, 'Response constructed stream with a null chunk');
promise_test(t => {
var controller;
var stream = new ReadableStream({start: c => controller = c});
setTimeout(() => controller.enqueue('hello'), 1);
var response = new Response(stream);
return promise_rejects(t, TypeError(), response.text());
}, 'Response constructed stream with a string chunk');
done();