Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add in Header WPTs #1685

Merged
merged 3 commits into from Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 50 additions & 16 deletions lib/fetch/webidl.js
Expand Up @@ -250,30 +250,64 @@ webidl.sequenceConverter = function (converter) {
}
}

// https://webidl.spec.whatwg.org/#es-to-record
webidl.recordConverter = function (keyConverter, valueConverter) {
return (V) => {
const record = {}
const type = webidl.util.Type(V)

if (type === 'Undefined' || type === 'Null') {
return record
}

if (type !== 'Object') {
return (O) => {
// 1. If Type(O) is not Object, throw a TypeError.
if (webidl.util.Type(O) !== 'Object') {
webidl.errors.exception({
header: 'Record',
message: `Expected ${V} to be an Object type.`
message: `Value of type ${webidl.util.Type(O)} is not an Object.`
})
}

for (let [key, value] of Object.entries(V)) {
key = keyConverter(key)
value = valueConverter(value)
// 2. Let result be a new empty instance of record<K, V>.
const result = {}

if (!types.isProxy(O)) {
// Object.keys only returns enumerable properties
const keys = Object.keys(O)

for (const key of keys) {
// 1. Let typedKey be key converted to an IDL value of type K.
const typedKey = keyConverter(key)

// 2. Let value be ? Get(O, key).
// 3. Let typedValue be value converted to an IDL value of type V.
const typedValue = valueConverter(O[key])

// 4. Set result[typedKey] to typedValue.
result[typedKey] = typedValue
}

// 5. Return result.
return result
}

// 3. Let keys be ? O.[[OwnPropertyKeys]]().
const keys = Reflect.ownKeys(O)

record[key] = value
// 4. For each key of keys.
for (const key of keys) {
// 1. Let desc be ? O.[[GetOwnProperty]](key).
const desc = Reflect.getOwnPropertyDescriptor(O, key)

// 2. If desc is not undefined and desc.[[Enumerable]] is true:
if (desc?.enumerable) {
// 1. Let typedKey be key converted to an IDL value of type K.
const typedKey = keyConverter(key)

// 2. Let value be ? Get(O, key).
// 3. Let typedValue be value converted to an IDL value of type V.
const typedValue = valueConverter(O[key])

// 4. Set result[typedKey] to typedValue.
result[typedKey] = typedValue
}
}

return record
// 5. Return result.
return result
}
}

Expand Down Expand Up @@ -401,7 +435,7 @@ webidl.converters.ByteString = function (V) {

if (charCode > 255) {
throw new TypeError(
'Cannot convert argument to a ByteString because the character at' +
'Cannot convert argument to a ByteString because the character at ' +
`index ${index} has a value of ${charCode} which is greater than 255.`
)
}
Expand Down
2 changes: 1 addition & 1 deletion test/webidl/converters.js
Expand Up @@ -194,7 +194,7 @@ test('ByteString', (t) => {
const char = String.fromCharCode(256)
webidl.converters.ByteString(`invalid${char}char`)
}, {
message: 'Cannot convert argument to a ByteString because the character at' +
message: 'Cannot convert argument to a ByteString because the character at ' +
'index 7 has a value of 256 which is greater than 255.'
})

Expand Down
14 changes: 14 additions & 0 deletions test/wpt/tests/fetch/api/body/formdata.any.js
@@ -0,0 +1,14 @@
promise_test(async t => {
const res = new Response(new FormData());
const fd = await res.formData();
assert_true(fd instanceof FormData);
}, 'Consume empty response.formData() as FormData');

promise_test(async t => {
const req = new Request('about:blank', {
method: 'POST',
body: new FormData()
});
const fd = await req.formData();
assert_true(fd instanceof FormData);
}, 'Consume empty request.formData() as FormData');
220 changes: 220 additions & 0 deletions test/wpt/tests/fetch/api/headers/headers-basic.any.js
@@ -0,0 +1,220 @@
// META: title=Headers structure
// META: global=window,worker

"use strict";

test(function() {
new Headers();
}, "Create headers from no parameter");

test(function() {
new Headers(undefined);
}, "Create headers from undefined parameter");

test(function() {
new Headers({});
}, "Create headers from empty object");

var parameters = [null, 1];
parameters.forEach(function(parameter) {
test(function() {
assert_throws_js(TypeError, function() { new Headers(parameter) });
}, "Create headers with " + parameter + " should throw");
});

var headerDict = {"name1": "value1",
"name2": "value2",
"name3": "value3",
"name4": null,
"name5": undefined,
"name6": 1,
"Content-Type": "value4"
};

var headerSeq = [];
for (var name in headerDict)
headerSeq.push([name, headerDict[name]]);

test(function() {
var headers = new Headers(headerSeq);
for (name in headerDict) {
assert_equals(headers.get(name), String(headerDict[name]),
"name: " + name + " has value: " + headerDict[name]);
}
assert_equals(headers.get("length"), null, "init should be treated as a sequence, not as a dictionary");
}, "Create headers with sequence");

test(function() {
var headers = new Headers(headerDict);
for (name in headerDict) {
assert_equals(headers.get(name), String(headerDict[name]),
"name: " + name + " has value: " + headerDict[name]);
}
}, "Create headers with record");

test(function() {
var headers = new Headers(headerDict);
var headers2 = new Headers(headers);
for (name in headerDict) {
assert_equals(headers2.get(name), String(headerDict[name]),
"name: " + name + " has value: " + headerDict[name]);
}
}, "Create headers with existing headers");

test(function() {
var headers = new Headers()
headers[Symbol.iterator] = function *() {
yield ["test", "test"]
}
var headers2 = new Headers(headers)
assert_equals(headers2.get("test"), "test")
}, "Create headers with existing headers with custom iterator");

test(function() {
var headers = new Headers();
for (name in headerDict) {
headers.append(name, headerDict[name]);
assert_equals(headers.get(name), String(headerDict[name]),
"name: " + name + " has value: " + headerDict[name]);
}
}, "Check append method");

test(function() {
var headers = new Headers();
for (name in headerDict) {
headers.set(name, headerDict[name]);
assert_equals(headers.get(name), String(headerDict[name]),
"name: " + name + " has value: " + headerDict[name]);
}
}, "Check set method");

test(function() {
var headers = new Headers(headerDict);
for (name in headerDict)
assert_true(headers.has(name),"headers has name " + name);

assert_false(headers.has("nameNotInHeaders"),"headers do not have header: nameNotInHeaders");
}, "Check has method");

test(function() {
var headers = new Headers(headerDict);
for (name in headerDict) {
assert_true(headers.has(name),"headers have a header: " + name);
headers.delete(name)
assert_true(!headers.has(name),"headers do not have anymore a header: " + name);
}
}, "Check delete method");

test(function() {
var headers = new Headers(headerDict);
for (name in headerDict)
assert_equals(headers.get(name), String(headerDict[name]),
"name: " + name + " has value: " + headerDict[name]);

assert_equals(headers.get("nameNotInHeaders"), null, "header: nameNotInHeaders has no value");
}, "Check get method");

var headerEntriesDict = {"name1": "value1",
"Name2": "value2",
"name": "value3",
"content-Type": "value4",
"Content-Typ": "value5",
"Content-Types": "value6"
};
var sortedHeaderDict = {};
var headerValues = [];
var sortedHeaderKeys = Object.keys(headerEntriesDict).map(function(value) {
sortedHeaderDict[value.toLowerCase()] = headerEntriesDict[value];
headerValues.push(headerEntriesDict[value]);
return value.toLowerCase();
}).sort();

var iteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
function checkIteratorProperties(iterator) {
var prototype = Object.getPrototypeOf(iterator);
assert_equals(Object.getPrototypeOf(prototype), iteratorPrototype);

var descriptor = Object.getOwnPropertyDescriptor(prototype, "next");
assert_true(descriptor.configurable, "configurable");
assert_true(descriptor.enumerable, "enumerable");
assert_true(descriptor.writable, "writable");
}

test(function() {
var headers = new Headers(headerEntriesDict);
var actual = headers.keys();
checkIteratorProperties(actual);

sortedHeaderKeys.forEach(function(key) {
const entry = actual.next();
assert_false(entry.done);
assert_equals(entry.value, key);
});
assert_true(actual.next().done);
assert_true(actual.next().done);

for (const key of headers.keys())
assert_true(sortedHeaderKeys.indexOf(key) != -1);
}, "Check keys method");

test(function() {
var headers = new Headers(headerEntriesDict);
var actual = headers.values();
checkIteratorProperties(actual);

sortedHeaderKeys.forEach(function(key) {
const entry = actual.next();
assert_false(entry.done);
assert_equals(entry.value, sortedHeaderDict[key]);
});
assert_true(actual.next().done);
assert_true(actual.next().done);

for (const value of headers.values())
assert_true(headerValues.indexOf(value) != -1);
}, "Check values method");

test(function() {
var headers = new Headers(headerEntriesDict);
var actual = headers.entries();
checkIteratorProperties(actual);

sortedHeaderKeys.forEach(function(key) {
const entry = actual.next();
assert_false(entry.done);
assert_equals(entry.value[0], key);
assert_equals(entry.value[1], sortedHeaderDict[key]);
});
assert_true(actual.next().done);
assert_true(actual.next().done);

for (const entry of headers.entries())
assert_equals(entry[1], sortedHeaderDict[entry[0]]);
}, "Check entries method");

test(function() {
var headers = new Headers(headerEntriesDict);
var actual = headers[Symbol.iterator]();

sortedHeaderKeys.forEach(function(key) {
const entry = actual.next();
assert_false(entry.done);
assert_equals(entry.value[0], key);
assert_equals(entry.value[1], sortedHeaderDict[key]);
});
assert_true(actual.next().done);
assert_true(actual.next().done);
}, "Check Symbol.iterator method");

test(function() {
var headers = new Headers(headerEntriesDict);
var reference = sortedHeaderKeys[Symbol.iterator]();
headers.forEach(function(value, key, container) {
assert_equals(headers, container);
const entry = reference.next();
assert_false(entry.done);
assert_equals(key, entry.value);
assert_equals(value, sortedHeaderDict[entry.value]);
});
assert_true(reference.next().done);
}, "Check forEach method");
54 changes: 54 additions & 0 deletions test/wpt/tests/fetch/api/headers/headers-casing.any.js
@@ -0,0 +1,54 @@
// META: title=Headers case management
// META: global=window,worker

"use strict";

var headerDictCase = {"UPPERCASE": "value1",
"lowercase": "value2",
"mixedCase": "value3",
"Content-TYPE": "value4"
};

function checkHeadersCase(originalName, headersToCheck, expectedDict) {
var lowCaseName = originalName.toLowerCase();
var upCaseName = originalName.toUpperCase();
var expectedValue = expectedDict[originalName];
assert_equals(headersToCheck.get(originalName), expectedValue,
"name: " + originalName + " has value: " + expectedValue);
assert_equals(headersToCheck.get(lowCaseName), expectedValue,
"name: " + lowCaseName + " has value: " + expectedValue);
assert_equals(headersToCheck.get(upCaseName), expectedValue,
"name: " + upCaseName + " has value: " + expectedValue);
}

test(function() {
var headers = new Headers(headerDictCase);
for (const name in headerDictCase)
checkHeadersCase(name, headers, headerDictCase)
}, "Create headers, names use characters with different case");

test(function() {
var headers = new Headers();
for (const name in headerDictCase) {
headers.append(name, headerDictCase[name]);
checkHeadersCase(name, headers, headerDictCase);
}
}, "Check append method, names use characters with different case");

test(function() {
var headers = new Headers();
for (const name in headerDictCase) {
headers.set(name, headerDictCase[name]);
checkHeadersCase(name, headers, headerDictCase);
}
}, "Check set method, names use characters with different case");

test(function() {
var headers = new Headers();
for (const name in headerDictCase)
headers.set(name, headerDictCase[name]);
for (const name in headerDictCase)
headers.delete(name.toLowerCase());
for (const name in headerDictCase)
assert_false(headers.has(name), "header " + name + " should have been deleted");
}, "Check delete method, names use characters with different case");