Skip to content

Commit 3bda21f

Browse files
authored
wpt: add scheme-blob.sub.any.js (nodejs#1707)
1 parent 2787b64 commit 3bda21f

File tree

4 files changed

+199
-7
lines changed

4 files changed

+199
-7
lines changed

test/wpt/runner/runner/runner.mjs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { readdirSync, readFileSync, statSync } from 'node:fs'
33
import { basename, isAbsolute, join, resolve } from 'node:path'
44
import { fileURLToPath } from 'node:url'
55
import { Worker } from 'node:worker_threads'
6-
import { parseMeta } from './util.mjs'
6+
import { parseMeta, handlePipes } from './util.mjs'
77

88
const basePath = fileURLToPath(join(import.meta.url, '../../..'))
99
const testPath = join(basePath, 'tests')
@@ -82,14 +82,16 @@ export class WPTRunner extends EventEmitter {
8282

8383
for (const test of this.#files) {
8484
const code = readFileSync(test, 'utf-8')
85+
const meta = this.resolveMeta(code, test)
86+
8587
const worker = new Worker(workerPath, {
8688
workerData: {
8789
// Code to load before the test harness and tests.
8890
initScripts: this.#initScripts,
8991
// The test file.
9092
test: code,
9193
// Parsed META tag information
92-
meta: this.resolveMeta(code, test),
94+
meta,
9395
url: this.#url,
9496
path: test
9597
}
@@ -159,19 +161,29 @@ export class WPTRunner extends EventEmitter {
159161
*/
160162
resolveMeta (code, path) {
161163
const meta = parseMeta(code)
162-
const scripts = meta.scripts.map((script) => {
163-
if (script === '/resources/WebIDLParser.js') {
164+
const scripts = meta.scripts.map((filePath) => {
165+
let content = ''
166+
167+
if (filePath === '/resources/WebIDLParser.js') {
164168
// See https://github.com/web-platform-tests/wpt/pull/731
165169
return readFileSync(join(testPath, '/resources/webidl2/lib/webidl2.js'), 'utf-8')
166-
} else if (isAbsolute(script)) {
167-
return readFileSync(join(testPath, script), 'utf-8')
170+
} else if (isAbsolute(filePath)) {
171+
content = readFileSync(join(testPath, filePath), 'utf-8')
172+
} else {
173+
content = readFileSync(resolve(path, '..', filePath), 'utf-8')
174+
}
175+
176+
// If the file has any built-in pipes.
177+
if (filePath.includes('.sub.')) {
178+
content = handlePipes(content, this.#url)
168179
}
169180

170-
return readFileSync(resolve(path, '..', script), 'utf-8')
181+
return content
171182
})
172183

173184
return {
174185
...meta,
186+
resourcePaths: meta.scripts,
175187
scripts
176188
}
177189
}

test/wpt/runner/runner/util.mjs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,57 @@ export function parseMeta (fileContents) {
6363

6464
return meta
6565
}
66+
67+
/**
68+
* @param {string} sub
69+
*/
70+
function parseSubBlock (sub) {
71+
const subName = sub.includes('[') ? sub.slice(0, sub.indexOf('[')) : sub
72+
const options = sub.matchAll(/\[(.*?)\]/gm)
73+
74+
return {
75+
sub: subName,
76+
options: [...options].map(match => match[1])
77+
}
78+
}
79+
80+
/**
81+
* @see https://web-platform-tests.org/writing-tests/server-pipes.html?highlight=sub#built-in-pipes
82+
* @param {string} code
83+
* @param {string} url
84+
*/
85+
export function handlePipes (code, url) {
86+
const server = new URL(url)
87+
88+
// "Substitutions are marked in a file using a block delimited by
89+
// {{ and }}. Inside the block the following variables are available:"
90+
return code.replace(/{{(.*?)}}/gm, (_, match) => {
91+
const { sub } = parseSubBlock(match)
92+
93+
switch (sub) {
94+
// "The host name of the server excluding any subdomain part."
95+
// eslint-disable-next-line no-fallthrough
96+
case 'host':
97+
// "The domain name of a particular subdomain e.g.
98+
// {{domains[www]}} for the www subdomain."
99+
// eslint-disable-next-line no-fallthrough
100+
case 'domains':
101+
// "The domain name of a particular subdomain for a particular host.
102+
// The first key may be empty (designating the “default” host) or
103+
// the value alt; i.e., {{hosts[alt][]}} (designating the alternate
104+
// host)."
105+
// eslint-disable-next-line no-fallthrough
106+
case 'hosts': {
107+
return 'localhost'
108+
}
109+
// "The port number of servers, by protocol e.g. {{ports[http][0]}}
110+
// for the first (and, depending on setup, possibly only) http server"
111+
case 'ports': {
112+
return server.port
113+
}
114+
default: {
115+
throw new TypeError(`Unknown substitute "${sub}".`)
116+
}
117+
}
118+
})
119+
}

test/wpt/status/fetch.status.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,11 @@
4848
"throwing Object.prototype.highWaterMark accessor should not affect stream creation by 'fetch'",
4949
"Object.prototype.highWaterMark accessor returning invalid value should not affect stream creation by 'fetch'"
5050
]
51+
},
52+
"scheme-blob.sub.any.js": {
53+
"fail": [
54+
"Set content type to the empty string for slice with invalid content type",
55+
"Set content type to the empty string for slice with no content type "
56+
]
5157
}
5258
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// META: script=../resources/utils.js
2+
3+
function checkFetchResponse(url, data, mime, size, desc) {
4+
promise_test(function(test) {
5+
size = size.toString();
6+
return fetch(url).then(function(resp) {
7+
assert_equals(resp.status, 200, "HTTP status is 200");
8+
assert_equals(resp.type, "basic", "response type is basic");
9+
assert_equals(resp.headers.get("Content-Type"), mime, "Content-Type is " + resp.headers.get("Content-Type"));
10+
assert_equals(resp.headers.get("Content-Length"), size, "Content-Length is " + resp.headers.get("Content-Length"));
11+
return resp.text();
12+
}).then(function(bodyAsText) {
13+
assert_equals(bodyAsText, data, "Response's body is " + data);
14+
});
15+
}, desc);
16+
}
17+
18+
var blob = new Blob(["Blob's data"], { "type" : "text/plain" });
19+
checkFetchResponse(URL.createObjectURL(blob), "Blob's data", "text/plain", blob.size,
20+
"Fetching [GET] URL.createObjectURL(blob) is OK");
21+
22+
function checkKoUrl(url, method, desc) {
23+
promise_test(function(test) {
24+
var promise = fetch(url, {"method": method});
25+
return promise_rejects_js(test, TypeError, promise);
26+
}, desc);
27+
}
28+
29+
var blob2 = new Blob(["Blob's data"], { "type" : "text/plain" });
30+
checkKoUrl("blob:http://{{domains[www]}}:{{ports[http][0]}}/", "GET",
31+
"Fetching [GET] blob:http://{{domains[www]}}:{{ports[http][0]}}/ is KO");
32+
33+
var invalidRequestMethods = [
34+
"POST",
35+
"OPTIONS",
36+
"HEAD",
37+
"PUT",
38+
"DELETE",
39+
"INVALID",
40+
];
41+
invalidRequestMethods.forEach(function(method) {
42+
checkKoUrl(URL.createObjectURL(blob2), method, "Fetching [" + method + "] URL.createObjectURL(blob) is KO");
43+
});
44+
45+
checkKoUrl("blob:not-backed-by-a-blob/", "GET",
46+
"Fetching [GET] blob:not-backed-by-a-blob/ is KO");
47+
48+
let empty_blob = new Blob([]);
49+
checkFetchResponse(URL.createObjectURL(empty_blob), "", "", 0,
50+
"Fetching URL.createObjectURL(empty_blob) is OK");
51+
52+
let empty_type_blob = new Blob([], {type: ""});
53+
checkFetchResponse(URL.createObjectURL(empty_type_blob), "", "", 0,
54+
"Fetching URL.createObjectURL(empty_type_blob) is OK");
55+
56+
let empty_data_blob = new Blob([], {type: "text/plain"});
57+
checkFetchResponse(URL.createObjectURL(empty_data_blob), "", "text/plain", 0,
58+
"Fetching URL.createObjectURL(empty_data_blob) is OK");
59+
60+
promise_test(function(test) {
61+
return fetch("/images/blue.png").then(function(resp) {
62+
return resp.arrayBuffer();
63+
}).then(function(image_buffer) {
64+
let blob = new Blob([image_buffer]);
65+
return fetch(URL.createObjectURL(blob)).then(function(resp) {
66+
assert_equals(resp.status, 200, "HTTP status is 200");
67+
assert_equals(resp.type, "basic", "response type is basic");
68+
assert_equals(resp.headers.get("Content-Type"), "", "Content-Type is " + resp.headers.get("Content-Type"));
69+
})
70+
});
71+
}, "Blob content is not sniffed for a content type [image/png]");
72+
73+
let simple_xml_string = '<?xml version="1.0" encoding="UTF-8"?><x></x>';
74+
let xml_blob_no_type = new Blob([simple_xml_string]);
75+
checkFetchResponse(URL.createObjectURL(xml_blob_no_type), simple_xml_string, "", 45,
76+
"Blob content is not sniffed for a content type [text/xml]");
77+
78+
promise_test(function(test) {
79+
let blob = new Blob([], {"type": "text/plain"});
80+
let slice = blob.slice(8, 25, "\0");
81+
return fetch(URL.createObjectURL(slice)).then(function (resp) {
82+
assert_equals(resp.status, 200, "HTTP status is 200");
83+
assert_equals(resp.type, "basic", "response type is basic");
84+
assert_equals(resp.headers.get("Content-Type"), "");
85+
assert_equals(resp.headers.get("Content-Length"), "17");
86+
return resp.text();
87+
}).then(function(bodyAsText) {
88+
assert_equals(bodyAsText, "type with invalid");
89+
});
90+
}, "Set content type to the empty string for slice with invalid content type");
91+
92+
promise_test(function(test) {
93+
let blob = new Blob([], {"type": "text/plain"});
94+
let slice = blob.slice(8, 20);
95+
return fetch(URL.createObjectURL(slice)).then(function (resp) {
96+
assert_equals(resp.status, 200, "HTTP status is 200");
97+
assert_equals(resp.type, "basic", "response type is basic");
98+
assert_equals(resp.headers.get("Content-Type"), "");
99+
assert_equals(resp.headers.get("Content-Length"), "12");
100+
return resp.text();
101+
}).then(function(bodyAsText) {
102+
assert_equals(bodyAsText, "type that is");
103+
});
104+
}, "Set content type to the empty string for slice with no content type ");
105+
106+
promise_test(function(test) {
107+
let blob = new Blob([simple_xml_string]);
108+
let slice = blob.slice(0, 38);
109+
return fetch(URL.createObjectURL(slice)).then(function (resp) {
110+
assert_equals(resp.status, 200, "HTTP status is 200");
111+
assert_equals(resp.type, "basic", "response type is basic");
112+
assert_equals(resp.headers.get("Content-Type"), "");
113+
assert_equals(resp.headers.get("Content-Length"), "38");
114+
return resp.text();
115+
}).then(function(bodyAsText) {
116+
assert_equals(bodyAsText, '<?xml version="1.0" encoding="UTF-8"?>');
117+
});
118+
}, "Blob.slice should not sniff the content for a content type");
119+
120+
done();

0 commit comments

Comments
 (0)