Auto merge of #27928 - servo-wpt-sync:wpt_update_09-12-2020, r=servo-wpt-sync

Sync WPT with upstream (09-12-2020)

Automated downstream sync of changes from upstream as of 09-12-2020.
[no-wpt-sync]
r? @servo-wpt-sync
This commit is contained in:
bors-servo 2020-12-09 06:48:46 -05:00 committed by GitHub
commit 58231a6260
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
96 changed files with 2074 additions and 1228 deletions

View file

@ -4,7 +4,7 @@
expected: TIMEOUT
[Opening a blob URL in a new window immediately before revoking it works.]
expected: TIMEOUT
expected: FAIL
[Fetching a blob URL immediately before revoking it works in an iframe.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[hit-test-floats-002.html]
[Hit test float]
expected: FAIL

View file

@ -1,4 +0,0 @@
[hit-test-floats-003.html]
[Miss float below something else]
expected: FAIL

View file

@ -1,4 +0,0 @@
[hit-test-floats-005.html]
[Miss clipped float]
expected: FAIL

View file

@ -0,0 +1,2 @@
[translation-animation-subpixel-offset.html]
expected: TIMEOUT

View file

@ -2,3 +2,6 @@
[Hit test intersecting scaled box]
expected: FAIL
[Hit test within unscaled box]
expected: FAIL

View file

@ -1,4 +0,0 @@
[elementFromPoint-001.html]
[CSSOM View - 5 - extensions to the Document interface]
expected: FAIL

View file

@ -0,0 +1,4 @@
[elementsFromPoint-invalid-cases.html]
[The root element is the last element returned for otherwise empty queries within the viewport]
expected: FAIL

View file

@ -1,2 +0,0 @@
[matchMedia-display-none-iframe.html]
expected: ERROR

View file

@ -309,12 +309,6 @@
[Response: combined response Content-Type: text/html;" \\" text/plain ";charset=GBK]
expected: NOTRUN
[<iframe>: combined response Content-Type: text/html */*]
expected: FAIL
[<iframe>: separate response Content-Type: text/html;" text/plain]
expected: FAIL
[<iframe>: separate response Content-Type: text/html */*;charset=gbk]
expected: FAIL
@ -324,3 +318,15 @@
[<iframe>: combined response Content-Type: text/html;x=" text/plain]
expected: FAIL
[<iframe>: combined response Content-Type: text/html;charset=gbk text/plain text/html]
expected: FAIL
[<iframe>: separate response Content-Type: text/html */*]
expected: FAIL
[<iframe>: separate response Content-Type: text/html;x=" text/plain]
expected: FAIL
[<iframe>: separate response Content-Type: text/plain */*;charset=gbk]
expected: FAIL

View file

@ -11,3 +11,6 @@
[X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
expected: FAIL
[X-Content-Type-Options%3A%0D%0AX-Content-Type-Options%3A%20nosniff]
expected: FAIL

View file

@ -1,4 +0,0 @@
[traverse_the_history_1.html]
[Multiple history traversals from the same task]
expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_5.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -1,4 +1,8 @@
[embedded-opener-remove-frame.html]
expected: TIMEOUT
[opener of discarded nested browsing context]
expected: FAIL
[opener of discarded auxiliary browsing context]
expected: TIMEOUT

View file

@ -0,0 +1,2 @@
[fieldset-border-radius-with-alpha.html]
expected: FAIL

View file

@ -171,6 +171,3 @@
[XHTML img usemap="#hash-id"]
expected: FAIL
[HTML (standards) IMG usemap="no-hash-name"]
expected: FAIL

View file

@ -1,5 +1,5 @@
[iframe_sandbox_popups_escaping-1.html]
expected: CRASH
expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: TIMEOUT

View file

@ -1,5 +1,4 @@
[iframe_sandbox_popups_escaping-3.html]
expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: TIMEOUT
expected: FAIL

View file

@ -1,5 +1,5 @@
[iframe_sandbox_popups_nonescaping-3.html]
expected: CRASH
expected: TIMEOUT
[Check that popups from a sandboxed iframe do not escape the sandbox]
expected: FAIL
expected: NOTRUN

View file

@ -1,4 +0,0 @@
[form-double-submit-2.html]
[preventDefault should allow onclick submit() to succeed]
expected: FAIL

View file

@ -0,0 +1,4 @@
[form-double-submit-3.html]
[<button> should have the same double-submit protection as <input type=submit>]
expected: FAIL

View file

@ -0,0 +1,4 @@
[form-double-submit.html]
[default submit action should supersede onclick submit()]
expected: FAIL

View file

@ -3,3 +3,6 @@
[The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: TIMEOUT
[The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: FAIL

View file

@ -0,0 +1,22 @@
[iframe-failed-commit.html]
[Precondition]
expected: FAIL
[Test iframe redirecting to non-existent host]
expected: FAIL
[Cross-origin iframe that doesn't comply with CSP attribute gets reported]
expected: FAIL
[Same-origin iframe that complies with CSP attribute gets reported]
expected: FAIL
[Cross-origin iframe that complies with CSP attribute gets reported]
expected: FAIL
[Same-origin iframe that doesn't comply with CSP attribute gets reported]
expected: FAIL
[Test iframe from non-existent host]
expected: FAIL

View file

@ -374,3 +374,9 @@
[X SNR (39.927354911150644 dB) is not greater than or equal to 65.737. Got 39.927354911150644.]
expected: FAIL
[X SNR (-336.5274753602913 dB) is not greater than or equal to 65.737. Got -336.5274753602913.]
expected: FAIL
[X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[28696\]\t9.9558435935349637e+18\t9.3139332532882690e-1\t9.9558435935349637e+18\t1.0689193622919801e+19\t3.8985999999999999e-3\n\t[28697\]\t7.0477002859115601e-1\t9.0675884485244751e-1\t2.0198881626129150e-1\t2.2275913536212616e-1\t3.8985999999999999e-3\n\tMax AbsError of 9.9558435935349637e+18 at index of 28696.\n\tMax RelError of 1.0689193622919801e+19 at index of 28696.\n]
expected: FAIL

View file

@ -1,5 +1,4 @@
[audiocontext-not-fully-active.html]
expected: TIMEOUT
[frame in navigated remote-site frame]
expected: FAIL

View file

@ -1,5 +0,0 @@
[018.html]
expected: TIMEOUT
[origin of the script that invoked the method, javascript:]
expected: TIMEOUT

View file

@ -1,4 +1,5 @@
[sharedworker-in-worker.html]
expected: ERROR
[Base URL in workers: new SharedWorker()]
expected: FAIL

View file

@ -1,2 +0,0 @@
[Worker-constructor.html]
expected: ERROR

View file

@ -7,7 +7,7 @@
expected: FAIL
[Opening a blob URL in a new window immediately before revoking it works.]
expected: TIMEOUT
expected: FAIL
[Opening a blob URL in a noopener about:blank window immediately before revoking it works.]
expected: TIMEOUT

View file

@ -17393,15 +17393,6 @@
]
]
},
"input-events": {
"input-events-cut-paste-manual.html": [
"4091c87cdd34720c29578b766b5791f8d68d4982",
[
null,
{}
]
]
},
"mediacapture-depth": {
"dictionary-manual.https.html": [
"0a5b824881509baee240f385a9eeaa380a579948",
@ -121654,6 +121645,19 @@
],
{}
]
],
"translation-animation-subpixel-offset.html": [
"5692a0823289811967e889969603cf8799cf7cf6",
[
null,
[
[
"/css/css-animations/translation-animation-subpixel-offset-ref.html",
"=="
]
],
{}
]
]
},
"css-backgrounds": {
@ -121844,7 +121848,7 @@
]
],
"attachment-local-clipping-image-5.html": [
"ddd02dcfaefdd805188b1b9682f2070d6f81a976",
"1d6f1a8c6628a47e5421eb99769f56d53a74a08a",
[
null,
[
@ -121864,7 +121868,7 @@
],
[
0,
160
300
]
]
]
@ -134577,6 +134581,19 @@
],
{}
]
],
"content-visibility-079.html": [
"9a8b46bf5c69507289150deb707b518234de8eab",
[
null,
[
[
"/css/css-contain/content-visibility/content-visibility-079-ref.html",
"=="
]
],
{}
]
]
},
"counter-scoping-001.html": [
@ -164906,6 +164923,32 @@
{}
]
],
"no-scrollable-overflow-vertical-rl-2.html": [
"895f9bafc621329b95d1bb2fe086602945991cde",
[
null,
[
[
"/css/reference/ref-filled-green-100px-square-only.html",
"=="
]
],
{}
]
],
"no-scrollable-overflow-vertical-rl.html": [
"13385a2685cb8b6c33c94a9b09186336fda9c61a",
[
null,
[
[
"/css/reference/ref-filled-green-100px-square-only.html",
"=="
]
],
{}
]
],
"overflow-body-propagation-001.html": [
"0998fe68e007d5a46ff11d5ff87fdbad12d1dfe2",
[
@ -245334,7 +245377,7 @@
]
],
"fieldset-border-radius-with-alpha.html": [
"36b9fc8c8ef7aa08473e4061c090042882b8b582",
"7a942076fbeaaee41c668ced609dee94906e56be",
[
null,
[
@ -257305,10 +257348,6 @@
[]
],
"workflows": {
"detect_pull_request_preview.yml": [
"02870d8c7d223a371c09a97b5906610af191a5e4",
[]
],
"documentation.yml": [
"b8bf7972b0f7666f8a01a81b05e8dec432732225",
[]
@ -257322,7 +257361,7 @@
[]
],
"pull_request_previews.yml": [
"bf049dd9e63b0a8901bbf693a42fab2dfb621305",
"676aff3021242bb0d9b0ac49209088f48beca9fc",
[]
],
"regen_certs.yml": [
@ -300804,6 +300843,10 @@
"transform-animation-under-large-scale-ref.html": [
"14b41366268c3a33f2d49e477bab2d40c6acb432",
[]
],
"translation-animation-subpixel-offset-ref.html": [
"5fc04d972b41d089b71d0c0baabc0d8e0d28c1d0",
[]
]
},
"css-backgrounds": {
@ -302846,6 +302889,10 @@
"64629ca11977c77e4b1420881c5bc59a50816323",
[]
],
"content-visibility-079-ref.html": [
"416f23e8ca6aff4cd121a1c6b7a77945accd433e",
[]
],
"inline-container-with-child-ref.html": [
"af51cbd5db8f3eff56411345860548e95fe4d354",
[]
@ -333190,7 +333237,7 @@
[]
],
"testdriver-extension-tutorial.md": [
"0d73f9d41f8205802407aa32cdd5e11aae2c44f9",
"185a27f1a46af840f19395cab885451088130cb6",
[]
],
"testdriver.md": [
@ -338885,6 +338932,10 @@
[]
],
"resources": {
"check-sandbox-flags.html": [
"0dc95315f164d693bd7bd0622880781f30bad7a1",
[]
],
"post-done-to-opener.html": [
"b47f0f274ef43148d3be418e2c0628a567f1c3c1",
[]
@ -341026,7 +341077,7 @@
[]
],
"redirect.py": [
"21a3dec9d224c1fca4a8815d9830d392c44cd18b",
"88dbd60fae7a41e245a0b822f8336ddeff56f410",
[]
],
"report.py": [
@ -341034,7 +341085,7 @@
[]
],
"reporting-common.js": [
"3b1820cebb8f1d5f8b7fb00b90740cee6de9fbb9",
"01f835e12ad5f24c07c50ae7376183628fff80bc",
[]
],
"test-access-property.js": [
@ -345780,7 +345831,7 @@
[]
],
"fieldset-border-radius-with-alpha-ref.html": [
"577482a328b47b72b78ddc22abef02e12836a378",
"5cfe13c40d465a5cd0cebfbafbb8db9d45491cbf",
[]
],
"fieldset-containing-block-ref.html": [
@ -351659,7 +351710,7 @@
},
"js-self-profiling": {
"__dir__.headers": [
"63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
"35b10bd2ccdbfa99feb96079fafab61346d025ed",
[]
]
},
@ -357053,6 +357104,14 @@
"dee5b62f0005705c18dde06865ab5a9947a37357",
[]
],
"csp-default-none.html": [
"1f59d8c2250f7f17f143887dc4f8e68cdf7faa83",
[]
],
"csp-default-none.html.headers": [
"d66f886dd23d44bb33750cb5723f3edb6b7930ab",
[]
],
"document-domain-no-impact.sub.html": [
"fbd7bc3b6e21ee39478c8a63780bb673dafe96a4",
[]
@ -362266,7 +362325,7 @@
[]
],
"pr_preview.py": [
"2396e9b8aee8a166c3b9321c7e9ec2e8f9b549c0",
"5bd2365164b98ba1a8c90a8d1a987fbaf8c97c73",
[]
],
"regen_certs.py": [
@ -362355,7 +362414,7 @@
[]
],
"test_pr_preview.py": [
"ed6310c1ea129b19f313c29c98e5238a85546e42",
"87478727d3e20528fea165f0634df7d299d5a6ea",
[]
]
},
@ -370453,8 +370512,8 @@
},
"urlpattern": {
"resources": {
"utils.js": [
"9132a52816e0c1cf0bc3a1b577d6611e65be8f6f",
"urlpatterntestdata.json": [
"66038e03d23bbb14f6d890a02e3e8dbefcb5aefe",
[]
]
}
@ -370898,7 +370957,7 @@
]
},
"generate-test-wbns.sh": [
"8fc28bb9db7cd9900bee9c3555e7e7d84e41b389",
"3c8a3ea68d60522452be94a7f8d0aaf9b11f5c55",
[]
],
"location": {
@ -370961,6 +371020,10 @@
[]
]
},
"urn-uuid.har": [
"e0cea3699ae53beedde24dc55287b7d4407083dd",
[]
],
"wbn": {
"__dir__.headers": [
"21e57b9caca1183ecda6f5d4fb9ebd556abf769a",
@ -371019,6 +371082,10 @@
"subresource.wbn": [
"eabd994ff8bceaf4ef52b329b4faa158c292af12",
[]
],
"urn-uuid.wbn": [
"a4bd3efc9cc73e1405bad5209631e0e1e1f352b5",
[]
]
}
}
@ -397200,7 +397267,7 @@
]
],
"script-src-1_10.html": [
"a1bfdaeb15bfbadb322b393b34f6872de1c55699",
"fa25976c128f195828dbfdb2e7c068f72aa83f8e",
[
null,
{}
@ -415778,7 +415845,7 @@
]
],
"fixed-layout-2.html": [
"7cb09d4dd04de1da380837b0b4c2e82cc1492f59",
"13a4664249f3a758858e190fa9361ffc6b2ed17b",
[
null,
{}
@ -458175,6 +458242,13 @@
{}
]
],
"sandbox-initial-empty-document-toward-same-origin.html": [
"d1306c970322682f3979c497a5decd78218ba845",
[
null,
{}
]
],
"sandbox-navigation-timing.tentative.html": [
"686f1c0c9f6847772edbe2ef070a483db1df3d9d",
[
@ -496248,6 +496322,15 @@
}
]
],
"input-events-cut-paste.html": [
"f2ca5a0a654f270d33d8662f4e8b25369cd98df7",
[
null,
{
"testdriver": true
}
]
],
"input-events-exec-command.html": [
"8f8493651e5af4b747d952ac786334ace8ed61c4",
[
@ -500396,7 +500479,18 @@
null,
{}
]
]
],
"mse-for-webcodecs": {
"tentative": {
"mediasource-webcodecs-addsourcebuffer.html": [
"a9dd7072b0758d58bac67c13dcb78c76dc6aa669",
[
null,
{}
]
]
}
}
},
"mediacapture-depth": {
"idlharness.html": [
@ -523278,6 +523372,13 @@
}
]
],
"iframe-failed-commit.html": [
"75e19f788efb2ecbccf67c4df02f030e3d9415c7",
[
null,
{}
]
],
"nested-context-navigations-embed.html": [
"bbba46c50edf21f50b49099c779dd5a8792faa0f",
[
@ -537621,7 +537722,7 @@
]
],
"block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.tentative.html": [
"f36ae94ea10ce95d7087ec22ef4a25f157f7e6a8",
"c24715718d17561f779c6df7098f4c0b874675a9",
[
null,
{}
@ -540521,127 +540622,48 @@
]
},
"urlpattern": {
"input.https.any.js": [
"71a8fb237b8101789828e1352dc516d97c7d509a",
"urlpattern.https.any.js": [
"27b03d0ff03261d50721edb7b22b8205e0610238",
[
"urlpattern/input.https.any.html",
"urlpattern/urlpattern.https.any.html",
{
"script_metadata": [
[
"global",
"window,worker"
],
[
"script",
"resources/utils.js"
]
]
}
],
[
"urlpattern/input.https.any.serviceworker.html",
"urlpattern/urlpattern.https.any.serviceworker.html",
{
"script_metadata": [
[
"global",
"window,worker"
],
[
"script",
"resources/utils.js"
]
]
}
],
[
"urlpattern/input.https.any.sharedworker.html",
"urlpattern/urlpattern.https.any.sharedworker.html",
{
"script_metadata": [
[
"global",
"window,worker"
],
[
"script",
"resources/utils.js"
]
]
}
],
[
"urlpattern/input.https.any.worker.html",
"urlpattern/urlpattern.https.any.worker.html",
{
"script_metadata": [
[
"global",
"window,worker"
],
[
"script",
"resources/utils.js"
]
]
}
]
],
"pathname.https.any.js": [
"5de41ccc438de61af3dd396b3b67489059140f55",
[
"urlpattern/pathname.https.any.html",
{
"script_metadata": [
[
"global",
"window,worker"
],
[
"script",
"resources/utils.js"
]
]
}
],
[
"urlpattern/pathname.https.any.serviceworker.html",
{
"script_metadata": [
[
"global",
"window,worker"
],
[
"script",
"resources/utils.js"
]
]
}
],
[
"urlpattern/pathname.https.any.sharedworker.html",
{
"script_metadata": [
[
"global",
"window,worker"
],
[
"script",
"resources/utils.js"
]
]
}
],
[
"urlpattern/pathname.https.any.worker.html",
{
"script_metadata": [
[
"global",
"window,worker"
],
[
"script",
"resources/utils.js"
]
]
}
@ -544797,7 +544819,7 @@
]
],
"subresource-loading-from-web-bundle.tentative.html": [
"c2de0eb0994f54c65a342b501aa3e9107949faf7",
"53a9c1b2cc707271c8d7b0e7fb7385fd59839744",
[
null,
{}

View file

@ -1,4 +0,0 @@
[hit-test-floats-002.html]
[Hit test float]
expected: FAIL

View file

@ -1,4 +0,0 @@
[hit-test-floats-003.html]
[Miss float below something else]
expected: FAIL

View file

@ -1,4 +0,0 @@
[hit-test-floats-005.html]
[Miss clipped float]
expected: FAIL

View file

@ -0,0 +1,2 @@
[translation-animation-subpixel-offset.html]
expected: TIMEOUT

View file

@ -2,3 +2,6 @@
[Hit test intersecting scaled box]
expected: FAIL
[Hit test within unscaled box]
expected: FAIL

View file

@ -1,4 +0,0 @@
[elementFromPoint-001.html]
[CSSOM View - 5 - extensions to the Document interface]
expected: FAIL

View file

@ -0,0 +1,4 @@
[elementsFromPoint-invalid-cases.html]
[The root element is the last element returned for otherwise empty queries within the viewport]
expected: FAIL

View file

@ -1,2 +0,0 @@
[matchMedia-display-none-iframe.html]
expected: ERROR

View file

@ -309,12 +309,6 @@
[fetch(): separate response Content-Type: text/plain ]
expected: NOTRUN
[<iframe>: combined response Content-Type: text/html */*]
expected: FAIL
[<iframe>: separate response Content-Type: text/html;" text/plain]
expected: FAIL
[<iframe>: separate response Content-Type: text/html */*;charset=gbk]
expected: FAIL
@ -324,3 +318,15 @@
[<iframe>: combined response Content-Type: text/html;x=" text/plain]
expected: FAIL
[<iframe>: combined response Content-Type: text/html;charset=gbk text/plain text/html]
expected: FAIL
[<iframe>: separate response Content-Type: text/html */*]
expected: FAIL
[<iframe>: separate response Content-Type: text/html;x=" text/plain]
expected: FAIL
[<iframe>: separate response Content-Type: text/plain */*;charset=gbk]
expected: FAIL

View file

@ -11,3 +11,6 @@
[X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
expected: FAIL
[X-Content-Type-Options%3A%0D%0AX-Content-Type-Options%3A%20nosniff]
expected: FAIL

View file

@ -1,4 +0,0 @@
[traverse_the_history_1.html]
[Multiple history traversals from the same task]
expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_5.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -1,7 +1,11 @@
[embedded-opener-remove-frame.html]
expected: TIMEOUT
[opener and "removed" embedded documents]
expected: FAIL
[opener of discarded nested browsing context]
expected: FAIL
[opener of discarded auxiliary browsing context]
expected: TIMEOUT

View file

@ -0,0 +1,2 @@
[fieldset-border-radius-with-alpha.html]
expected: FAIL

View file

@ -172,6 +172,3 @@
[XHTML img usemap="http://example.org/#garbage-before-hash-id"]
expected: FAIL
[HTML (standards) IMG usemap="no-hash-name"]
expected: FAIL

View file

@ -1,6 +1,6 @@
[iframe_sandbox_popups_escaping-1.html]
type: testharness
expected: CRASH
expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: TIMEOUT

View file

@ -1,6 +1,5 @@
[iframe_sandbox_popups_escaping-3.html]
type: testharness
expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: TIMEOUT
expected: FAIL

View file

@ -1,5 +1,5 @@
[iframe_sandbox_popups_nonescaping-3.html]
expected: CRASH
expected: TIMEOUT
[Check that popups from a sandboxed iframe do not escape the sandbox]
expected: FAIL
expected: NOTRUN

View file

@ -1,4 +0,0 @@
[form-double-submit-2.html]
[preventDefault should allow onclick submit() to succeed]
expected: FAIL

View file

@ -0,0 +1,4 @@
[form-double-submit-3.html]
[<button> should have the same double-submit protection as <input type=submit>]
expected: FAIL

View file

@ -0,0 +1,4 @@
[form-double-submit.html]
[default submit action should supersede onclick submit()]
expected: FAIL

View file

@ -4,3 +4,6 @@
[The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: TIMEOUT
[The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: FAIL

View file

@ -0,0 +1,22 @@
[iframe-failed-commit.html]
[Precondition]
expected: FAIL
[Test iframe redirecting to non-existent host]
expected: FAIL
[Cross-origin iframe that doesn't comply with CSP attribute gets reported]
expected: FAIL
[Same-origin iframe that complies with CSP attribute gets reported]
expected: FAIL
[Cross-origin iframe that complies with CSP attribute gets reported]
expected: FAIL
[Same-origin iframe that doesn't comply with CSP attribute gets reported]
expected: FAIL
[Test iframe from non-existent host]
expected: FAIL

View file

@ -602,3 +602,9 @@
[X SNR (39.927354911150644 dB) is not greater than or equal to 65.737. Got 39.927354911150644.]
expected: FAIL
[X SNR (-336.5274753602913 dB) is not greater than or equal to 65.737. Got -336.5274753602913.]
expected: FAIL
[X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[28696\]\t9.9558435935349637e+18\t9.3139332532882690e-1\t9.9558435935349637e+18\t1.0689193622919801e+19\t3.8985999999999999e-3\n\t[28697\]\t7.0477002859115601e-1\t9.0675884485244751e-1\t2.0198881626129150e-1\t2.2275913536212616e-1\t3.8985999999999999e-3\n\tMax AbsError of 9.9558435935349637e+18 at index of 28696.\n\tMax RelError of 1.0689193622919801e+19 at index of 28696.\n]
expected: FAIL

View file

@ -1,5 +1,4 @@
[audiocontext-not-fully-active.html]
expected: TIMEOUT
[frame in navigated remote-site frame]
expected: FAIL

View file

@ -1,5 +0,0 @@
[018.html]
expected: TIMEOUT
[origin of the script that invoked the method, javascript:]
expected: TIMEOUT

View file

@ -1,4 +1,5 @@
[sharedworker-in-worker.html]
expected: ERROR
[Base URL in workers: new SharedWorker()]
expected: FAIL

View file

@ -1,2 +0,0 @@
[Worker-constructor.html]
expected: ERROR

View file

@ -1,25 +0,0 @@
name: pr-preview-detect
# This workflow triggers on deployment, which can never happen on a fork.
on: deployment
jobs:
detect-deployment:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
# By default, the "checkout" Action will attempt to check out the
# revision to be deployed. Because it does not fetch GitHub Pull Request
# branches, this will fail.
with:
ref: refs/heads/master
- name: Install dependency
run: pip install requests
- name: Detect deployment
run:
./tools/ci/pr_preview.py
--host https://api.github.com
--github-project web-platform-tests/wpt
detect
--target https://wptpr.live
--timeout 600
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

View file

@ -1,31 +1,35 @@
name: pr-preview-sync
# Create previews for pull requests on https://wptpr.live
#
# Mirroring pull requests to wptpr.live requires write access to the WPT GitHub
# repository. This means that we cannot run any code from the pull request in
# doing so, to defend against attacks from malicious pull requests. As such,
# this workflow uses the 'pull_request_target' event which runs in the context
# of the base repository and thus doesn't run code from the pull request. Any
# code run in this workflow should NOT checkout or trust code from the pull
# request.
#
# Note that in pull_request_target the GITHUB token is read/write.
name: create-pr-preview
on:
schedule:
- cron: '*/5 * * * *'
pull_request_target:
types: [opened, synchronize, reopened, closed, labeled, unlabeled]
jobs:
update-pr-preview:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install dependency
run: pip install requests
- name: Synchronize state
- name: Deploy PR
# Use a conditional step instead of a conditional job to work around #20700.
if: github.repository == 'web-platform-tests/wpt'
run:
./tools/ci/pr_preview.py
--host https://api.github.com
--github-project web-platform-tests/wpt
synchronize
--window 480
--target https://wptpr.live
--timeout 600
env:
# This Workflow must trigger further workflows. The GitHub-provided
# `GITHUB_TOKEN` secret is incapable of doing this [1], so a
# user-generated token must be specified instead. This token requires
# the "repo" scope, and is should be stored as a Secret named
# "DEPLOY_TOKEN" in this GitHub project.
#
# [1] https://help.github.com/en/github/automating-your-workflow-with-github-actions/events-that-trigger-workflows
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -15,7 +15,7 @@
var t_spv = async_test("Test that securitypolicyviolation event is fired");
window.addEventListener("securitypolicyviolation", t_spv.step_func_done(function(e) {
assert_equals(e.violatedDirective, "script-src");
assert_equals(e.violatedDirective, "script-src-elem");
}));
</script>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<style>
div {
position: absolute;
top: 11px;
left: 1px;
width: 100px;
height: 100px;
background: green;
}
</style>
<div></div>

View file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<title>Translation-only transform animation with subpixel offset</title>
<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org">
<link rel="help" href="https://crbug.com/1155029">
<link rel="match" href="translation-animation-subpixel-offset-ref.html">
<style>
@keyframes move {
0% {transform: translateY(10px);}
100% {transform: translateY(10px);}
}
#red {
position: absolute;
top: 11px;
left: 1px;
width: 100px;
height: 100px;
background: red;
}
#container {
position: absolute;
top: 0.4px;
left: 0.6px;
}
#target {
position: relative;
top: 0.4px;
left: 0.6px;
width: 100px;
height: 100px;
background: green;
animation: move 1s infinite alternate;
}
</style>
<div id="red"></div>
<div id="container">
<div id="target"></div>
</div>

View file

@ -2,7 +2,7 @@
<title>CSS Test: background-{attachment: local; clip: padding-box; image}; border-radius</title>
<link rel="match" href="attachment-local-clipping-image-4-ref.html" />
<meta name="flags" content="dom" />
<meta name=fuzzy content="0-10;0-160">
<meta name=fuzzy content="0-10;0-300">
<link rel="author" title="Simon Sapin" href="http://exyr.org/about/" />
<link rel="help" href="http://www.w3.org/TR/css3-background/#the-background-attachment" />
<style>

View file

@ -0,0 +1,8 @@
<!doctype HTML>
<html>
<meta charset="utf8">
<title>CSS Content Visibility: auto in overflow hidden paints (reference)</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
<div>content</div>

View file

@ -0,0 +1,16 @@
<!doctype HTML>
<meta charset="utf8">
<title>CSS Content Visibility: auto in overflow hidden paints</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
<link rel="match" href="content-visibility-079-ref.html">
<meta name="assert" content="content-visibility auto element paints in an overflow hidden element that is not sized">
<style>
.auto { content-visibility: auto; }
.overflow { overflow: hidden; }
</style>
<div class=overflow>
<div class=auto>content</div>
</div>

View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<meta charset="utf-8">
<title>CSS Overflow Test: Scrollable overflow area of a multicol container in vertical-rl writing-mode</title>
<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#scrollable">
<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="This test verifies the nested float element in a multicol container in vertical-rl writing-mode shouldn't contribute to its container'sscrollable overflow area.">
<style>
.scroll-container {
writing-mode: vertical-rl;
inline-size: 100px;
block-size: 100px;
overflow: auto;
}
.multicol {
block-size: 100px;
inline-size: 100px;
column-fill: auto;
column-count: 2;
column-gap: 0;
}
.float {
float: left;
inline-size: 50px;
block-size: 100px;
background: green;
}
</style>
<!--This test passes if the scroll-container has no scrollbar. -->
<p>Test passes if there is a filled green square.</p>
<div class="scroll-container">
<div class="multicol">
<div>
<div style="block-size: 100px; background: green;"></div>
<div class="float"></div>
</div>
</div>
</div>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<meta charset="utf-8">
<title>CSS Overflow Test: Scrollable overflow area of a multicol container in vertical-rl writing-mode</title>
<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#scrollable">
<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="This test verifies the float element in a multicol container in vertical-rl writing-mode shouldn't contribute to its container'sscrollable overflow area.">
<style>
.scroll-container {
writing-mode: vertical-rl;
inline-size: 100px;
block-size: 100px;
overflow: auto;
}
.multicol {
block-size: 100px;
inline-size: 100px;
column-fill: auto;
column-count: 2;
column-gap: 0;
}
.float {
float: left;
inline-size: 50px;
block-size: 100px;
background: green;
}
</style>
<!--This test passes if the scroll-container has no scrollbar. -->
<p>Test passes if there is a filled green square.</p>
<div class="scroll-container">
<div class="multicol">
<div style="block-size: 100px; background: green;"></div>
<div class="float"></div>
</div>
</div>
</html>

View file

@ -60,9 +60,9 @@
100
],
[
"Table-layout:fixed reports it is not applied when width is auto",
"Table-layout:fixed reports fixed when width is auto",
getComputedStyle(document.querySelector("x-table:nth-of-type(1)")).tableLayout,
'auto'
'fixed'
],
[
"Table-layout:fixed is not applied when width is max-content",
@ -70,9 +70,9 @@
100
],
[
"Table-layout:fixed reports it is not applied when width is max-content",
"Table-layout:fixed reports fixed when width is max-content",
getComputedStyle(document.querySelector("x-table:nth-of-type(2)")).tableLayout,
'auto'
'fixed'
],
[
"Table-layout:fixed is not applied when width is min-content",

View file

@ -15,6 +15,31 @@ First, we need to think of what the API will look like a little. We will be usin
The first part of this will be browser agnostic, but later we will need to implement a specific layer for each browser (here we will do Firefox and Chrome).
## RFC Process
Before we invest any significant work into extending the testdriver.js API, we should check in with other stakeholders of the Web Platform Tests community on the proposed changes, by writing an [RFC](https://github.com/web-platform-tests/rfcs) ("request for comments"). This is especially useful for changes that may affect test authors or downstream users of web-platform-tests.
The [process is given in more detail in the RFC repo](https://github.com/web-platform-tests/rfcs#the-rfc-process), but to start let's send in a PR to the RFCs repo by adding a file named `rfcs/testdriver_set_window_rect.md`:
```md
# RFC N: Add window resizing to testdriver.js
(*Note: N should be replaced by the PR number*)
## Summary
Add testdriver.js support for the [Set Window Rect command](https://w3c.github.io/webdriver/#set-window-rect).
## Details
(*add details here*)
## Risks
(*add risks here*)
```
Members of the community will then have the opportunity to comment on our proposed changes, and perhaps suggest improvements to our ideas. If all goes well it will be approved and merged in.
With that said, developing a prototype implementation may help others evaluate the proposal during the RFC process, so let's move on to writing some code.
## Code!
### [resources/testdriver.js](https://github.com/web-platform-tests/wpt/blob/master/resources/testdriver.js)
@ -273,4 +298,3 @@ async_test(t => {
});
</script>
```

View file

@ -0,0 +1,8 @@
<script>
try {
document.domain = document.domain;
parent.postMessage('document-domain-is-allowed', '*');
} catch (error) {
parent.postMessage('document-domain-is-disallowed', '*');
}
</script>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>
Check sandbox-flags inheritance in case of javascript window reuse.
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
promise_test(async test => {
let message = new Promise(resolve =>
window.addEventListener("message", event => resolve(event.data))
);
// Create an initial empty document in the iframe, sandboxed. It will attempt
// to load a slow page, but won't have time.
let iframe = document.createElement("iframe");
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin");
iframe.src = "/fetch/api/resources/infinite-slow-response.py";
document.body.appendChild(iframe);
// Remove sandbox flags. This should apply to documents committed from
// navigations started after this instruction.
iframe.removeAttribute("sandbox");
iframe.src = "./resources/check-sandbox-flags.html";
// The window is reused, but the new sandbox flags should be used.
assert_equals(await message, "document-domain-is-allowed");
});
</script>

View file

@ -1,3 +1,5 @@
from wptserve.utils import isomorphic_encode
def main(request, response):
response.status = 302
response.headers.set(b"Location", request.url[request.url.find('?')+1:])
response.headers.set(b"Location", isomorphic_encode(request.url[request.url.find(u'?')+1:]))

View file

@ -171,7 +171,8 @@ function navigationReportingTest(testName, host, coop, coep, coopRo, coepRo,
`|header(Cross-Origin-Embedder-Policy-Report-Only,${encodeURIComponent(coepRo)})`+
`&uuid=${executorToken}`;
const openee = window.open(openee_url);
t.add_cleanup(() => send(5, "window.close()"));
const uuid = token();
t.add_cleanup(() => send(uuid, "window.close()"));
// 1. Make sure the new document is loaded.
send(executorToken, `

View file

@ -2,10 +2,10 @@
<style>
div {
background-color: green;
height: 150px;
height: 110px;
position: absolute;
width: 150px;
top: 50px;
top: 70px;
}
</style>
<p>There should be no red.</p>

View file

@ -9,14 +9,18 @@ fieldset {
height: 100px;
width: 100px;
}
legend {
height: 50px;
width: 50px;
}
div {
background-color: green;
height: 150px;
height: 110px;
position: absolute;
width: 150px;
top: 50px;
top: 70px;
}
</style>
<p>There should be no red.</p>
<fieldset></fieldset>
<fieldset><legend></legend></fieldset>
<div></div>

View file

@ -1,83 +0,0 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Cut and Paste should trigger corresponding InputEvent</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<p>To manually run this test, please follow the steps below:<br/>
1. Select 'plain' => Cut (e.g. Ctrl/Cmd-X) => Paste (e.g. Ctrl/Cmd-V).<br/>
2. Select 'rich' => Cut => Paste.<br/>
3. Select 'prevent' => Paste.<br/>
4. Select 'prevent' => Cut => Select 'normal' => Paste.<br/>
<br/>
If a "PASS" result appears the test passes, otherwise it fails</p>
<textarea id="test1_plain">plain</textarea>
<p id="test2_editable" contenteditable><b>rich</b></p>
<p id="test3_editable_prevent" contenteditable>prevent</p>
<p id="test3_editable_normal" contenteditable>normal</p>
<script>
async_test(t => {
const expectedEventLog = [
'cut-[null]', 'beforeinput-deleteByCut', 'input-deleteByCut',
'paste-[null]', 'beforeinput-insertFromPaste', 'input-insertFromPaste'];
const actualEventLog = [];
for (let eventType of ['beforeinput', 'input', 'cut', 'paste']) {
document.getElementById('test1_plain').addEventListener(eventType, t.step_func(event => {
if (event.type === 'beforeinput' && event.inputType === 'insertFromPaste') {
assert_equals(event.data, 'plain');
assert_equals(event.dataTransfer, null);
}
actualEventLog.push(`${event.type}-${event.inputType || '[null]'}`);
if (actualEventLog.length === expectedEventLog.length) {
assert_array_equals(actualEventLog, expectedEventLog,
`Expected: ${expectedEventLog}; Actual: ${actualEventLog}.`);
t.done();
}
}));
}
}, 'Event order and data on textarea.');
async_test(t => {
const expectedEventLog = [
'cut-[null]', 'beforeinput-deleteByCut', 'input-deleteByCut',
'paste-[null]', 'beforeinput-insertFromPaste', 'input-insertFromPaste'];
const actualEventLog = [];
for (let eventType of ['beforeinput', 'input', 'cut', 'paste']) {
document.getElementById('test2_editable').addEventListener(eventType, t.step_func(event => {
if (event.type === 'beforeinput' && event.inputType === 'insertFromPaste') {
assert_equals(event.data, null);
assert_equals(event.dataTransfer.getData('text/plain'), 'rich');
assert_regexp_match(event.dataTransfer.getData('text/html'), /<b.*>rich<\/b>$/);
}
actualEventLog.push(`${event.type}-${event.inputType || '[null]'}`);
if (actualEventLog.length === expectedEventLog.length) {
assert_array_equals(actualEventLog, expectedEventLog,
`Expected: ${expectedEventLog}; Actual: ${actualEventLog}.`);
t.done();
}
}));
}
}, 'Event order and dataTransfer on contenteditable.');
async_test(t => {
const prevent = document.getElementById('test3_editable_prevent');
const normal = document.getElementById('test3_editable_normal');
prevent.addEventListener('beforeinput', t.step_func(event => {
if (event.inputType === 'deleteByCut' ||
event.inputType === 'insertFromPaste') {
event.preventDefault();
}
}));
normal.addEventListener('input', t.step_func(event => {
if (event.inputType === 'insertFromPaste') {
assert_equals(prevent.textContent, 'prevent');
assert_equals(normal.textContent, 'prevent');
t.done();
}
}));
}, 'preventDefault() should prevent DOM modification but allow clipboard updates.');
</script>

View file

@ -0,0 +1,141 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Cut and Paste should trigger corresponding InputEvent</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script type="text/javascript" src="pointerevent_support.js"></script>
<p>To manually run this test, please follow the steps below:<br/>
1. Select 'plain' => Cut (e.g. Ctrl/Cmd-X) => Paste (e.g. Ctrl/Cmd-V).<br/>
2. Select 'rich' => Cut => Paste.<br/>
3. Select 'prevent' => Paste.<br/>
4. Select 'prevent' => Cut => Select 'normal' => Paste.<br/>
<br/>
If a "PASS" result appears the test passes, otherwise it fails</p>
<textarea id="test1_plain">plain</textarea>
<p id="test2_editable" contenteditable><b>rich</b></p>
<p id="test3_editable_prevent" contenteditable>prevent</p>
<p id="test3_editable_normal" contenteditable>normal</p>
<script>
function resolveWhen(condition) {
return new Promise((resolve, reject) => {
function tick() {
if (condition())
resolve();
else
requestAnimationFrame(tick.bind(this));
}
tick();
});
}
let modifier_key = "\uE009";
if(navigator.platform.includes('Mac'))
modifier_key = "\uE03D";
const commands = {
COPY: 'copy',
CUT: 'cut',
PASTE: 'paste',
}
function selectTextCommand(target, command) {
let command_key = "";
if(command == "copy")
command_key = "c";
else if (command == "cut")
command_key = "x";
else if (command == "paste")
command_key = "v";
return new test_driver.Actions()
.pointerMove(0, 0, {origin: target})
.pointerDown()
.pointerUp()
.addTick()
.keyDown(modifier_key)
.keyDown("a")
.keyUp("a")
.keyDown(command_key)
.keyUp(command_key)
.keyUp(modifier_key)
.send();
}
function selectTextCutAndPaste(target1, target2) {
return selectTextCommand(target1, commands.CUT).then(() => {
return selectTextCommand(target2, commands.PASTE);
})
}
promise_test(async test => {
const expectedEventLog = [
'cut-[null]', 'beforeinput-deleteByCut', 'input-deleteByCut',
'paste-[null]', 'beforeinput-insertFromPaste', 'input-insertFromPaste'];
const actualEventLog = [];
const text1 = document.getElementById("test1_plain");
for (let eventType of ['beforeinput', 'input', 'cut', 'paste']) {
text1.addEventListener(eventType, test.step_func(function() {
if (event.type === 'beforeinput' && event.inputType === 'insertFromPaste') {
assert_equals(event.data, 'plain');
assert_equals(event.dataTransfer, null);
}
actualEventLog.push(`${event.type}-${event.inputType || '[null]'}`);
}));
}
await selectTextCutAndPaste(text1, text1);
await resolveWhen(() => { return actualEventLog.length == expectedEventLog.length });
assert_array_equals(actualEventLog, expectedEventLog,
`Expected: ${expectedEventLog}; Actual: ${actualEventLog}.`);
}, 'Event order and data on textarea.');
promise_test(async test => {
const expectedEventLog = [
'cut-[null]', 'beforeinput-deleteByCut', 'input-deleteByCut',
'paste-[null]', 'beforeinput-insertFromPaste', 'input-insertFromPaste'];
const actualEventLog = [];
const text2 = document.getElementById("test2_editable");
for (let eventType of ['beforeinput', 'input', 'cut', 'paste']) {
text2.addEventListener(eventType, test.step_func(function() {
if (event.type === 'beforeinput' && event.inputType === 'insertFromPaste') {
assert_equals(event.data, null);
assert_equals(event.dataTransfer.getData('text/plain'), 'rich');
assert_regexp_match(event.dataTransfer.getData('text/html'), /<b.*>rich<\/b>$/);
}
actualEventLog.push(`${event.type}-${event.inputType || '[null]'}`);
}));
}
await selectTextCutAndPaste(text2, text2);
await resolveWhen(() => { return actualEventLog.length == expectedEventLog.length });
assert_array_equals(actualEventLog, expectedEventLog,
`Expected: ${expectedEventLog}; Actual: ${actualEventLog}.`);
}, 'Event order and dataTransfer on contenteditable.');
promise_test(async test => {
const prevent = document.getElementById('test3_editable_prevent');
const normal = document.getElementById('test3_editable_normal');
prevent.addEventListener('beforeinput', test.step_func(function() {
if (event.inputType === 'deleteByCut' ||
event.inputType === 'insertFromPaste') {
event.preventDefault();
}
}));
normal.addEventListener('input', test.step_func(function() {
if (event.inputType === 'insertFromPaste') {
assert_equals(prevent.textContent, 'prevent');
assert_equals(normal.textContent, 'prevent');
}
}));
await selectTextCommand(prevent, commands.PASTE);
await selectTextCutAndPaste(prevent, normal);
await resolveWhen(() => { return normal.textContent == 'prevent' });
assert_equals(prevent.textContent, 'prevent');
assert_equals(normal.textContent, 'prevent');
}, 'preventDefault() should prevent DOM modification but allow clipboard updates.');
</script>

View file

@ -1,2 +1,3 @@
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Document-Policy: js-profiling

View file

@ -0,0 +1,207 @@
<!DOCTYPE html>
<html>
<title>Test MediaSource addSourceBuffer overloads for WebCodecs Audio/VideoDecoderConfigs</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
setup(() => {
assert_implements(
SourceBuffer.prototype.hasOwnProperty('appendEncodedChunks'),
'SourceBuffer prototype hasOwnProperty "appendEncodedChunks", used ' +
'here to feature detect MSE-for-WebCodecs implementation.');
});
testInvalidArguments();
testValidArguments();
function getValidAudioConfig() {
// TODO(crbug.com/1144908): Consider confirming with WebCodecs'
// isConfigSupported() once that API is available.
return {
codec: 'opus',
sampleRate: 48000,
numberOfChannels: 2
};
}
function getValidVideoConfig() {
// TODO(crbug.com/1144908): Consider confirming with WebCodecs'
// isConfigSupported() once that API is available.
return { codec: 'vp09.00.10.08' };
}
function testInvalidArguments() {
const INVALID_CASES = [
{ arg: null,
name: 'null' },
{ arg: undefined,
name: 'undefined' },
{ arg: { },
name: '{ empty dictionary }' },
{
arg: {
audioConfig: getValidAudioConfig(),
videoConfig: getValidVideoConfig()
},
name: '{ valid audioConfig and videoConfig }',
},
{
arg: {
audioConfig: {
codec: 'bogus',
sampleRate: 48000,
numberOfChannels: 2
}
},
name: 'bad audio config codec',
},
{ arg: { videoConfig: { codec: 'bogus' } },
name: 'bad video config codec' },
{ arg: { audioConfig: { sampleRate: 48000, numberOfChannels: 2 } },
name: 'audio config missing required member "codec"' },
{ arg: { videoConfig: { } },
name: 'video config missing required member "codec"' },
];
[ 'closed', 'open', 'ended' ].forEach(readyStateScenario => {
INVALID_CASES.forEach(invalidCase => {
runAddSourceBufferTest(invalidCase['arg'] /* argument */,
false /* isValidArgument */,
invalidCase['name'] /* argumentDescription */,
readyStateScenario);
});
});
}
function testValidArguments() {
const VALID_CASES = [
{
arg: {
audioConfig: getValidAudioConfig()
},
name: 'valid audioConfig'
},
{
arg: {
videoConfig: getValidVideoConfig()
},
name: 'valid videoConfig'
},
];
[ 'closed', 'open', 'ended' ].forEach(readyStateScenario => {
VALID_CASES.forEach(validCase => {
runAddSourceBufferTest(
validCase['arg'] /* argument */,
true /* isValidArgument */,
validCase['name'] /* argumentDescription */,
readyStateScenario);
});
});
}
async function getClosedMediaSource(test) {
let mediaSource = new MediaSource();
assert_equals(mediaSource.readyState, 'closed');
return mediaSource;
}
async function getOpenMediaSource(test) {
return new Promise(async resolve => {
const v = document.createElement('video');
const mediaSource = new MediaSource();
const url = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', test.step_func(() => {
URL.revokeObjectURL(url);
assert_equals(mediaSource.readyState, 'open', 'MediaSource is open');
resolve(mediaSource);
}), { once: true });
v.src = url;
});
}
async function getEndedMediaSource(test) {
let mediaSource = await getOpenMediaSource(test);
assert_equals(mediaSource.readyState, 'open', 'MediaSource is open');
mediaSource.endOfStream();
assert_equals(mediaSource.readyState, 'ended', 'MediaSource is ended');
return mediaSource;
}
function runAddSourceBufferTest(argument, isValidArgument, argumentDescription, readyStateScenario) {
const testDescription = 'addSourceBuffer call with ' +
(isValidArgument ? 'valid' : 'invalid') +
' argument ' + argumentDescription + ' while MediaSource readyState is ' +
readyStateScenario;
switch(readyStateScenario) {
case 'closed':
promise_test(async t => {
let mediaSource = await getClosedMediaSource(t);
assert_equals(mediaSource.readyState, 'closed');
let sourceBuffer;
if (isValidArgument) {
assert_throws_dom('InvalidStateError',
() => { sourceBuffer = mediaSource.addSourceBuffer(argument); },
'addSourceBuffer(valid config) throws InvalidStateError if MediaSource is "closed"');
assert_equals(sourceBuffer, undefined,
'addSourceBuffer result for valid config while "closed" should be exception');
} else {
assert_throws_js(TypeError,
() => { sourceBuffer = mediaSource.addSourceBuffer(argument); },
'addSourceBuffer(invalid config) throws TypeError if MediaSource is "closed"');
assert_equals(sourceBuffer, undefined,
'addSourceBuffer result for invalid config while "closed" should be exception');
}
}, testDescription);
break;
case 'open':
promise_test(async t => {
let mediaSource = await getOpenMediaSource(t);
assert_equals(mediaSource.readyState, 'open', 'MediaSource is open');
let sourceBuffer;
if (isValidArgument) {
// TODO(crbug.com/1144908): Update to expect success once the impl is
// more complete.
assert_throws_dom('QuotaExceededError',
() => { sourceBuffer = mediaSource.addSourceBuffer(argument); },
'addSourceBuffer(valid config) throws QuotaExceededError');
assert_equals(sourceBuffer, undefined,
'addSourceBuffer result for valid config while "open" should be exception');
} else {
assert_throws_js(TypeError,
() => { sourceBuffer = mediaSource.addSourceBuffer(argument); },
'addSourceBuffer(invalid config) throws TypeError if MediaSource is "open"');
assert_equals(sourceBuffer, undefined,
'addSourceBufferResult for invalid config while "open" should be exception');
}
}, testDescription);
break;
case 'ended':
promise_test(async t => {
let mediaSource = await getEndedMediaSource(t);
let sourceBuffer;
if (isValidArgument) {
assert_throws_dom('InvalidStateError',
() => { sourceBuffer = mediaSource.addSourceBuffer(argument); },
'addSourceBuffer(valid config) throws InvalidStateError if MediaSource is "ended"');
assert_equals(sourceBuffer, undefined,
'addSourceBuffer result for valid config while "ended" should be exception');
} else {
assert_throws_js(TypeError,
() => { sourceBuffer = mediaSource.addSourceBuffer(argument); },
'addSourceBuffer(invalid config) throws TypeError if MediaSource is "ended"');
assert_equals(sourceBuffer, undefined,
'addSourceBuffer result for invalid config while "ended" should be exception');
}
}, testDescription);
break;
default:
assert_unreached('Invalid readyStateScenario ' + readyStateScenario);
break;
}
}
</script>
</html>

View file

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing - test that unsuccessful iframes create entries</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<body>
<script>
test(() => {
const entries = performance.getEntriesByType("resource");
assert_equals(entries.length, 3, "Precondition - Entries for blocking scripts fired");
}, "Precondition");
const run_test = async (t, url, csp) => {
const setPerformanceObserver = new Promise(resolve => {
const po = new PerformanceObserver(resolve);
po.observe({type: "resource"});
});
const timeout = new Promise(resolve => t.step_timeout(resolve, 1000));
const frame = document.createElement("iframe");
frame.src = url;
if (csp) {
frame.csp = csp;
}
document.body.appendChild(frame);
const list = await Promise.race([setPerformanceObserver, timeout]);
assert_equals(typeof(list), "object", "No iframe entry was fired");
const entries = list.getEntriesByName(url);
assert_equals(entries.length, 1);
}
const {REMOTE_ORIGIN, ORIGINAL_HOST, HTTPS_PORT} = get_host_info();
const nonexistent_url = "https://nonexistent." + ORIGINAL_HOST + ":" + HTTPS_PORT + "/";
promise_test(t => {
return run_test(t, nonexistent_url);
}, "Test iframe from non-existent host");
promise_test(t => {
const url = new URL("resources/fake_responses.py?redirect=" + nonexistent_url, location.href);
return run_test(t, url.toString());
}, "Test iframe redirecting to non-existent host");
const csp_directive = "default-src 'none'";
promise_test(t => {
const url = new URL("/resource-timing/resources/csp-default-none.html", location.href);
return run_test(t, url, csp_directive);
}, "Same-origin iframe that complies with CSP attribute gets reported");
promise_test(t => {
const url = new URL("/resource-timing/resources/green_frame.htm", location.href);
return run_test(t, url.toString(), csp_directive);
}, "Same-origin iframe that doesn't comply with CSP attribute gets reported");
promise_test(t => {
const url = new URL("/resource-timing/resources/csp-default-none.html", REMOTE_ORIGIN);
return run_test(t, url.toString(), csp_directive);
}, "Cross-origin iframe that complies with CSP attribute gets reported");
promise_test(t => {
const url = new URL("/resource-timing/resources/green_frame.htm", REMOTE_ORIGIN);
return run_test(t, url.toString(), csp_directive);
}, "Cross-origin iframe that doesn't comply with CSP attribute gets reported");
</script>
</body>
</html>

View file

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>empty page</title>

View file

@ -0,0 +1,2 @@
Content-Security-Policy: default-src 'none'

View file

@ -25,7 +25,7 @@ API_RATE_LIMIT_THRESHOLD = 0.2
LABEL = 'safe for preview'
# The number of seconds to wait between attempts to verify that a submission
# preview is available on the Pull Request preview server
POLLING_PERIOD = 5
POLLING_PERIOD = 15
# Pull Requests from authors with the following associations to the project
# should automatically receive previews
#
@ -44,7 +44,7 @@ logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def gh_request(method_name, url, body=None, media_type=None):
github_token = os.environ['DEPLOY_TOKEN']
github_token = os.environ['GITHUB_TOKEN']
kwargs = {
'headers': {
@ -67,6 +67,9 @@ def gh_request(method_name, url, body=None, media_type=None):
return resp.json()
class GitHubRateLimitException(Exception):
pass
def guard(resource):
'''Decorate a `Project` instance method which interacts with the GitHub
API, ensuring that the subsequent request will not deplete the relevant
@ -90,7 +93,7 @@ def guard(resource):
)
if limit and float(remaining) / limit < API_RATE_LIMIT_THRESHOLD:
raise Exception(
raise GitHubRateLimitException(
'Exiting to avoid GitHub.com API request throttling.'
)
@ -132,25 +135,6 @@ class Project(object):
return {pr['number']: pr for pr in data['items']}
def pull_request_is_from_fork(self, pull_request):
pr_number = pull_request['number']
logger.info('Checking if pull request %s is from a fork', pr_number)
# We may already have this information; if so no need to make another
# API call.
if 'head' not in pull_request:
pull_request = self.get_pull_request(pr_number)
repo_name = pull_request['head']['repo']['full_name']
is_fork = repo_name != self._github_project
logger.info(
'Pull request %s is from \'%s\'. Is a fork: %s',
pr_number, repo_name, is_fork
)
return is_fork
@guard('core')
def create_ref(self, refspec, revision):
url = '{}/repos/{}/git/refs'.format(self._host, self._github_project)
@ -206,7 +190,7 @@ class Project(object):
return deployments.pop() if len(deployments) else None
@guard('core')
def update_deployment(self, target, deployment, state, description=''):
def add_deployment_status(self, target, deployment, state, description=''):
if state in ('pending', 'success'):
pr_number = deployment['environment'][len(DEPLOYMENT_PREFIX):]
environment_url = '{}/{}'.format(target, pr_number)
@ -227,7 +211,7 @@ class Remote(object):
# The repository in the GitHub Actions environment is configured with
# a remote whose URL uses unauthenticated HTTPS, making it unsuitable
# for pushing changes.
self._token = os.environ['DEPLOY_TOKEN']
self._token = os.environ['GITHUB_TOKEN']
def _git(self, command):
return subprocess.check_output([
@ -306,131 +290,130 @@ def should_be_mirrored(project, pull_request):
pull_request['user']['login'] not in AUTOMATION_GITHUB_USERS and
pull_request['author_association'] in TRUSTED_AUTHOR_ASSOCIATIONS
)
) and
# Query this last as it requires another API call to verify
not project.pull_request_is_from_fork(pull_request)
)
)
def is_deployed(host, deployment):
worktree_name = deployment['environment'][len(DEPLOYMENT_PREFIX):]
response = requests.get(
'{}/.git/worktrees/{}/HEAD'.format(host, worktree_name)
)
url = '{}/.git/worktrees/{}/HEAD'.format(host, worktree_name)
logger.info('Issuing request: GET %s', url)
response = requests.get(url)
logger.info('Response status code: %s', response.status_code)
if response.status_code != 200:
return False
logger.info('Response text: %s', response.text.strip())
return response.text.strip() == deployment['sha']
def synchronize(host, github_project, window):
'''Inspect all Pull Requests which have been modified in a given window of
time. Add or remove the "preview" label and update or delete the relevant
git refs according to the status of each Pull Request.'''
def update_mirror_refs(project, remote, pull_request):
'''Update the WPT refs that control mirroring of this pull request.
project = Project(host, github_project)
remote = Remote(github_project)
Two sets of refs are used to control wptpr.live's mirroring of pull
requests:
pull_requests = project.get_pull_requests(
time.gmtime(time.time() - window)
1. refs/prs-trusted-for-preview/{number}
2. refs/prs-open/{number}
wptpr.live will only mirror a pull request if both exist for the given pull
request number; otherwise the pull request is either not open or is not
trustworthy (e.g. came from someone who doesn't have push access anyway.)
This method returns the revision that is being mirrored, or None if the
pull request should not be mirrored.
'''
refspec_trusted = 'prs-trusted-for-preview/{number}'.format(
**pull_request
)
refspec_open = 'prs-open/{number}'.format(**pull_request)
revision_latest = remote.get_revision(
'pull/{number}/head'.format(**pull_request)
)
revision_trusted = remote.get_revision(refspec_trusted)
revision_open = remote.get_revision(refspec_open)
# It is possible we may miss some pull requests if this script breaks or is
# not run for a while and the PR falls outside the checked window. To
# ensure that closed pull requests are deleted, extend the list of pull
# requests to look at with any that have an existing refs/prs-open/{pr} ref
# in the repo.
existing_pr_numbers = remote.get_pull_requests_with_open_ref()
for pr_number in existing_pr_numbers:
if pr_number in pull_requests:
continue
pull_request = project.get_pull_request(pr_number)
pull_requests[pull_request['number']] = pull_request
if should_be_mirrored(project, pull_request):
logger.info('Pull Request should be mirrored')
for pull_request in pull_requests.values():
logger.info('Processing Pull Request #%(number)d', pull_request)
if revision_trusted is None:
project.create_ref(refspec_trusted, revision_latest)
elif revision_trusted != revision_latest:
project.update_ref(refspec_trusted, revision_latest)
refspec_trusted = 'prs-trusted-for-preview/{number}'.format(
**pull_request
)
refspec_open = 'prs-open/{number}'.format(**pull_request)
revision_latest = remote.get_revision(
'pull/{number}/head'.format(**pull_request)
)
revision_trusted = remote.get_revision(refspec_trusted)
revision_open = remote.get_revision(refspec_open)
if revision_open is None:
project.create_ref(refspec_open, revision_latest)
elif revision_open != revision_latest:
project.update_ref(refspec_open, revision_latest)
if should_be_mirrored(project, pull_request):
logger.info('Pull Request should be mirrored')
return revision_latest
if revision_trusted is None:
project.create_ref(refspec_trusted, revision_latest)
elif revision_trusted != revision_latest:
project.update_ref(refspec_trusted, revision_latest)
logger.info('Pull Request should not be mirrored')
if revision_open is None:
project.create_ref(refspec_open, revision_latest)
elif revision_open != revision_latest:
project.update_ref(refspec_open, revision_latest)
if not has_mirroring_label(pull_request) and revision_trusted is not None:
remote.delete_ref(refspec_trusted)
if project.get_deployment(revision_latest) is None:
project.create_deployment(
pull_request, revision_latest
)
else:
logger.info('Pull Request should not be mirrored')
if revision_open is not None and not is_open(pull_request):
remote.delete_ref(refspec_open)
if not has_mirroring_label(pull_request) and revision_trusted is not None:
remote.delete_ref(refspec_trusted)
# No revision to be deployed to wptpr.live
return None
if revision_open is not None and not is_open(pull_request):
remote.delete_ref(refspec_open)
class DeploymentFailedException(Exception):
pass
def detect(host, github_project, target, timeout):
'''Manage the status of a GitHub Deployment by polling the Pull Request
preview website until the Deployment is complete or a timeout is
reached.'''
def deploy(project, target, pull_request, revision, timeout):
'''Create a GitHub deployment for the given pull request and revision.
project = Project(host, github_project)
with open(os.environ['GITHUB_EVENT_PATH']) as handle:
data = json.loads(handle.read())
logger.info('Event data: %s', json.dumps(data, indent=2))
deployment = data['deployment']
if not deployment['environment'].startswith(DEPLOYMENT_PREFIX):
logger.info(
'Deployment environment "%s" is unrecognized. Exiting.',
deployment['environment']
)
This method creates a pending GitHub deployment, waits for the
corresponding revision to be available on wptpr.live and marks the
deployment as successful. If the revision does not appear in the given
timeout, the deployment is marked as errored instead.'''
if project.get_deployment(revision) is not None:
return
deployment = project.create_deployment(pull_request, revision)
message = 'Waiting up to {} seconds for Deployment {} to be available on {}'.format(
timeout, deployment['environment'], target
)
logger.info(message)
project.update_deployment(target, deployment, 'pending', message)
project.add_deployment_status(target, deployment, 'pending', message)
start = time.time()
while not is_deployed(target, deployment):
if time.time() - start > timeout:
message = 'Deployment did not become available after {} seconds'.format(timeout)
project.update_deployment(target, deployment, 'error', message)
raise Exception(message)
project.add_deployment_status(target, deployment, 'error', message)
raise DeploymentFailedException(message)
time.sleep(POLLING_PERIOD)
result = project.update_deployment(target, deployment, 'success')
result = project.add_deployment_status(target, deployment, 'success')
logger.info(json.dumps(result, indent=2))
def main(host, github_project, target, timeout):
project = Project(host, github_project)
remote = Remote(github_project)
with open(os.environ['GITHUB_EVENT_PATH']) as handle:
data = json.load(handle)
logger.info('Event data: %s', json.dumps(data, indent=2))
pull_request = data['pull_request']
logger.info('Processing Pull Request #%(number)d', pull_request)
revision_to_mirror = update_mirror_refs(project, remote, pull_request)
if revision_to_mirror:
deploy(project, target, pull_request, revision_to_mirror, timeout)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='''Synchronize the state of a GitHub.com project with the
underlying git repository in order to support a externally-hosted
Pull Request preview system. Communicate the state of that system
via GitHub Deployments associated with each Pull Request.'''
description='''Mirror a pull request to an externally-hosted preview
system, and create a GitHub Deployment associated with the pull
request pointing at the preview.'''
)
parser.add_argument(
'--host', required=True, help='the location of the GitHub API server'
@ -441,36 +424,19 @@ if __name__ == '__main__':
help='''the GitHub organization and GitHub project name, separated by
a forward slash (e.g. "web-platform-tests/wpt")'''
)
subparsers = parser.add_subparsers(title='subcommands')
parser_sync = subparsers.add_parser(
'synchronize', help=synchronize.__doc__
)
parser_sync.add_argument(
'--window',
type=int,
required=True,
help='''the number of seconds prior to the current moment within which
to search for GitHub Pull Requests. Any Pull Requests updated in
this time frame will be considered for synchronization.'''
)
parser_sync.set_defaults(func=synchronize)
parser_detect = subparsers.add_parser('detect', help=detect.__doc__)
parser_detect.add_argument(
parser.add_argument(
'--target',
required=True,
help='''the URL of the website to which submission previews are
expected to become available'''
)
parser_detect.add_argument(
parser.add_argument(
'--timeout',
type=int,
required=True,
help='''the number of seconds to wait for a submission preview to
become available before reporting a GitHub Deployment failure'''
)
parser_detect.set_defaults(func=detect)
values = dict(vars(parser.parse_args()))
values.pop('func')(**values)
main(**values)

View file

@ -14,10 +14,11 @@ import sys
import tempfile
import threading
subject = os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', 'pr_preview.py'
)
test_host = 'localhost'
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import pr_preview
TEST_HOST = 'localhost'
def same_members(a, b):
@ -81,7 +82,10 @@ class MockHandler(BaseHTTPRequestHandler, object):
self.server.actual_traffic.append((request, response))
self.send_response(response[0])
self.end_headers()
self.wfile.write(json.dumps(response[1]).encode('utf-8'))
if self.server.reponse_body_is_json:
self.wfile.write(json.dumps(response[1]).encode('utf-8'))
else:
self.wfile.write(response[1].encode('utf-8'))
def do_DELETE(self):
return self.do_all()
@ -100,10 +104,11 @@ class MockServer(HTTPServer, object):
'''HTTP server that responds to all requests with status code 200 and body
'{}' unless an alternative status code and body are specified for the given
method and path in the `responses` parameter.'''
def __init__(self, address, expected_traffic):
def __init__(self, address, expected_traffic, reponse_body_is_json=True):
super(MockServer, self).__init__(address, MockHandler)
self.expected_traffic = expected_traffic
self.actual_traffic = []
self.reponse_body_is_json = reponse_body_is_json
def __enter__(self):
threading.Thread(target=lambda: self.serve_forever()).start()
@ -115,21 +120,19 @@ class MockServer(HTTPServer, object):
class Requests(object):
get_rate = ('GET', '/rate_limit', {})
search = ('GET', '/search/issues', {})
pr_details = ('GET', '/repos/test-org/test-repo/pulls/23', {})
ref_create_open = (
'POST', '/repos/test-org/test-repo/git/refs', {'ref':'refs/prs-open/23'}
'POST', '/repos/test-org/test-repo/git/refs', {'ref':'refs/prs-open/45'}
)
ref_create_trusted = (
'POST',
'/repos/test-org/test-repo/git/refs',
{'ref':'refs/prs-trusted-for-preview/23'}
{'ref':'refs/prs-trusted-for-preview/45'}
)
ref_update_open = (
'PATCH', '/repos/test-org/test-repo/git/refs/prs-open/23', {}
'PATCH', '/repos/test-org/test-repo/git/refs/prs-open/45', {}
)
ref_update_trusted = (
'PATCH', '/repos/test-org/test-repo/git/refs/prs-trusted-for-preview/23', {}
'PATCH', '/repos/test-org/test-repo/git/refs/prs-trusted-for-preview/45', {}
)
deployment_get = ('GET', '/repos/test-org/test-repo/deployments', {})
deployment_create = ('POST', '/repos/test-org/test-repo/deployments', {})
@ -167,9 +170,13 @@ class Responses(object):
@contextlib.contextmanager
def temp_repo():
def temp_repo(change_dir=False):
original_dir = os.getcwd()
directory = tempfile.mkdtemp()
if change_dir:
os.chdir(directory)
try:
subprocess.check_call(['git', 'init'], cwd=directory)
# Explicitly create the default branch.
@ -192,20 +199,22 @@ def temp_repo():
yield directory
finally:
if change_dir:
os.chdir(original_dir)
shutil.rmtree(
directory, ignore_errors=False, onerror=handle_remove_readonly
)
def synchronize(expected_traffic, refs={}):
env = {
'DEPLOY_TOKEN': 'c0ffee'
}
env.update(os.environ)
server = MockServer((test_host, 0), expected_traffic)
test_port = server.server_address[1]
def update_mirror_refs(pull_request, expected_traffic, refs={}):
os.environ['GITHUB_TOKEN'] = 'c0ffee'
github_server = MockServer((TEST_HOST, 0), expected_traffic)
github_port = github_server.server_address[1]
remote_refs = {}
with temp_repo() as local_repo, temp_repo() as remote_repo, server:
method_threw = False
with temp_repo(change_dir=True), temp_repo() as remote_repo, github_server:
subprocess.check_call(
['git', 'commit', '--allow-empty', '-m', 'first'],
cwd=remote_repo
@ -214,9 +223,7 @@ def synchronize(expected_traffic, refs={}):
['git', 'commit', '--allow-empty', '-m', 'second'],
cwd=remote_repo
)
subprocess.check_call(
['git', 'remote', 'add', 'origin', remote_repo], cwd=local_repo
)
subprocess.check_call(['git', 'remote', 'add', 'origin', remote_repo])
for name, value in refs.items():
subprocess.check_call(
@ -224,26 +231,18 @@ def synchronize(expected_traffic, refs={}):
cwd=remote_repo
)
child = subprocess.Popen(
[
sys.executable,
subject,
'--host',
'http://{}:{}'.format(test_host, test_port),
'--github-project',
'test-org/test-repo',
'synchronize',
'--window',
'3000'
],
cwd=local_repo,
env=env
subprocess.check_call(['git', 'remote', '-v'])
project = pr_preview.Project(
'http://{}:{}'.format(TEST_HOST, github_port),
'test-org/test-repo',
)
remote = pr_preview.Remote('test-org/test-repo')
try:
pr_preview.update_mirror_refs(project, remote, pull_request)
except pr_preview.GitHubRateLimitException:
method_threw = True
child.communicate()
lines = subprocess.check_output(
['git', 'ls-remote', 'origin'], cwd=local_repo
)
lines = subprocess.check_output(['git', 'ls-remote', 'origin'])
for line in lines.decode('utf-8').strip().split('\n'):
revision, ref = line.split()
@ -252,75 +251,55 @@ def synchronize(expected_traffic, refs={}):
remote_refs[ref] = revision
return child.returncode, server.actual_traffic, remote_refs
return (
method_threw,
github_server.actual_traffic,
remote_refs,
)
def detect(event, expected_github_traffic, expected_preview_traffic):
env = {
'DEPLOY_TOKEN': 'c0ffee'
}
env.update(os.environ)
github_server = MockServer((test_host, 0), expected_github_traffic)
def deploy(pr_num, revision, expected_github_traffic, expected_preview_traffic):
os.environ['GITHUB_TOKEN'] = 'c0ffee'
github_server = MockServer((TEST_HOST, 0), expected_github_traffic)
github_port = github_server.server_address[1]
preview_server = MockServer((test_host, 0), expected_preview_traffic)
preview_server = MockServer((TEST_HOST, 0), expected_preview_traffic, reponse_body_is_json=False)
preview_port = preview_server.server_address[1]
with temp_repo() as repo, github_server, preview_server:
env['GITHUB_EVENT_PATH'] = repo + '/event.json'
with open(env['GITHUB_EVENT_PATH'], 'w') as handle:
handle.write(json.dumps(event))
child = subprocess.Popen(
[
sys.executable,
subject,
'--host',
'http://{}:{}'.format(test_host, github_port),
'--github-project',
'test-org/test-repo',
'detect',
'--target',
'http://{}:{}'.format(test_host, preview_port),
'--timeout',
'1'
],
cwd=repo,
env=env
method_threw = False
with github_server, preview_server:
project = pr_preview.Project(
'http://{}:{}'.format(TEST_HOST, github_port),
'test-org/test-repo',
)
child.communicate()
target = 'http://{}:{}'.format(TEST_HOST, preview_port)
pull_request = {'number': pr_num}
timeout = 1
try:
pr_preview.deploy(project, target, pull_request, revision, timeout)
except (pr_preview.GitHubRateLimitException, pr_preview.DeploymentFailedException):
method_threw = True
return (
child.returncode,
method_threw,
github_server.actual_traffic,
preview_server.actual_traffic
)
def test_synchronize_zero_results():
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [],
'incomplete_results': False
}
))
]
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
assert returncode == 0
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_fail_search_throttled():
def test_update_mirror_refs_fail_rate_limited():
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'stephenmcgruer'},
'author_association': 'COLLABORATOR',
'closed_at': None,
}
expected_traffic = [
(Requests.get_rate, (
200,
{
'resources': {
'search': {
'core': {
'remaining': 1,
'limit': 10
}
@ -329,451 +308,213 @@ def test_synchronize_fail_search_throttled():
))
]
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic
)
assert returncode != 0
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_fail_incomplete_results():
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [],
'incomplete_results': True
}
))
]
returncode, actual_traffic, remove_refs = synchronize(expected_traffic)
assert returncode != 0
assert method_threw
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_ignore_closed():
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [
{
'number': 23,
'labels': [],
'closed_at': '2019-10-28',
'user': {'login': 'grace'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
))
]
# No existing refs, but a closed PR event comes in. Nothing should happen.
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'stephenmcgruer'},
'author_association': 'COLLABORATOR',
'closed_at': '2019-10-28',
}
expected_traffic = []
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic
)
assert returncode == 0
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_sync_collaborator():
def test_update_mirror_refs_collaborator():
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'stephenmcgruer'},
'author_association': 'COLLABORATOR',
'closed_at': None,
}
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [
{
'number': 23,
'labels': [],
'closed_at': None,
'user': {'login': 'grace'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
)),
(Requests.pr_details, (200,
{
'head': {
'repo': {
'full_name': 'test-org/test-repo'
}
}
}
)),
(Requests.ref_create_open, (200, {})),
(Requests.ref_create_trusted, (200, {})),
(Requests.deployment_get, (200, {})),
(Requests.deployment_create, (200, {}))
]
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic
)
assert returncode == 0
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_ignore_collaborator_bot():
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [
{
'number': 23,
'labels': [],
'closed_at': None,
'user': {'login': 'chromium-wpt-export-bot'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
))
]
def test_update_mirror_refs_ignore_collaborator_bot():
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'chromium-wpt-export-bot'},
'author_association': 'COLLABORATOR',
'closed_at': None,
}
expected_traffic = []
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic
)
assert returncode == 0
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_ignore_untrusted_contributor():
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [
{
'number': 23,
'labels': [],
'closed_at': None,
'user': {'login': 'grace'},
'author_association': 'CONTRIBUTOR'
}
],
'incomplete_results': False
}
))
]
def test_update_mirror_refs_ignore_untrusted_contributor():
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'stephenmcgruer'},
'author_association': 'CONTRIBUTOR',
'closed_at': None,
}
expected_traffic = []
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic
)
assert returncode == 0
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_ignore_pull_request_from_fork():
def test_update_mirror_refs_trusted_contributor():
pull_request = {
'number': 45,
# user here is a contributor (untrusted), but the issue
# has been labelled as safe.
'labels': [{'name': 'safe for preview'}],
'user': {'login': 'Hexcles'},
'author_association': 'CONTRIBUTOR',
'closed_at': None,
}
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [
{
'number': 23,
'labels': [],
'closed_at': None,
'user': {'login': 'grace'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
)),
(Requests.pr_details, (200,
{
'head': {
'repo': {
'full_name': 'some-other-org/test-repo'
}
}
}
)),
]
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
assert returncode == 0
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_sync_trusted_contributor():
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [
{
'number': 23,
# user here is a contributor (untrusted), but the issue
# has been labelled as safe.
'labels': [{'name': 'safe for preview'}],
'closed_at': None,
'user': {'login': 'Hexcles'},
'author_association': 'CONTRIBUTOR'
}
],
'incomplete_results': False
}
)),
(Requests.pr_details, (200,
{
'head': {
'repo': {
'full_name': 'test-org/test-repo'
}
}
}
)),
(Requests.ref_create_open, (200, {})),
(Requests.ref_create_trusted, (200, {})),
(Requests.deployment_get, (200, [])),
(Requests.deployment_create, (200, {}))
]
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic
)
assert returncode == 0
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_sync_bot_with_label():
pull_request = {
'number': 45,
# user here is a bot which is normally not mirrored,
# but the issue has been labelled as safe.
'labels': [{'name': 'safe for preview'}],
'user': {'login': 'chromium-wpt-export-bot'},
'author_association': 'COLLABORATOR',
'closed_at': None,
}
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.search, (
200,
{
'items': [
{
'number': 23,
# user here is a bot which is normally not mirrored,
# but the issue has been labelled as safe.
'labels': [{'name': 'safe for preview'}],
'closed_at': None,
'user': {'login': 'chromium-wpt-export-bot'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
)),
(Requests.pr_details, (200,
{
'head': {
'repo': {
'full_name': 'test-org/test-repo'
}
}
}
)),
(Requests.ref_create_open, (200, {})),
(Requests.ref_create_trusted, (200, {})),
(Requests.deployment_get, (200, [])),
(Requests.deployment_create, (200, {}))
]
returncode, actual_traffic, remote_refs = synchronize(expected_traffic)
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic
)
assert returncode == 0
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_update_collaborator():
def test_update_mirror_refs_update_collaborator():
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'stephenmcgruer'},
'author_association': 'COLLABORATOR',
'closed_at': None,
}
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.search, (200,
{
'items': [
{
'number': 23,
'labels': [],
'closed_at': None,
'user': {'login': 'grace'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
)),
(Requests.pr_details, (200,
{
'head': {
'repo': {
'full_name': 'test-org/test-repo'
}
}
}
)),
(Requests.deployment_get, (200, [])),
(Requests.ref_update_open, (200, {})),
(Requests.ref_update_trusted, (200, {})),
(Requests.deployment_create, (200, {}))
]
refs = {
'refs/pull/23/head': 'HEAD',
'refs/prs-open/23': 'HEAD~',
'refs/prs-trusted-for-preview/23': 'HEAD~'
'refs/pull/45/head': 'HEAD',
'refs/prs-open/45': 'HEAD~',
'refs/prs-trusted-for-preview/45': 'HEAD~'
}
returncode, actual_traffic, remote_refs = synchronize(expected_traffic, refs)
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic, refs
)
assert returncode == 0
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_update_member():
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'jgraham'},
'author_association': 'MEMBER',
'closed_at': None,
}
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.search, (200,
{
'items': [
{
'number': 23,
'labels': [],
'closed_at': None,
'user': {'login': 'grace'},
'author_association': 'MEMBER'
}
],
'incomplete_results': False
}
)),
(Requests.pr_details, (200,
{
'head': {
'repo': {
'full_name': 'test-org/test-repo'
}
}
}
)),
(Requests.deployment_get, (200, [{'some': 'deployment'}])),
(Requests.ref_update_open, (200, {})),
(Requests.ref_update_trusted, (200, {}))
]
refs = {
'refs/pull/23/head': 'HEAD',
'refs/prs-open/23': 'HEAD~',
'refs/prs-trusted-for-preview/23': 'HEAD~'
'refs/pull/45/head': 'HEAD',
'refs/prs-open/45': 'HEAD~',
'refs/prs-trusted-for-preview/45': 'HEAD~'
}
returncode, actual_traffic, remote_refs = synchronize(expected_traffic, refs)
assert returncode == 0
assert same_members(expected_traffic, actual_traffic)
def test_synchronize_delete_collaborator():
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.search, (200,
{
'items': [
{
'number': 23,
'labels': [],
'closed_at': '2019-10-30',
'user': {'login': 'grace'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
))
]
refs = {
'refs/pull/23/head': 'HEAD',
'refs/prs-open/23': 'HEAD~',
'refs/prs-trusted-for-preview/23': 'HEAD~'
}
returncode, actual_traffic, remote_refs = synchronize(expected_traffic, refs)
assert returncode == 0
assert same_members(expected_traffic, actual_traffic)
assert list(remote_refs) == ['refs/pull/23/head']
def test_synchronize_delete_old_pr():
# No pull requests from the search, but one outstanding closed PR.
expected_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.get_rate, Responses.no_limit),
(Requests.search, (200,
{
'items': [],
'incomplete_results': False
}
)),
(Requests.pr_details, (200,
{
'number': 23,
'labels': [],
'closed_at': '2019-10-28',
'head': {
'repo': {
'full_name': 'test-org/test-repo'
}
}
}
)),
]
refs = {
'refs/pull/23/head': 'HEAD',
'refs/prs-open/23': 'HEAD~',
'refs/prs-trusted-for-preview/23': 'HEAD~'
}
returncode, actual_traffic, remote_refs = synchronize(expected_traffic, refs)
assert returncode == 0
assert same_members(expected_traffic, actual_traffic)
assert list(remote_refs) == ['refs/pull/23/head']
def test_detect_ignore_unknown_env():
expected_github_traffic = []
expected_preview_traffic = []
event = {
'deployment': {
'id': 24601,
'environment': 'ghosts',
'sha': '3232'
}
}
returncode, actual_github_traffic, actual_preview_traffic = detect(
event, expected_github_traffic, expected_preview_traffic
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic, refs
)
assert returncode == 0
assert len(actual_github_traffic) == 0
assert len(actual_preview_traffic) == 0
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
def test_detect_fail_search_throttled():
def test_update_mirror_refs_delete_collaborator():
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'stephenmcgruer'},
'author_association': 'COLLABORATOR',
'closed_at': 2019-10-30,
}
expected_traffic = []
refs = {
'refs/pull/45/head': 'HEAD',
'refs/prs-open/45': 'HEAD~',
'refs/prs-trusted-for-preview/45': 'HEAD~'
}
method_threw, actual_traffic, remote_refs = update_mirror_refs(
pull_request, expected_traffic, refs
)
assert not method_threw
assert same_members(expected_traffic, actual_traffic)
assert list(remote_refs) == ['refs/pull/45/head']
def test_deploy_fail_rate_limited():
expected_github_traffic = [
(Requests.get_rate, (
200,
@ -788,99 +529,107 @@ def test_detect_fail_search_throttled():
))
]
expected_preview_traffic = []
event = {
'deployment': {
'id': 24601,
'environment': 'wpt-preview-45',
'sha': '3232'
}
}
returncode, actual_github_traffic, actual_preview_traffic = detect(
event, expected_github_traffic, expected_preview_traffic
pr_num = 45
revision = "abcdef123"
method_threw, actual_github_traffic, actual_preview_traffic = deploy(
pr_num, revision, expected_github_traffic, expected_preview_traffic
)
assert returncode == 1
assert method_threw
assert actual_github_traffic == expected_github_traffic
assert actual_preview_traffic == expected_preview_traffic
def test_detect_success():
def test_deploy_success():
pr_num = 45
revision = 'abcdef123'
expected_github_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_get, (200, [])),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_create, (200, {
'id': 24601,
'sha': revision,
'environment': 'wpt-preview-45',
})),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_pending, (200, {})),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_success, (200, {}))
]
expected_preview_traffic = [
(Requests.preview, (200, 3232))
(Requests.preview, (200, revision))
]
event = {
'deployment': {
'id': 24601,
'environment': 'wpt-preview-45',
'sha': '3232'
}
}
returncode, actual_github_traffic, actual_preview_traffic = detect(
event, expected_github_traffic, expected_preview_traffic
method_threw, actual_github_traffic, actual_preview_traffic = deploy(
pr_num, revision, expected_github_traffic, expected_preview_traffic
)
assert returncode == 0
assert not method_threw
assert actual_github_traffic == expected_github_traffic
assert actual_preview_traffic == expected_preview_traffic
def test_detect_timeout_missing():
def test_deploy_timeout_missing():
pr_num = 45
revision = 'abcdef123'
expected_github_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_get, (200, [])),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_create, (200, {
'id': 24601,
'sha': revision,
'environment': 'wpt-preview-45',
})),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_pending, (200, {})),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_error, (200, {}))
]
expected_preview_traffic = [
(Requests.preview, (404, {}))
(Requests.preview, (404, ""))
]
event = {
'deployment': {
'id': 24601,
'environment': 'wpt-preview-45',
'sha': '3232'
}
}
returncode, actual_github_traffic, actual_preview_traffic = detect(
event, expected_github_traffic, expected_preview_traffic
method_threw, actual_github_traffic, actual_preview_traffic = deploy(
pr_num, revision, expected_github_traffic, expected_preview_traffic
)
assert returncode == 1
assert method_threw
assert expected_github_traffic == actual_github_traffic
ping_count = len(actual_preview_traffic)
assert ping_count > 0
assert actual_preview_traffic == expected_preview_traffic * ping_count
def test_detect_timeout_wrong_revision():
def test_deploy_timeout_wrong_revision():
pr_num = 45
revision = 'abcdef123'
expected_github_traffic = [
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_get, (200, [])),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_create, (200, {
'id': 24601,
'sha': revision,
'environment': 'wpt-preview-45',
})),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_pending, (200, {})),
(Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_error, (200, {}))
]
expected_preview_traffic = [
(Requests.preview, (200, 1234))
# wptpr.live has the wrong revision deployed
(Requests.preview, (200, 'ghijkl456'))
]
event = {
'deployment': {
'id': 24601,
'environment': 'wpt-preview-45',
'sha': '3232'
}
}
returncode, actual_github_traffic, actual_preview_traffic = detect(
event, expected_github_traffic, expected_preview_traffic
method_threw, actual_github_traffic, actual_preview_traffic = deploy(
pr_num, revision, expected_github_traffic, expected_preview_traffic
)
assert returncode == 1
assert method_threw
assert expected_github_traffic == actual_github_traffic
ping_count = len(actual_preview_traffic)
assert ping_count > 0

View file

@ -54,13 +54,12 @@
});
}, "`window.setInterval(null)` throws.");
let policy = window.trustedTypes.createPolicy("default", { createScript: x => "0" });
// After default policy creation string assignment implicitly calls createScript.
test(t => {
let policy = window.trustedTypes.createPolicy("default", { createScript: createScriptJS }, true);
setTimeout(INPUTS.SCRIPT);
setInterval(INPUTS.SCRIPT);
}, "`setTimeout(string)`, `setInterval(string)` via default policy (successful Script transformation).");
// After default policy creation null assignment implicitly calls createScript.
test(t => {
setTimeout(null);

View file

@ -1,88 +0,0 @@
// META: global=window,worker
// META: script=resources/utils.js
// This file attempts to test the different ways you can pass input values
// to be matched; e.g. as strings vs structured component parts.
test(() => {
runTest({ pathname: '/foo/bar' }, [
{ input: "https://example.com/foo/bar", expected: true },
{ input: "https://example.com/foo/bar/baz", expected: false },
]);
}, "init single component, input string");
test(() => {
runTest({ pathname: '/foo/bar' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: false },
]);
}, "init single component, input single component");
test(() => {
runTest({ pathname: '/foo/bar' }, [
{ input: { hostname: 'example.com', pathname: '/foo/bar' },
expected: true },
{ input: { hostname: 'example.com', pathname: '/foo/bar/baz' },
expected: false },
]);
}, "init single component, input two components");
test(() => {
runTest({ pathname: '/foo/bar' }, [
{ input: { pathname: '/foo/bar', baseURL: 'https://example.com' },
expected: true },
{ input: { pathname: '/foo/bar/baz', baseURL: 'https://example.com' },
expected: false },
]);
}, "init single component, input baseURL and single component");
test(() => {
runTest({ pathname: '/foo/bar', baseURL: 'https://example.com?query#hash' }, [
{ input: "https://example.com/foo/bar", expected: true },
{ input: "https://example.com/foo/bar/baz", expected: false },
{ input: "https://example2.com/foo/bar", expected: false },
{ input: "http://example.com/foo/bar", expected: false },
]);
}, "init baseURL and single component, input string");
test(() => {
runTest({ pathname: '/foo/bar', baseURL: 'https://example.com?query#hash' }, [
{ input: { pathname: '/foo/bar' }, expected: false },
{ input: { pathname: '/foo/bar/baz' }, expected: false },
]);
}, "init baseURL and single component, input single component");
test(() => {
runTest({ pathname: '/foo/bar', baseURL: 'https://example.com?query#hash' }, [
{ input: { hostname: 'example.com', pathname: '/foo/bar' },
expected: false },
{ input: { hostname: 'example.com', pathname: '/foo/bar/baz' },
expected: false },
{ input: { hostname: 'example2.com', pathname: '/foo/bar' },
expected: false },
]);
}, "init baseURL and single component, input two components");
test(() => {
runTest({ pathname: '/foo/bar', baseURL: 'https://example.com?query#hash' }, [
{ input: { protocol: 'https', hostname: 'example.com',
pathname: '/foo/bar' },
expected: true },
{ input: { protocol: 'https', hostname: 'example.com',
pathname: '/foo/bar/baz' },
expected: false },
]);
}, "init single component, input three components");
test(() => {
runTest({ pathname: '/foo/bar', baseURL: 'https://example.com?query#hash' }, [
{ input: { pathname: '/foo/bar', baseURL: 'https://example.com' },
expected: true },
{ input: { pathname: '/foo/bar/baz', baseURL: 'https://example.com' },
expected: false },
{ input: { pathname: '/foo/bar', baseURL: 'https://example2.com' },
expected: false },
{ input: { pathname: '/foo/bar', baseURL: 'http://example.com' },
expected: false },
]);
}, "init baseURL and single component, input baseURL and single component");

View file

@ -1,139 +0,0 @@
// META: global=window,worker
// META: script=resources/utils.js
test(() => {
runTest({ pathname: '/foo/bar' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/ba' }, expected: false },
{ input: { pathname: '/foo/bar/' }, expected: false },
{ input: { pathname: '/foo/bar/baz' }, expected: false },
]);
}, "fixed string");
test(() => {
runTest({ pathname: '/foo/:bar' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/index.html' }, expected: true },
{ input: { pathname: '/foo/bar/' }, expected: false },
{ input: { pathname: '/foo/' }, expected: false },
]);
}, "named group");
test(() => {
runTest({ pathname: '/foo/(.*)' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: true },
{ input: { pathname: '/foo/' }, expected: true },
{ input: { pathname: '/foo' }, expected: false },
]);
}, "regexp group");
test(() => {
runTest({ pathname: '/foo/:bar(.*)' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: true },
{ input: { pathname: '/foo/' }, expected: true },
{ input: { pathname: '/foo' }, expected: false },
]);
}, "named regexp group");
test(() => {
runTest({ pathname: '/foo/:bar?' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo' }, expected: true },
{ input: { pathname: '/foo/' }, expected: false },
{ input: { pathname: '/foobar' }, expected: false },
{ input: { pathname: '/foo/bar/baz' }, expected: false },
]);
}, "optional named group");
test(() => {
runTest({ pathname: '/foo/:bar+' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: true },
{ input: { pathname: '/foo' }, expected: false },
{ input: { pathname: '/foo/' }, expected: false },
{ input: { pathname: '/foobar' }, expected: false },
]);
}, "repeated named group");
test(() => {
runTest({ pathname: '/foo/:bar*' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: true },
{ input: { pathname: '/foo' }, expected: true },
{ input: { pathname: '/foo/' }, expected: false },
{ input: { pathname: '/foobar' }, expected: false },
]);
}, "optional repeated named group");
test(() => {
runTest({ pathname: '/foo/(.*)?' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: true },
{ input: { pathname: '/foo/' }, expected: true },
{ input: { pathname: '/foo' }, expected: true },
{ input: { pathname: '/fo' }, expected: false },
{ input: { pathname: '/foobar' }, expected: false },
]);
}, "optional regexp group");
test(() => {
runTest({ pathname: '/foo/(.*)+' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: true },
{ input: { pathname: '/foo/' }, expected: true },
{ input: { pathname: '/foo' }, expected: false },
{ input: { pathname: '/fo' }, expected: false },
{ input: { pathname: '/foobar' }, expected: false },
]);
}, "repeated regexp group");
test(() => {
runTest({ pathname: '/foo/(.*)*' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: true },
{ input: { pathname: '/foo/' }, expected: true },
{ input: { pathname: '/foo' }, expected: true },
{ input: { pathname: '/fo' }, expected: false },
{ input: { pathname: '/foobar' }, expected: false },
]);
}, "optional repeated regexp group");
test(() => {
runTest({ pathname: '/foo{/bar}' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: false },
{ input: { pathname: '/foo/' }, expected: false },
{ input: { pathname: '/foo' }, expected: false },
]);
}, "group");
test(() => {
runTest({ pathname: '/foo{/bar}?' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: false },
{ input: { pathname: '/foo' }, expected: true },
{ input: { pathname: '/foo/' }, expected: false },
]);
}, "optional group");
test(() => {
runTest({ pathname: '/foo{/bar}+' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: false },
{ input: { pathname: '/foo' }, expected: false },
{ input: { pathname: '/foo/' }, expected: false },
]);
}, "repeated group");
test(() => {
runTest({ pathname: '/foo{/bar}*' }, [
{ input: { pathname: '/foo/bar' }, expected: true },
{ input: { pathname: '/foo/bar/bar' }, expected: true },
{ input: { pathname: '/foo/bar/baz' }, expected: false },
{ input: { pathname: '/foo' }, expected: true },
{ input: { pathname: '/foo/' }, expected: false },
]);
}, "repeated optional group");

View file

@ -0,0 +1,611 @@
[
{
"pattern": { "pathname": "/foo/bar" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo/bar" },
"input": { "pathname": "/foo/ba" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar" },
"input": { "pathname": "/foo/bar/" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar" },
"input": { "pathname": "/foo/bar/baz" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar" },
"input": "https://example.com/foo/bar",
"expected": {
"input": "https://example.com/foo/bar",
"hostname": { "input": "example.com", "groups": { "0": "example.com" } },
"pathname": { "input": "/foo/bar", "groups": {} },
"protocol": { "input": "https", "groups": { "0": "https" } }
}
},
{
"pattern": { "pathname": "/foo/bar" },
"input": "https://example.com/foo/bar/baz",
"expected": null
},
{
"pattern": { "pathname": "/foo/bar" },
"input": { "hostname": "example.com", "pathname": "/foo/bar" },
"expected": {
"input": { "hostname": "example.com", "pathname": "/foo/bar" },
"hostname": { "input": "example.com", "groups": { "0": "example.com" } },
"pathname": { "input": "/foo/bar", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo/bar" },
"input": { "hostname": "example.com", "pathname": "/foo/bar/baz" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar" },
"input": { "pathname": "/foo/bar", "baseURL": "https://example.com" },
"expected": {
"input": { "pathname": "/foo/bar", "baseURL": "https://example.com" },
"hostname": { "input": "example.com", "groups": { "0": "example.com" } },
"pathname": { "input": "/foo/bar", "groups": {} },
"protocol": { "input": "https", "groups": { "0": "https" } }
}
},
{
"pattern": { "pathname": "/foo/bar" },
"input": { "pathname": "/foo/bar/baz", "baseURL": "https://example.com" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "pathname": "/foo/bar" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "hostname": "example.com", "pathname": "/foo/bar" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "protocol": "https", "hostname": "example.com",
"pathname": "/foo/bar" },
"expected": {
"input": { "protocol": "https", "hostname": "example.com",
"pathname": "/foo/bar" },
"exactly_empty_components": [ "username", "password", "port" ],
"hostname": { "input": "example.com", "groups": {} },
"pathname": { "input": "/foo/bar", "groups": {} },
"protocol": { "input": "https", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "protocol": "https", "hostname": "example.com",
"pathname": "/foo/bar/baz" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "protocol": "https", "hostname": "example.com",
"pathname": "/foo/bar", "search": "otherquery",
"hash": "otherhash" },
"expected": {
"input": { "protocol": "https", "hostname": "example.com",
"pathname": "/foo/bar", "search": "otherquery",
"hash": "otherhash" },
"exactly_empty_components": [ "username", "password", "port" ],
"hash": { "input": "otherhash", "groups": { "0": "otherhash" } },
"hostname": { "input": "example.com", "groups": {} },
"pathname": { "input": "/foo/bar", "groups": {} },
"protocol": { "input": "https", "groups": {} },
"search": { "input": "otherquery", "groups": { "0": "otherquery" } }
}
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": "https://example.com/foo/bar",
"expected": {
"input": "https://example.com/foo/bar",
"exactly_empty_components": [ "username", "password", "port" ],
"hostname": { "input": "example.com", "groups": {} },
"pathname": { "input": "/foo/bar", "groups": {} },
"protocol": { "input": "https", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": "https://example.com/foo/bar?otherquery#otherhash",
"expected": {
"input": "https://example.com/foo/bar?otherquery#otherhash",
"exactly_empty_components": [ "username", "password", "port" ],
"hash": { "input": "otherhash", "groups": { "0": "otherhash" } },
"hostname": { "input": "example.com", "groups": {} },
"pathname": { "input": "/foo/bar", "groups": {} },
"protocol": { "input": "https", "groups": {} },
"search": { "input": "otherquery", "groups": { "0": "otherquery" } }
}
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": "https://example.com/foo/bar/baz",
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": "https://other.com/foo/bar",
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": "http://other.com/foo/bar",
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "pathname": "/foo/bar", "baseURL": "https://example.com" },
"expected": {
"input": { "pathname": "/foo/bar", "baseURL": "https://example.com" },
"exactly_empty_components": [ "username", "password", "port" ],
"hostname": { "input": "example.com", "groups": {} },
"pathname": { "input": "/foo/bar", "groups": {} },
"protocol": { "input": "https", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "pathname": "/foo/bar/baz", "baseURL": "https://example.com" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "pathname": "/foo/bar", "baseURL": "https://other.com" },
"expected": null
},
{
"pattern": { "pathname": "/foo/bar",
"baseURL": "https://example.com?query#hash" },
"input": { "pathname": "/foo/bar", "baseURL": "http://example.com" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/:bar" },
"input": { "pathname": "/foo/index.html" },
"expected": {
"input": { "pathname": "/foo/index.html" },
"pathname": { "input": "/foo/index.html", "groups": { "bar": "index.html" } }
}
},
{
"pattern": { "pathname": "/foo/:bar" },
"input": { "pathname": "/foo/bar/" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar" },
"input": { "pathname": "/foo/" },
"expected": null
},
{
"pattern": { "pathname": "/foo/(.*)" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)" },
"input": { "pathname": "/foo/bar/baz" },
"expected": {
"input": { "pathname": "/foo/bar/baz" },
"pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)" },
"input": { "pathname": "/foo/" },
"expected": {
"input": { "pathname": "/foo/" },
"pathname": { "input": "/foo/", "groups": { "0": "" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)" },
"input": { "pathname": "/foo" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar(.*)" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/:bar(.*)" },
"input": { "pathname": "/foo/bar/baz" },
"expected": {
"input": { "pathname": "/foo/bar/baz" },
"pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } }
}
},
{
"pattern": { "pathname": "/foo/:bar(.*)" },
"input": { "pathname": "/foo/" },
"expected": {
"input": { "pathname": "/foo/" },
"pathname": { "input": "/foo/", "groups": { "bar": "" } }
}
},
{
"pattern": { "pathname": "/foo/:bar(.*)" },
"input": { "pathname": "/foo" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar?" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/:bar?" },
"input": { "pathname": "/foo" },
"expected": {
"input": { "pathname": "/foo" },
"pathname": { "input": "/foo", "groups": { "bar": "" } }
}
},
{
"pattern": { "pathname": "/foo/:bar?" },
"input": { "pathname": "/foo/" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar?" },
"input": { "pathname": "/foobar" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar?" },
"input": { "pathname": "/foo/bar/baz" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar+" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/:bar+" },
"input": { "pathname": "/foo/bar/baz" },
"expected": {
"input": { "pathname": "/foo/bar/baz" },
"pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } }
}
},
{
"pattern": { "pathname": "/foo/:bar+" },
"input": { "pathname": "/foo" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar+" },
"input": { "pathname": "/foo/" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar+" },
"input": { "pathname": "/foobar" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar*" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/:bar*" },
"input": { "pathname": "/foo/bar/baz" },
"expected": {
"input": { "pathname": "/foo/bar/baz" },
"pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } }
}
},
{
"pattern": { "pathname": "/foo/:bar*" },
"input": { "pathname": "/foo" },
"expected": {
"input": { "pathname": "/foo" },
"pathname": { "input": "/foo", "groups": { "bar": "" } }
}
},
{
"pattern": { "pathname": "/foo/:bar*" },
"input": { "pathname": "/foo/" },
"expected": null
},
{
"pattern": { "pathname": "/foo/:bar*" },
"input": { "pathname": "/foobar" },
"expected": null
},
{
"pattern": { "pathname": "/foo/(.*)?" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)?" },
"input": { "pathname": "/foo/bar/baz" },
"expected": {
"input": { "pathname": "/foo/bar/baz" },
"pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)?" },
"input": { "pathname": "/foo" },
"expected": {
"input": { "pathname": "/foo" },
"pathname": { "input": "/foo", "groups": { "0": "" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)?" },
"input": { "pathname": "/foo/" },
"expected": {
"input": { "pathname": "/foo/" },
"pathname": { "input": "/foo/", "groups": { "0": "" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)?" },
"input": { "pathname": "/foobar" },
"expected": null
},
{
"pattern": { "pathname": "/foo/(.*)?" },
"input": { "pathname": "/fo" },
"expected": null
},
{
"pattern": { "pathname": "/foo/(.*)+" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)+" },
"input": { "pathname": "/foo/bar/baz" },
"expected": {
"input": { "pathname": "/foo/bar/baz" },
"pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)+" },
"input": { "pathname": "/foo" },
"expected": null
},
{
"pattern": { "pathname": "/foo/(.*)+" },
"input": { "pathname": "/foo/" },
"expected": {
"input": { "pathname": "/foo/" },
"pathname": { "input": "/foo/", "groups": { "0": "" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)+" },
"input": { "pathname": "/foobar" },
"expected": null
},
{
"pattern": { "pathname": "/foo/(.*)+" },
"input": { "pathname": "/fo" },
"expected": null
},
{
"pattern": { "pathname": "/foo/(.*)*" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)*" },
"input": { "pathname": "/foo/bar/baz" },
"expected": {
"input": { "pathname": "/foo/bar/baz" },
"pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)*" },
"input": { "pathname": "/foo" },
"expected": {
"input": { "pathname": "/foo" },
"pathname": { "input": "/foo", "groups": { "0": "" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)*" },
"input": { "pathname": "/foo/" },
"expected": {
"input": { "pathname": "/foo/" },
"pathname": { "input": "/foo/", "groups": { "0": "" } }
}
},
{
"pattern": { "pathname": "/foo/(.*)*" },
"input": { "pathname": "/foobar" },
"expected": null
},
{
"pattern": { "pathname": "/foo/(.*)*" },
"input": { "pathname": "/fo" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo{/bar}" },
"input": { "pathname": "/foo/bar/baz" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}" },
"input": { "pathname": "/foo" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}" },
"input": { "pathname": "/foo/" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}?" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo{/bar}?" },
"input": { "pathname": "/foo/bar/baz" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}?" },
"input": { "pathname": "/foo" },
"expected": {
"input": { "pathname": "/foo" },
"pathname": { "input": "/foo", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo{/bar}?" },
"input": { "pathname": "/foo/" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}+" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo{/bar}+" },
"input": { "pathname": "/foo/bar/bar" },
"expected": {
"input": { "pathname": "/foo/bar/bar" },
"pathname": { "input": "/foo/bar/bar", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo{/bar}+" },
"input": { "pathname": "/foo/bar/baz" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}+" },
"input": { "pathname": "/foo" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}+" },
"input": { "pathname": "/foo/" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}*" },
"input": { "pathname": "/foo/bar" },
"expected": {
"input": { "pathname": "/foo/bar" },
"pathname": { "input": "/foo/bar", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo{/bar}*" },
"input": { "pathname": "/foo/bar/bar" },
"expected": {
"input": { "pathname": "/foo/bar/bar" },
"pathname": { "input": "/foo/bar/bar", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo{/bar}*" },
"input": { "pathname": "/foo/bar/baz" },
"expected": null
},
{
"pattern": { "pathname": "/foo{/bar}*" },
"input": { "pathname": "/foo" },
"expected": {
"input": { "pathname": "/foo" },
"pathname": { "input": "/foo", "groups": {} }
}
},
{
"pattern": { "pathname": "/foo{/bar}*" },
"input": { "pathname": "/foo/" },
"expected": null
}
]

View file

@ -1,7 +0,0 @@
function runTest(pattern, expected_list) {
const p = new URLPattern(pattern);
for (let entry of expected_list) {
assert_equals(p.test(entry.input), entry.expected,
`input: ${JSON.stringify(entry.input)}`);
}
}

View file

@ -0,0 +1,81 @@
// META: global=window,worker
function runTests(data) {
for (let entry of data) {
test(function() {
const pattern = new URLPattern(entry.pattern);
// First, validate the test() method by converting the expected result to
// a truthy value.
assert_equals(pattern.test(entry.input), !!entry.expected,
'test() result');
// Next, start validating the exec() method.
const result = pattern.exec(entry.input);
// On a failed match exec() returns null.
if (!entry.expected) {
assert_equals(result, entry.expected, 'exec() failed match result');
return;
}
// Next verify the result.input is correct. This may be a structured
// URLPatternInit dictionary object or a URL string.
if (typeof entry.expected.input === 'object') {
assert_object_equals(result.input, entry.expected.input,
'exec() result.input');
} else {
assert_equals(result.input, entry.expected.input,
'exec() result.input');
}
// Next we will compare the URLPatternComponentResult for each of these
// expected components.
const component_list = [
'protocol',
'username',
'password',
'hostname',
'password',
'pathname',
'search',
'hash',
];
for (let component of component_list) {
let expected_obj = entry.expected[component];
// If the test expectations don't include a component object, then
// we auto-generate one. This is convenient for the many cases
// where the pattern has a default wildcard or empty string pattern
// for a component and the input is essentially empty.
if (!expected_obj) {
// We must treat pathname specially since it always has at least one
// slash.
if (component === 'pathname')
expected_obj = { input: '/', groups: {} };
else
expected_obj = { input: '', groups: {} };
// Next, we must treat default wildcards differently than empty string
// patterns. The wildcard results in a capture group, but the empty
// string pattern does not. The expectation object must list which
// components should be empty instead of wildcards in
// |exactly_empty_components|.
if (!entry.expected.exactly_empty_components ||
!entry.expected.exactly_empty_components.includes(component)) {
expected_obj.groups['0'] = '';
}
}
assert_object_equals(result[component], expected_obj,
`exec() result for ${component}`);
}
}, `Pattern: ${JSON.stringify(entry.pattern)} Input: ${JSON.stringify(entry.input)}`);
}
}
promise_test(async function() {
const response = await fetch('resources/urlpatterntestdata.json');
const data = await response.json();
runTests(data);
}, 'Loading data...');

View file

@ -64,3 +64,9 @@ gen-bundle \
-primaryURL $wpt_test_http_origin/web-bundle/resources/wbn/resource.js \
-dir nested/ \
-o wbn/nested-main.wbn
gen-bundle \
-version b1 \
-har urn-uuid.har \
-primaryURL urn:uuid:020111b3-437a-4c5c-ae07-adb6bbffb720 \
-o wbn/urn-uuid.wbn

View file

@ -0,0 +1,25 @@
{
"log": {
"entries": [
{
"request": {
"method": "GET",
"url": "urn:uuid:020111b3-437a-4c5c-ae07-adb6bbffb720",
"headers": []
},
"response": {
"status": 200,
"headers": [
{
"name": "Content-type",
"value": "application/javascript"
}
],
"content": {
"text": "window.report_result('OK');"
}
}
}
]
}
}

View file

@ -100,6 +100,16 @@
assert_equals(module.result, 'resource1 from network');
}, 'Subresource URL must be same-origin with bundle URL');
promise_test(async () => {
const url = 'urn:uuid:020111b3-437a-4c5c-ae07-adb6bbffb720';
const link = document.createElement('link');
link.rel = 'webbundle';
link.href = '../resources/wbn/urn-uuid.wbn';
link.resources = url;
document.body.appendChild(link);
assert_equals(await loadScriptAndWaitReport(url), 'OK');
}, 'Subresource loading with urn:uuid: URL');
promise_test(async () => {
const wbn_url = 'http://web-platform.test:8001/web-bundle/resources/wbn/subresource.wbn?test-resources-update';
const resource_url = 'http://web-platform.test:8001/web-bundle/resources/wbn/submodule.js';