Skip to content

Commit a04cf3d

Browse files
authored
feat: add in Header WPTs (nodejs#1685)
1 parent ab99638 commit a04cf3d

File tree

10 files changed

+934
-17
lines changed

10 files changed

+934
-17
lines changed

lib/fetch/webidl.js

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -250,30 +250,64 @@ webidl.sequenceConverter = function (converter) {
250250
}
251251
}
252252

253+
// https://webidl.spec.whatwg.org/#es-to-record
253254
webidl.recordConverter = function (keyConverter, valueConverter) {
254-
return (V) => {
255-
const record = {}
256-
const type = webidl.util.Type(V)
257-
258-
if (type === 'Undefined' || type === 'Null') {
259-
return record
260-
}
261-
262-
if (type !== 'Object') {
255+
return (O) => {
256+
// 1. If Type(O) is not Object, throw a TypeError.
257+
if (webidl.util.Type(O) !== 'Object') {
263258
webidl.errors.exception({
264259
header: 'Record',
265-
message: `Expected ${V} to be an Object type.`
260+
message: `Value of type ${webidl.util.Type(O)} is not an Object.`
266261
})
267262
}
268263

269-
for (let [key, value] of Object.entries(V)) {
270-
key = keyConverter(key)
271-
value = valueConverter(value)
264+
// 2. Let result be a new empty instance of record<K, V>.
265+
const result = {}
266+
267+
if (!types.isProxy(O)) {
268+
// Object.keys only returns enumerable properties
269+
const keys = Object.keys(O)
270+
271+
for (const key of keys) {
272+
// 1. Let typedKey be key converted to an IDL value of type K.
273+
const typedKey = keyConverter(key)
274+
275+
// 2. Let value be ? Get(O, key).
276+
// 3. Let typedValue be value converted to an IDL value of type V.
277+
const typedValue = valueConverter(O[key])
278+
279+
// 4. Set result[typedKey] to typedValue.
280+
result[typedKey] = typedValue
281+
}
282+
283+
// 5. Return result.
284+
return result
285+
}
286+
287+
// 3. Let keys be ? O.[[OwnPropertyKeys]]().
288+
const keys = Reflect.ownKeys(O)
272289

273-
record[key] = value
290+
// 4. For each key of keys.
291+
for (const key of keys) {
292+
// 1. Let desc be ? O.[[GetOwnProperty]](key).
293+
const desc = Reflect.getOwnPropertyDescriptor(O, key)
294+
295+
// 2. If desc is not undefined and desc.[[Enumerable]] is true:
296+
if (desc?.enumerable) {
297+
// 1. Let typedKey be key converted to an IDL value of type K.
298+
const typedKey = keyConverter(key)
299+
300+
// 2. Let value be ? Get(O, key).
301+
// 3. Let typedValue be value converted to an IDL value of type V.
302+
const typedValue = valueConverter(O[key])
303+
304+
// 4. Set result[typedKey] to typedValue.
305+
result[typedKey] = typedValue
306+
}
274307
}
275308

276-
return record
309+
// 5. Return result.
310+
return result
277311
}
278312
}
279313

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

402436
if (charCode > 255) {
403437
throw new TypeError(
404-
'Cannot convert argument to a ByteString because the character at' +
438+
'Cannot convert argument to a ByteString because the character at ' +
405439
`index ${index} has a value of ${charCode} which is greater than 255.`
406440
)
407441
}

test/webidl/converters.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ test('ByteString', (t) => {
194194
const char = String.fromCharCode(256)
195195
webidl.converters.ByteString(`invalid${char}char`)
196196
}, {
197-
message: 'Cannot convert argument to a ByteString because the character at' +
197+
message: 'Cannot convert argument to a ByteString because the character at ' +
198198
'index 7 has a value of 256 which is greater than 255.'
199199
})
200200

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
promise_test(async t => {
2+
const res = new Response(new FormData());
3+
const fd = await res.formData();
4+
assert_true(fd instanceof FormData);
5+
}, 'Consume empty response.formData() as FormData');
6+
7+
promise_test(async t => {
8+
const req = new Request('about:blank', {
9+
method: 'POST',
10+
body: new FormData()
11+
});
12+
const fd = await req.formData();
13+
assert_true(fd instanceof FormData);
14+
}, 'Consume empty request.formData() as FormData');
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// META: title=Headers structure
2+
// META: global=window,worker
3+
4+
"use strict";
5+
6+
test(function() {
7+
new Headers();
8+
}, "Create headers from no parameter");
9+
10+
test(function() {
11+
new Headers(undefined);
12+
}, "Create headers from undefined parameter");
13+
14+
test(function() {
15+
new Headers({});
16+
}, "Create headers from empty object");
17+
18+
var parameters = [null, 1];
19+
parameters.forEach(function(parameter) {
20+
test(function() {
21+
assert_throws_js(TypeError, function() { new Headers(parameter) });
22+
}, "Create headers with " + parameter + " should throw");
23+
});
24+
25+
var headerDict = {"name1": "value1",
26+
"name2": "value2",
27+
"name3": "value3",
28+
"name4": null,
29+
"name5": undefined,
30+
"name6": 1,
31+
"Content-Type": "value4"
32+
};
33+
34+
var headerSeq = [];
35+
for (var name in headerDict)
36+
headerSeq.push([name, headerDict[name]]);
37+
38+
test(function() {
39+
var headers = new Headers(headerSeq);
40+
for (name in headerDict) {
41+
assert_equals(headers.get(name), String(headerDict[name]),
42+
"name: " + name + " has value: " + headerDict[name]);
43+
}
44+
assert_equals(headers.get("length"), null, "init should be treated as a sequence, not as a dictionary");
45+
}, "Create headers with sequence");
46+
47+
test(function() {
48+
var headers = new Headers(headerDict);
49+
for (name in headerDict) {
50+
assert_equals(headers.get(name), String(headerDict[name]),
51+
"name: " + name + " has value: " + headerDict[name]);
52+
}
53+
}, "Create headers with record");
54+
55+
test(function() {
56+
var headers = new Headers(headerDict);
57+
var headers2 = new Headers(headers);
58+
for (name in headerDict) {
59+
assert_equals(headers2.get(name), String(headerDict[name]),
60+
"name: " + name + " has value: " + headerDict[name]);
61+
}
62+
}, "Create headers with existing headers");
63+
64+
test(function() {
65+
var headers = new Headers()
66+
headers[Symbol.iterator] = function *() {
67+
yield ["test", "test"]
68+
}
69+
var headers2 = new Headers(headers)
70+
assert_equals(headers2.get("test"), "test")
71+
}, "Create headers with existing headers with custom iterator");
72+
73+
test(function() {
74+
var headers = new Headers();
75+
for (name in headerDict) {
76+
headers.append(name, headerDict[name]);
77+
assert_equals(headers.get(name), String(headerDict[name]),
78+
"name: " + name + " has value: " + headerDict[name]);
79+
}
80+
}, "Check append method");
81+
82+
test(function() {
83+
var headers = new Headers();
84+
for (name in headerDict) {
85+
headers.set(name, headerDict[name]);
86+
assert_equals(headers.get(name), String(headerDict[name]),
87+
"name: " + name + " has value: " + headerDict[name]);
88+
}
89+
}, "Check set method");
90+
91+
test(function() {
92+
var headers = new Headers(headerDict);
93+
for (name in headerDict)
94+
assert_true(headers.has(name),"headers has name " + name);
95+
96+
assert_false(headers.has("nameNotInHeaders"),"headers do not have header: nameNotInHeaders");
97+
}, "Check has method");
98+
99+
test(function() {
100+
var headers = new Headers(headerDict);
101+
for (name in headerDict) {
102+
assert_true(headers.has(name),"headers have a header: " + name);
103+
headers.delete(name)
104+
assert_true(!headers.has(name),"headers do not have anymore a header: " + name);
105+
}
106+
}, "Check delete method");
107+
108+
test(function() {
109+
var headers = new Headers(headerDict);
110+
for (name in headerDict)
111+
assert_equals(headers.get(name), String(headerDict[name]),
112+
"name: " + name + " has value: " + headerDict[name]);
113+
114+
assert_equals(headers.get("nameNotInHeaders"), null, "header: nameNotInHeaders has no value");
115+
}, "Check get method");
116+
117+
var headerEntriesDict = {"name1": "value1",
118+
"Name2": "value2",
119+
"name": "value3",
120+
"content-Type": "value4",
121+
"Content-Typ": "value5",
122+
"Content-Types": "value6"
123+
};
124+
var sortedHeaderDict = {};
125+
var headerValues = [];
126+
var sortedHeaderKeys = Object.keys(headerEntriesDict).map(function(value) {
127+
sortedHeaderDict[value.toLowerCase()] = headerEntriesDict[value];
128+
headerValues.push(headerEntriesDict[value]);
129+
return value.toLowerCase();
130+
}).sort();
131+
132+
var iteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
133+
function checkIteratorProperties(iterator) {
134+
var prototype = Object.getPrototypeOf(iterator);
135+
assert_equals(Object.getPrototypeOf(prototype), iteratorPrototype);
136+
137+
var descriptor = Object.getOwnPropertyDescriptor(prototype, "next");
138+
assert_true(descriptor.configurable, "configurable");
139+
assert_true(descriptor.enumerable, "enumerable");
140+
assert_true(descriptor.writable, "writable");
141+
}
142+
143+
test(function() {
144+
var headers = new Headers(headerEntriesDict);
145+
var actual = headers.keys();
146+
checkIteratorProperties(actual);
147+
148+
sortedHeaderKeys.forEach(function(key) {
149+
const entry = actual.next();
150+
assert_false(entry.done);
151+
assert_equals(entry.value, key);
152+
});
153+
assert_true(actual.next().done);
154+
assert_true(actual.next().done);
155+
156+
for (const key of headers.keys())
157+
assert_true(sortedHeaderKeys.indexOf(key) != -1);
158+
}, "Check keys method");
159+
160+
test(function() {
161+
var headers = new Headers(headerEntriesDict);
162+
var actual = headers.values();
163+
checkIteratorProperties(actual);
164+
165+
sortedHeaderKeys.forEach(function(key) {
166+
const entry = actual.next();
167+
assert_false(entry.done);
168+
assert_equals(entry.value, sortedHeaderDict[key]);
169+
});
170+
assert_true(actual.next().done);
171+
assert_true(actual.next().done);
172+
173+
for (const value of headers.values())
174+
assert_true(headerValues.indexOf(value) != -1);
175+
}, "Check values method");
176+
177+
test(function() {
178+
var headers = new Headers(headerEntriesDict);
179+
var actual = headers.entries();
180+
checkIteratorProperties(actual);
181+
182+
sortedHeaderKeys.forEach(function(key) {
183+
const entry = actual.next();
184+
assert_false(entry.done);
185+
assert_equals(entry.value[0], key);
186+
assert_equals(entry.value[1], sortedHeaderDict[key]);
187+
});
188+
assert_true(actual.next().done);
189+
assert_true(actual.next().done);
190+
191+
for (const entry of headers.entries())
192+
assert_equals(entry[1], sortedHeaderDict[entry[0]]);
193+
}, "Check entries method");
194+
195+
test(function() {
196+
var headers = new Headers(headerEntriesDict);
197+
var actual = headers[Symbol.iterator]();
198+
199+
sortedHeaderKeys.forEach(function(key) {
200+
const entry = actual.next();
201+
assert_false(entry.done);
202+
assert_equals(entry.value[0], key);
203+
assert_equals(entry.value[1], sortedHeaderDict[key]);
204+
});
205+
assert_true(actual.next().done);
206+
assert_true(actual.next().done);
207+
}, "Check Symbol.iterator method");
208+
209+
test(function() {
210+
var headers = new Headers(headerEntriesDict);
211+
var reference = sortedHeaderKeys[Symbol.iterator]();
212+
headers.forEach(function(value, key, container) {
213+
assert_equals(headers, container);
214+
const entry = reference.next();
215+
assert_false(entry.done);
216+
assert_equals(key, entry.value);
217+
assert_equals(value, sortedHeaderDict[entry.value]);
218+
});
219+
assert_true(reference.next().done);
220+
}, "Check forEach method");
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// META: title=Headers case management
2+
// META: global=window,worker
3+
4+
"use strict";
5+
6+
var headerDictCase = {"UPPERCASE": "value1",
7+
"lowercase": "value2",
8+
"mixedCase": "value3",
9+
"Content-TYPE": "value4"
10+
};
11+
12+
function checkHeadersCase(originalName, headersToCheck, expectedDict) {
13+
var lowCaseName = originalName.toLowerCase();
14+
var upCaseName = originalName.toUpperCase();
15+
var expectedValue = expectedDict[originalName];
16+
assert_equals(headersToCheck.get(originalName), expectedValue,
17+
"name: " + originalName + " has value: " + expectedValue);
18+
assert_equals(headersToCheck.get(lowCaseName), expectedValue,
19+
"name: " + lowCaseName + " has value: " + expectedValue);
20+
assert_equals(headersToCheck.get(upCaseName), expectedValue,
21+
"name: " + upCaseName + " has value: " + expectedValue);
22+
}
23+
24+
test(function() {
25+
var headers = new Headers(headerDictCase);
26+
for (const name in headerDictCase)
27+
checkHeadersCase(name, headers, headerDictCase)
28+
}, "Create headers, names use characters with different case");
29+
30+
test(function() {
31+
var headers = new Headers();
32+
for (const name in headerDictCase) {
33+
headers.append(name, headerDictCase[name]);
34+
checkHeadersCase(name, headers, headerDictCase);
35+
}
36+
}, "Check append method, names use characters with different case");
37+
38+
test(function() {
39+
var headers = new Headers();
40+
for (const name in headerDictCase) {
41+
headers.set(name, headerDictCase[name]);
42+
checkHeadersCase(name, headers, headerDictCase);
43+
}
44+
}, "Check set method, names use characters with different case");
45+
46+
test(function() {
47+
var headers = new Headers();
48+
for (const name in headerDictCase)
49+
headers.set(name, headerDictCase[name]);
50+
for (const name in headerDictCase)
51+
headers.delete(name.toLowerCase());
52+
for (const name in headerDictCase)
53+
assert_false(headers.has(name), "header " + name + " should have been deleted");
54+
}, "Check delete method, names use characters with different case");

0 commit comments

Comments
 (0)