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 expected: TIMEOUT
[Opening a blob URL in a new window immediately before revoking it works.] [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.] [Fetching a blob URL immediately before revoking it works in an iframe.]
expected: FAIL 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] [Hit test intersecting scaled box]
expected: FAIL 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] [Response: combined response Content-Type: text/html;" \\" text/plain ";charset=GBK]
expected: NOTRUN 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] [<iframe>: separate response Content-Type: text/html */*;charset=gbk]
expected: FAIL expected: FAIL
@ -324,3 +318,15 @@
[<iframe>: combined response Content-Type: text/html;x=" text/plain] [<iframe>: combined response Content-Type: text/html;x=" text/plain]
expected: FAIL 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!] [X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
expected: FAIL 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] [embedded-opener-remove-frame.html]
expected: TIMEOUT
[opener of discarded nested browsing context] [opener of discarded nested browsing context]
expected: FAIL 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"] [XHTML img usemap="#hash-id"]
expected: FAIL expected: FAIL
[HTML (standards) IMG usemap="no-hash-name"]
expected: FAIL

View file

@ -1,5 +1,5 @@
[iframe_sandbox_popups_escaping-1.html] [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] [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: TIMEOUT expected: TIMEOUT

View file

@ -1,5 +1,4 @@
[iframe_sandbox_popups_escaping-3.html] [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] [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] [iframe_sandbox_popups_nonescaping-3.html]
expected: CRASH expected: TIMEOUT
[Check that popups from a sandboxed iframe do not escape the sandbox] [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] [The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: TIMEOUT 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.] [X SNR (39.927354911150644 dB) is not greater than or equal to 65.737. Got 39.927354911150644.]
expected: FAIL 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] [audiocontext-not-fully-active.html]
expected: TIMEOUT
[frame in navigated remote-site frame] [frame in navigated remote-site frame]
expected: FAIL 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] [sharedworker-in-worker.html]
expected: ERROR
[Base URL in workers: new SharedWorker()] [Base URL in workers: new SharedWorker()]
expected: FAIL expected: FAIL

View file

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

View file

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

View file

@ -17393,15 +17393,6 @@
] ]
] ]
}, },
"input-events": {
"input-events-cut-paste-manual.html": [
"4091c87cdd34720c29578b766b5791f8d68d4982",
[
null,
{}
]
]
},
"mediacapture-depth": { "mediacapture-depth": {
"dictionary-manual.https.html": [ "dictionary-manual.https.html": [
"0a5b824881509baee240f385a9eeaa380a579948", "0a5b824881509baee240f385a9eeaa380a579948",
@ -121654,6 +121645,19 @@
], ],
{} {}
] ]
],
"translation-animation-subpixel-offset.html": [
"5692a0823289811967e889969603cf8799cf7cf6",
[
null,
[
[
"/css/css-animations/translation-animation-subpixel-offset-ref.html",
"=="
]
],
{}
]
] ]
}, },
"css-backgrounds": { "css-backgrounds": {
@ -121844,7 +121848,7 @@
] ]
], ],
"attachment-local-clipping-image-5.html": [ "attachment-local-clipping-image-5.html": [
"ddd02dcfaefdd805188b1b9682f2070d6f81a976", "1d6f1a8c6628a47e5421eb99769f56d53a74a08a",
[ [
null, null,
[ [
@ -121864,7 +121868,7 @@
], ],
[ [
0, 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": [ "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": [ "overflow-body-propagation-001.html": [
"0998fe68e007d5a46ff11d5ff87fdbad12d1dfe2", "0998fe68e007d5a46ff11d5ff87fdbad12d1dfe2",
[ [
@ -245334,7 +245377,7 @@
] ]
], ],
"fieldset-border-radius-with-alpha.html": [ "fieldset-border-radius-with-alpha.html": [
"36b9fc8c8ef7aa08473e4061c090042882b8b582", "7a942076fbeaaee41c668ced609dee94906e56be",
[ [
null, null,
[ [
@ -257305,10 +257348,6 @@
[] []
], ],
"workflows": { "workflows": {
"detect_pull_request_preview.yml": [
"02870d8c7d223a371c09a97b5906610af191a5e4",
[]
],
"documentation.yml": [ "documentation.yml": [
"b8bf7972b0f7666f8a01a81b05e8dec432732225", "b8bf7972b0f7666f8a01a81b05e8dec432732225",
[] []
@ -257322,7 +257361,7 @@
[] []
], ],
"pull_request_previews.yml": [ "pull_request_previews.yml": [
"bf049dd9e63b0a8901bbf693a42fab2dfb621305", "676aff3021242bb0d9b0ac49209088f48beca9fc",
[] []
], ],
"regen_certs.yml": [ "regen_certs.yml": [
@ -300804,6 +300843,10 @@
"transform-animation-under-large-scale-ref.html": [ "transform-animation-under-large-scale-ref.html": [
"14b41366268c3a33f2d49e477bab2d40c6acb432", "14b41366268c3a33f2d49e477bab2d40c6acb432",
[] []
],
"translation-animation-subpixel-offset-ref.html": [
"5fc04d972b41d089b71d0c0baabc0d8e0d28c1d0",
[]
] ]
}, },
"css-backgrounds": { "css-backgrounds": {
@ -302846,6 +302889,10 @@
"64629ca11977c77e4b1420881c5bc59a50816323", "64629ca11977c77e4b1420881c5bc59a50816323",
[] []
], ],
"content-visibility-079-ref.html": [
"416f23e8ca6aff4cd121a1c6b7a77945accd433e",
[]
],
"inline-container-with-child-ref.html": [ "inline-container-with-child-ref.html": [
"af51cbd5db8f3eff56411345860548e95fe4d354", "af51cbd5db8f3eff56411345860548e95fe4d354",
[] []
@ -333190,7 +333237,7 @@
[] []
], ],
"testdriver-extension-tutorial.md": [ "testdriver-extension-tutorial.md": [
"0d73f9d41f8205802407aa32cdd5e11aae2c44f9", "185a27f1a46af840f19395cab885451088130cb6",
[] []
], ],
"testdriver.md": [ "testdriver.md": [
@ -338885,6 +338932,10 @@
[] []
], ],
"resources": { "resources": {
"check-sandbox-flags.html": [
"0dc95315f164d693bd7bd0622880781f30bad7a1",
[]
],
"post-done-to-opener.html": [ "post-done-to-opener.html": [
"b47f0f274ef43148d3be418e2c0628a567f1c3c1", "b47f0f274ef43148d3be418e2c0628a567f1c3c1",
[] []
@ -341026,7 +341077,7 @@
[] []
], ],
"redirect.py": [ "redirect.py": [
"21a3dec9d224c1fca4a8815d9830d392c44cd18b", "88dbd60fae7a41e245a0b822f8336ddeff56f410",
[] []
], ],
"report.py": [ "report.py": [
@ -341034,7 +341085,7 @@
[] []
], ],
"reporting-common.js": [ "reporting-common.js": [
"3b1820cebb8f1d5f8b7fb00b90740cee6de9fbb9", "01f835e12ad5f24c07c50ae7376183628fff80bc",
[] []
], ],
"test-access-property.js": [ "test-access-property.js": [
@ -345780,7 +345831,7 @@
[] []
], ],
"fieldset-border-radius-with-alpha-ref.html": [ "fieldset-border-radius-with-alpha-ref.html": [
"577482a328b47b72b78ddc22abef02e12836a378", "5cfe13c40d465a5cd0cebfbafbb8db9d45491cbf",
[] []
], ],
"fieldset-containing-block-ref.html": [ "fieldset-containing-block-ref.html": [
@ -351659,7 +351710,7 @@
}, },
"js-self-profiling": { "js-self-profiling": {
"__dir__.headers": [ "__dir__.headers": [
"63b60e490f47f4db77d33d7a4ca2f5b9a4181de8", "35b10bd2ccdbfa99feb96079fafab61346d025ed",
[] []
] ]
}, },
@ -357053,6 +357104,14 @@
"dee5b62f0005705c18dde06865ab5a9947a37357", "dee5b62f0005705c18dde06865ab5a9947a37357",
[] []
], ],
"csp-default-none.html": [
"1f59d8c2250f7f17f143887dc4f8e68cdf7faa83",
[]
],
"csp-default-none.html.headers": [
"d66f886dd23d44bb33750cb5723f3edb6b7930ab",
[]
],
"document-domain-no-impact.sub.html": [ "document-domain-no-impact.sub.html": [
"fbd7bc3b6e21ee39478c8a63780bb673dafe96a4", "fbd7bc3b6e21ee39478c8a63780bb673dafe96a4",
[] []
@ -362266,7 +362325,7 @@
[] []
], ],
"pr_preview.py": [ "pr_preview.py": [
"2396e9b8aee8a166c3b9321c7e9ec2e8f9b549c0", "5bd2365164b98ba1a8c90a8d1a987fbaf8c97c73",
[] []
], ],
"regen_certs.py": [ "regen_certs.py": [
@ -362355,7 +362414,7 @@
[] []
], ],
"test_pr_preview.py": [ "test_pr_preview.py": [
"ed6310c1ea129b19f313c29c98e5238a85546e42", "87478727d3e20528fea165f0634df7d299d5a6ea",
[] []
] ]
}, },
@ -370453,8 +370512,8 @@
}, },
"urlpattern": { "urlpattern": {
"resources": { "resources": {
"utils.js": [ "urlpatterntestdata.json": [
"9132a52816e0c1cf0bc3a1b577d6611e65be8f6f", "66038e03d23bbb14f6d890a02e3e8dbefcb5aefe",
[] []
] ]
} }
@ -370898,7 +370957,7 @@
] ]
}, },
"generate-test-wbns.sh": [ "generate-test-wbns.sh": [
"8fc28bb9db7cd9900bee9c3555e7e7d84e41b389", "3c8a3ea68d60522452be94a7f8d0aaf9b11f5c55",
[] []
], ],
"location": { "location": {
@ -370961,6 +371020,10 @@
[] []
] ]
}, },
"urn-uuid.har": [
"e0cea3699ae53beedde24dc55287b7d4407083dd",
[]
],
"wbn": { "wbn": {
"__dir__.headers": [ "__dir__.headers": [
"21e57b9caca1183ecda6f5d4fb9ebd556abf769a", "21e57b9caca1183ecda6f5d4fb9ebd556abf769a",
@ -371019,6 +371082,10 @@
"subresource.wbn": [ "subresource.wbn": [
"eabd994ff8bceaf4ef52b329b4faa158c292af12", "eabd994ff8bceaf4ef52b329b4faa158c292af12",
[] []
],
"urn-uuid.wbn": [
"a4bd3efc9cc73e1405bad5209631e0e1e1f352b5",
[]
] ]
} }
} }
@ -397200,7 +397267,7 @@
] ]
], ],
"script-src-1_10.html": [ "script-src-1_10.html": [
"a1bfdaeb15bfbadb322b393b34f6872de1c55699", "fa25976c128f195828dbfdb2e7c068f72aa83f8e",
[ [
null, null,
{} {}
@ -415778,7 +415845,7 @@
] ]
], ],
"fixed-layout-2.html": [ "fixed-layout-2.html": [
"7cb09d4dd04de1da380837b0b4c2e82cc1492f59", "13a4664249f3a758858e190fa9361ffc6b2ed17b",
[ [
null, null,
{} {}
@ -458175,6 +458242,13 @@
{} {}
] ]
], ],
"sandbox-initial-empty-document-toward-same-origin.html": [
"d1306c970322682f3979c497a5decd78218ba845",
[
null,
{}
]
],
"sandbox-navigation-timing.tentative.html": [ "sandbox-navigation-timing.tentative.html": [
"686f1c0c9f6847772edbe2ef070a483db1df3d9d", "686f1c0c9f6847772edbe2ef070a483db1df3d9d",
[ [
@ -496248,6 +496322,15 @@
} }
] ]
], ],
"input-events-cut-paste.html": [
"f2ca5a0a654f270d33d8662f4e8b25369cd98df7",
[
null,
{
"testdriver": true
}
]
],
"input-events-exec-command.html": [ "input-events-exec-command.html": [
"8f8493651e5af4b747d952ac786334ace8ed61c4", "8f8493651e5af4b747d952ac786334ace8ed61c4",
[ [
@ -500396,7 +500479,18 @@
null, null,
{} {}
] ]
] ],
"mse-for-webcodecs": {
"tentative": {
"mediasource-webcodecs-addsourcebuffer.html": [
"a9dd7072b0758d58bac67c13dcb78c76dc6aa669",
[
null,
{}
]
]
}
}
}, },
"mediacapture-depth": { "mediacapture-depth": {
"idlharness.html": [ "idlharness.html": [
@ -523278,6 +523372,13 @@
} }
] ]
], ],
"iframe-failed-commit.html": [
"75e19f788efb2ecbccf67c4df02f030e3d9415c7",
[
null,
{}
]
],
"nested-context-navigations-embed.html": [ "nested-context-navigations-embed.html": [
"bbba46c50edf21f50b49099c779dd5a8792faa0f", "bbba46c50edf21f50b49099c779dd5a8792faa0f",
[ [
@ -537621,7 +537722,7 @@
] ]
], ],
"block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.tentative.html": [ "block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.tentative.html": [
"f36ae94ea10ce95d7087ec22ef4a25f157f7e6a8", "c24715718d17561f779c6df7098f4c0b874675a9",
[ [
null, null,
{} {}
@ -540521,127 +540622,48 @@
] ]
}, },
"urlpattern": { "urlpattern": {
"input.https.any.js": [ "urlpattern.https.any.js": [
"71a8fb237b8101789828e1352dc516d97c7d509a", "27b03d0ff03261d50721edb7b22b8205e0610238",
[ [
"urlpattern/input.https.any.html", "urlpattern/urlpattern.https.any.html",
{ {
"script_metadata": [ "script_metadata": [
[ [
"global", "global",
"window,worker" "window,worker"
],
[
"script",
"resources/utils.js"
] ]
] ]
} }
], ],
[ [
"urlpattern/input.https.any.serviceworker.html", "urlpattern/urlpattern.https.any.serviceworker.html",
{ {
"script_metadata": [ "script_metadata": [
[ [
"global", "global",
"window,worker" "window,worker"
],
[
"script",
"resources/utils.js"
] ]
] ]
} }
], ],
[ [
"urlpattern/input.https.any.sharedworker.html", "urlpattern/urlpattern.https.any.sharedworker.html",
{ {
"script_metadata": [ "script_metadata": [
[ [
"global", "global",
"window,worker" "window,worker"
],
[
"script",
"resources/utils.js"
] ]
] ]
} }
], ],
[ [
"urlpattern/input.https.any.worker.html", "urlpattern/urlpattern.https.any.worker.html",
{ {
"script_metadata": [ "script_metadata": [
[ [
"global", "global",
"window,worker" "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": [ "subresource-loading-from-web-bundle.tentative.html": [
"c2de0eb0994f54c65a342b501aa3e9107949faf7", "53a9c1b2cc707271c8d7b0e7fb7385fd59839744",
[ [
null, 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] [Hit test intersecting scaled box]
expected: FAIL 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 ] [fetch(): separate response Content-Type: text/plain ]
expected: NOTRUN 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] [<iframe>: separate response Content-Type: text/html */*;charset=gbk]
expected: FAIL expected: FAIL
@ -324,3 +318,15 @@
[<iframe>: combined response Content-Type: text/html;x=" text/plain] [<iframe>: combined response Content-Type: text/html;x=" text/plain]
expected: FAIL 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!] [X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
expected: FAIL 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] [embedded-opener-remove-frame.html]
expected: TIMEOUT
[opener and "removed" embedded documents] [opener and "removed" embedded documents]
expected: FAIL expected: FAIL
[opener of discarded nested browsing context] [opener of discarded nested browsing context]
expected: FAIL 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"] [XHTML img usemap="http://example.org/#garbage-before-hash-id"]
expected: FAIL expected: FAIL
[HTML (standards) IMG usemap="no-hash-name"]
expected: FAIL

View file

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

View file

@ -1,6 +1,5 @@
[iframe_sandbox_popups_escaping-3.html] [iframe_sandbox_popups_escaping-3.html]
type: testharness type: testharness
expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] [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] [iframe_sandbox_popups_nonescaping-3.html]
expected: CRASH expected: TIMEOUT
[Check that popups from a sandboxed iframe do not escape the sandbox] [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] [The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: TIMEOUT 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.] [X SNR (39.927354911150644 dB) is not greater than or equal to 65.737. Got 39.927354911150644.]
expected: FAIL 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] [audiocontext-not-fully-active.html]
expected: TIMEOUT
[frame in navigated remote-site frame] [frame in navigated remote-site frame]
expected: FAIL 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] [sharedworker-in-worker.html]
expected: ERROR
[Base URL in workers: new SharedWorker()] [Base URL in workers: new SharedWorker()]
expected: FAIL 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: on:
schedule: pull_request_target:
- cron: '*/5 * * * *' types: [opened, synchronize, reopened, closed, labeled, unlabeled]
jobs: jobs:
update-pr-preview: update-pr-preview:
runs-on: ubuntu-18.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Install dependency - name: Install dependency
run: pip install requests run: pip install requests
- name: Synchronize state - name: Deploy PR
# Use a conditional step instead of a conditional job to work around #20700. # Use a conditional step instead of a conditional job to work around #20700.
if: github.repository == 'web-platform-tests/wpt' if: github.repository == 'web-platform-tests/wpt'
run: run:
./tools/ci/pr_preview.py ./tools/ci/pr_preview.py
--host https://api.github.com --host https://api.github.com
--github-project web-platform-tests/wpt --github-project web-platform-tests/wpt
synchronize --target https://wptpr.live
--window 480 --timeout 600
env: env:
# This Workflow must trigger further workflows. The GitHub-provided GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# `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 }}

View file

@ -15,7 +15,7 @@
var t_spv = async_test("Test that securitypolicyviolation event is fired"); var t_spv = async_test("Test that securitypolicyviolation event is fired");
window.addEventListener("securitypolicyviolation", t_spv.step_func_done(function(e) { window.addEventListener("securitypolicyviolation", t_spv.step_func_done(function(e) {
assert_equals(e.violatedDirective, "script-src"); assert_equals(e.violatedDirective, "script-src-elem");
})); }));
</script> </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> <title>CSS Test: background-{attachment: local; clip: padding-box; image}; border-radius</title>
<link rel="match" href="attachment-local-clipping-image-4-ref.html" /> <link rel="match" href="attachment-local-clipping-image-4-ref.html" />
<meta name="flags" content="dom" /> <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="author" title="Simon Sapin" href="http://exyr.org/about/" />
<link rel="help" href="http://www.w3.org/TR/css3-background/#the-background-attachment" /> <link rel="help" href="http://www.w3.org/TR/css3-background/#the-background-attachment" />
<style> <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 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, getComputedStyle(document.querySelector("x-table:nth-of-type(1)")).tableLayout,
'auto' 'fixed'
], ],
[ [
"Table-layout:fixed is not applied when width is max-content", "Table-layout:fixed is not applied when width is max-content",
@ -70,9 +70,9 @@
100 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, getComputedStyle(document.querySelector("x-table:nth-of-type(2)")).tableLayout,
'auto' 'fixed'
], ],
[ [
"Table-layout:fixed is not applied when width is min-content", "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). 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! ## Code!
### [resources/testdriver.js](https://github.com/web-platform-tests/wpt/blob/master/resources/testdriver.js) ### [resources/testdriver.js](https://github.com/web-platform-tests/wpt/blob/master/resources/testdriver.js)
@ -273,4 +298,3 @@ async_test(t => {
}); });
</script> </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): def main(request, response):
response.status = 302 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)})`+ `|header(Cross-Origin-Embedder-Policy-Report-Only,${encodeURIComponent(coepRo)})`+
`&uuid=${executorToken}`; `&uuid=${executorToken}`;
const openee = window.open(openee_url); 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. // 1. Make sure the new document is loaded.
send(executorToken, ` send(executorToken, `

View file

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

View file

@ -9,14 +9,18 @@ fieldset {
height: 100px; height: 100px;
width: 100px; width: 100px;
} }
legend {
height: 50px;
width: 50px;
}
div { div {
background-color: green; background-color: green;
height: 150px; height: 110px;
position: absolute; position: absolute;
width: 150px; width: 150px;
top: 50px; top: 70px;
} }
</style> </style>
<p>There should be no red.</p> <p>There should be no red.</p>
<fieldset></fieldset> <fieldset><legend></legend></fieldset>
<div></div> <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-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp 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' LABEL = 'safe for preview'
# The number of seconds to wait between attempts to verify that a submission # The number of seconds to wait between attempts to verify that a submission
# preview is available on the Pull Request preview server # 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 # Pull Requests from authors with the following associations to the project
# should automatically receive previews # should automatically receive previews
# #
@ -44,7 +44,7 @@ logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def gh_request(method_name, url, body=None, media_type=None): def gh_request(method_name, url, body=None, media_type=None):
github_token = os.environ['DEPLOY_TOKEN'] github_token = os.environ['GITHUB_TOKEN']
kwargs = { kwargs = {
'headers': { 'headers': {
@ -67,6 +67,9 @@ def gh_request(method_name, url, body=None, media_type=None):
return resp.json() return resp.json()
class GitHubRateLimitException(Exception):
pass
def guard(resource): def guard(resource):
'''Decorate a `Project` instance method which interacts with the GitHub '''Decorate a `Project` instance method which interacts with the GitHub
API, ensuring that the subsequent request will not deplete the relevant 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: if limit and float(remaining) / limit < API_RATE_LIMIT_THRESHOLD:
raise Exception( raise GitHubRateLimitException(
'Exiting to avoid GitHub.com API request throttling.' 'Exiting to avoid GitHub.com API request throttling.'
) )
@ -132,25 +135,6 @@ class Project(object):
return {pr['number']: pr for pr in data['items']} 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') @guard('core')
def create_ref(self, refspec, revision): def create_ref(self, refspec, revision):
url = '{}/repos/{}/git/refs'.format(self._host, self._github_project) 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 return deployments.pop() if len(deployments) else None
@guard('core') @guard('core')
def update_deployment(self, target, deployment, state, description=''): def add_deployment_status(self, target, deployment, state, description=''):
if state in ('pending', 'success'): if state in ('pending', 'success'):
pr_number = deployment['environment'][len(DEPLOYMENT_PREFIX):] pr_number = deployment['environment'][len(DEPLOYMENT_PREFIX):]
environment_url = '{}/{}'.format(target, pr_number) environment_url = '{}/{}'.format(target, pr_number)
@ -227,7 +211,7 @@ class Remote(object):
# The repository in the GitHub Actions environment is configured with # The repository in the GitHub Actions environment is configured with
# a remote whose URL uses unauthenticated HTTPS, making it unsuitable # a remote whose URL uses unauthenticated HTTPS, making it unsuitable
# for pushing changes. # for pushing changes.
self._token = os.environ['DEPLOY_TOKEN'] self._token = os.environ['GITHUB_TOKEN']
def _git(self, command): def _git(self, command):
return subprocess.check_output([ 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['user']['login'] not in AUTOMATION_GITHUB_USERS and
pull_request['author_association'] in TRUSTED_AUTHOR_ASSOCIATIONS 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): def is_deployed(host, deployment):
worktree_name = deployment['environment'][len(DEPLOYMENT_PREFIX):] worktree_name = deployment['environment'][len(DEPLOYMENT_PREFIX):]
response = requests.get( url = '{}/.git/worktrees/{}/HEAD'.format(host, worktree_name)
'{}/.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: if response.status_code != 200:
return False return False
logger.info('Response text: %s', response.text.strip())
return response.text.strip() == deployment['sha'] return response.text.strip() == deployment['sha']
def synchronize(host, github_project, window): def update_mirror_refs(project, remote, pull_request):
'''Inspect all Pull Requests which have been modified in a given window of '''Update the WPT refs that control mirroring of this pull request.
time. Add or remove the "preview" label and update or delete the relevant
git refs according to the status of each Pull Request.'''
project = Project(host, github_project) Two sets of refs are used to control wptpr.live's mirroring of pull
remote = Remote(github_project) requests:
pull_requests = project.get_pull_requests( 1. refs/prs-trusted-for-preview/{number}
time.gmtime(time.time() - window) 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 if should_be_mirrored(project, pull_request):
# not run for a while and the PR falls outside the checked window. To logger.info('Pull Request should be mirrored')
# 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
for pull_request in pull_requests.values(): if revision_trusted is None:
logger.info('Processing Pull Request #%(number)d', pull_request) 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( if revision_open is None:
**pull_request project.create_ref(refspec_open, revision_latest)
) elif revision_open != revision_latest:
refspec_open = 'prs-open/{number}'.format(**pull_request) project.update_ref(refspec_open, revision_latest)
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 should_be_mirrored(project, pull_request): return revision_latest
logger.info('Pull Request should be mirrored')
if revision_trusted is None: logger.info('Pull Request should not be mirrored')
project.create_ref(refspec_trusted, revision_latest)
elif revision_trusted != revision_latest:
project.update_ref(refspec_trusted, revision_latest)
if revision_open is None: if not has_mirroring_label(pull_request) and revision_trusted is not None:
project.create_ref(refspec_open, revision_latest) remote.delete_ref(refspec_trusted)
elif revision_open != revision_latest:
project.update_ref(refspec_open, revision_latest)
if project.get_deployment(revision_latest) is None: if revision_open is not None and not is_open(pull_request):
project.create_deployment( remote.delete_ref(refspec_open)
pull_request, revision_latest
)
else:
logger.info('Pull Request should not be mirrored')
if not has_mirroring_label(pull_request) and revision_trusted is not None: # No revision to be deployed to wptpr.live
remote.delete_ref(refspec_trusted) return None
if revision_open is not None and not is_open(pull_request): class DeploymentFailedException(Exception):
remote.delete_ref(refspec_open) pass
def detect(host, github_project, target, timeout): def deploy(project, target, pull_request, revision, timeout):
'''Manage the status of a GitHub Deployment by polling the Pull Request '''Create a GitHub deployment for the given pull request and revision.
preview website until the Deployment is complete or a timeout is
reached.'''
project = Project(host, github_project) This method creates a pending GitHub deployment, waits for the
corresponding revision to be available on wptpr.live and marks the
with open(os.environ['GITHUB_EVENT_PATH']) as handle: deployment as successful. If the revision does not appear in the given
data = json.loads(handle.read()) timeout, the deployment is marked as errored instead.'''
if project.get_deployment(revision) is not None:
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']
)
return return
deployment = project.create_deployment(pull_request, revision)
message = 'Waiting up to {} seconds for Deployment {} to be available on {}'.format( message = 'Waiting up to {} seconds for Deployment {} to be available on {}'.format(
timeout, deployment['environment'], target timeout, deployment['environment'], target
) )
logger.info(message) logger.info(message)
project.update_deployment(target, deployment, 'pending', message) project.add_deployment_status(target, deployment, 'pending', message)
start = time.time() start = time.time()
while not is_deployed(target, deployment): while not is_deployed(target, deployment):
if time.time() - start > timeout: if time.time() - start > timeout:
message = 'Deployment did not become available after {} seconds'.format(timeout) message = 'Deployment did not become available after {} seconds'.format(timeout)
project.update_deployment(target, deployment, 'error', message) project.add_deployment_status(target, deployment, 'error', message)
raise Exception(message) raise DeploymentFailedException(message)
time.sleep(POLLING_PERIOD) 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)) 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__': if __name__ == '__main__':
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='''Synchronize the state of a GitHub.com project with the description='''Mirror a pull request to an externally-hosted preview
underlying git repository in order to support a externally-hosted system, and create a GitHub Deployment associated with the pull
Pull Request preview system. Communicate the state of that system request pointing at the preview.'''
via GitHub Deployments associated with each Pull Request.'''
) )
parser.add_argument( parser.add_argument(
'--host', required=True, help='the location of the GitHub API server' '--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 help='''the GitHub organization and GitHub project name, separated by
a forward slash (e.g. "web-platform-tests/wpt")''' a forward slash (e.g. "web-platform-tests/wpt")'''
) )
subparsers = parser.add_subparsers(title='subcommands') parser.add_argument(
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(
'--target', '--target',
required=True, required=True,
help='''the URL of the website to which submission previews are help='''the URL of the website to which submission previews are
expected to become available''' expected to become available'''
) )
parser_detect.add_argument( parser.add_argument(
'--timeout', '--timeout',
type=int, type=int,
required=True, required=True,
help='''the number of seconds to wait for a submission preview to help='''the number of seconds to wait for a submission preview to
become available before reporting a GitHub Deployment failure''' become available before reporting a GitHub Deployment failure'''
) )
parser_detect.set_defaults(func=detect)
values = dict(vars(parser.parse_args())) values = dict(vars(parser.parse_args()))
values.pop('func')(**values) main(**values)

View file

@ -14,10 +14,11 @@ import sys
import tempfile import tempfile
import threading import threading
subject = os.path.join( sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
os.path.dirname(os.path.abspath(__file__)), '..', 'pr_preview.py' import pr_preview
)
test_host = 'localhost'
TEST_HOST = 'localhost'
def same_members(a, b): def same_members(a, b):
@ -81,7 +82,10 @@ class MockHandler(BaseHTTPRequestHandler, object):
self.server.actual_traffic.append((request, response)) self.server.actual_traffic.append((request, response))
self.send_response(response[0]) self.send_response(response[0])
self.end_headers() 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): def do_DELETE(self):
return self.do_all() 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 '''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 '{}' unless an alternative status code and body are specified for the given
method and path in the `responses` parameter.''' 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) super(MockServer, self).__init__(address, MockHandler)
self.expected_traffic = expected_traffic self.expected_traffic = expected_traffic
self.actual_traffic = [] self.actual_traffic = []
self.reponse_body_is_json = reponse_body_is_json
def __enter__(self): def __enter__(self):
threading.Thread(target=lambda: self.serve_forever()).start() threading.Thread(target=lambda: self.serve_forever()).start()
@ -115,21 +120,19 @@ class MockServer(HTTPServer, object):
class Requests(object): class Requests(object):
get_rate = ('GET', '/rate_limit', {}) get_rate = ('GET', '/rate_limit', {})
search = ('GET', '/search/issues', {})
pr_details = ('GET', '/repos/test-org/test-repo/pulls/23', {})
ref_create_open = ( 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 = ( ref_create_trusted = (
'POST', 'POST',
'/repos/test-org/test-repo/git/refs', '/repos/test-org/test-repo/git/refs',
{'ref':'refs/prs-trusted-for-preview/23'} {'ref':'refs/prs-trusted-for-preview/45'}
) )
ref_update_open = ( 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 = ( 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_get = ('GET', '/repos/test-org/test-repo/deployments', {})
deployment_create = ('POST', '/repos/test-org/test-repo/deployments', {}) deployment_create = ('POST', '/repos/test-org/test-repo/deployments', {})
@ -167,9 +170,13 @@ class Responses(object):
@contextlib.contextmanager @contextlib.contextmanager
def temp_repo(): def temp_repo(change_dir=False):
original_dir = os.getcwd()
directory = tempfile.mkdtemp() directory = tempfile.mkdtemp()
if change_dir:
os.chdir(directory)
try: try:
subprocess.check_call(['git', 'init'], cwd=directory) subprocess.check_call(['git', 'init'], cwd=directory)
# Explicitly create the default branch. # Explicitly create the default branch.
@ -192,20 +199,22 @@ def temp_repo():
yield directory yield directory
finally: finally:
if change_dir:
os.chdir(original_dir)
shutil.rmtree( shutil.rmtree(
directory, ignore_errors=False, onerror=handle_remove_readonly directory, ignore_errors=False, onerror=handle_remove_readonly
) )
def synchronize(expected_traffic, refs={}): def update_mirror_refs(pull_request, expected_traffic, refs={}):
env = { os.environ['GITHUB_TOKEN'] = 'c0ffee'
'DEPLOY_TOKEN': 'c0ffee'
} github_server = MockServer((TEST_HOST, 0), expected_traffic)
env.update(os.environ) github_port = github_server.server_address[1]
server = MockServer((test_host, 0), expected_traffic)
test_port = server.server_address[1]
remote_refs = {} 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( subprocess.check_call(
['git', 'commit', '--allow-empty', '-m', 'first'], ['git', 'commit', '--allow-empty', '-m', 'first'],
cwd=remote_repo cwd=remote_repo
@ -214,9 +223,7 @@ def synchronize(expected_traffic, refs={}):
['git', 'commit', '--allow-empty', '-m', 'second'], ['git', 'commit', '--allow-empty', '-m', 'second'],
cwd=remote_repo cwd=remote_repo
) )
subprocess.check_call( subprocess.check_call(['git', 'remote', 'add', 'origin', remote_repo])
['git', 'remote', 'add', 'origin', remote_repo], cwd=local_repo
)
for name, value in refs.items(): for name, value in refs.items():
subprocess.check_call( subprocess.check_call(
@ -224,26 +231,18 @@ def synchronize(expected_traffic, refs={}):
cwd=remote_repo cwd=remote_repo
) )
child = subprocess.Popen( subprocess.check_call(['git', 'remote', '-v'])
[ project = pr_preview.Project(
sys.executable, 'http://{}:{}'.format(TEST_HOST, github_port),
subject, 'test-org/test-repo',
'--host',
'http://{}:{}'.format(test_host, test_port),
'--github-project',
'test-org/test-repo',
'synchronize',
'--window',
'3000'
],
cwd=local_repo,
env=env
) )
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'])
lines = subprocess.check_output(
['git', 'ls-remote', 'origin'], cwd=local_repo
)
for line in lines.decode('utf-8').strip().split('\n'): for line in lines.decode('utf-8').strip().split('\n'):
revision, ref = line.split() revision, ref = line.split()
@ -252,75 +251,55 @@ def synchronize(expected_traffic, refs={}):
remote_refs[ref] = revision 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): def deploy(pr_num, revision, expected_github_traffic, expected_preview_traffic):
env = { os.environ['GITHUB_TOKEN'] = 'c0ffee'
'DEPLOY_TOKEN': 'c0ffee'
} github_server = MockServer((TEST_HOST, 0), expected_github_traffic)
env.update(os.environ)
github_server = MockServer((test_host, 0), expected_github_traffic)
github_port = github_server.server_address[1] 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] preview_port = preview_server.server_address[1]
with temp_repo() as repo, github_server, preview_server: method_threw = False
env['GITHUB_EVENT_PATH'] = repo + '/event.json' with github_server, preview_server:
project = pr_preview.Project(
with open(env['GITHUB_EVENT_PATH'], 'w') as handle: 'http://{}:{}'.format(TEST_HOST, github_port),
handle.write(json.dumps(event)) 'test-org/test-repo',
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
) )
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 ( return (
child.returncode, method_threw,
github_server.actual_traffic, github_server.actual_traffic,
preview_server.actual_traffic preview_server.actual_traffic
) )
def test_update_mirror_refs_fail_rate_limited():
def test_synchronize_zero_results(): pull_request = {
expected_traffic = [ 'number': 45,
(Requests.get_rate, Responses.no_limit), 'labels': [],
(Requests.search, ( 'user': {'login': 'stephenmcgruer'},
200, 'author_association': 'COLLABORATOR',
{ 'closed_at': None,
'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():
expected_traffic = [ expected_traffic = [
(Requests.get_rate, ( (Requests.get_rate, (
200, 200,
{ {
'resources': { 'resources': {
'search': { 'core': {
'remaining': 1, 'remaining': 1,
'limit': 10 '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 method_threw
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 same_members(expected_traffic, actual_traffic) assert same_members(expected_traffic, actual_traffic)
def test_synchronize_ignore_closed(): def test_synchronize_ignore_closed():
expected_traffic = [ # No existing refs, but a closed PR event comes in. Nothing should happen.
(Requests.get_rate, Responses.no_limit), pull_request = {
(Requests.search, ( 'number': 45,
200, 'labels': [],
{ 'user': {'login': 'stephenmcgruer'},
'items': [ 'author_association': 'COLLABORATOR',
{ 'closed_at': '2019-10-28',
'number': 23, }
'labels': [], expected_traffic = []
'closed_at': '2019-10-28',
'user': {'login': 'grace'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
))
]
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) 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 = [ 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.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_open, (200, {})),
(Requests.ref_create_trusted, (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) assert same_members(expected_traffic, actual_traffic)
def test_synchronize_ignore_collaborator_bot(): def test_update_mirror_refs_ignore_collaborator_bot():
expected_traffic = [ pull_request = {
(Requests.get_rate, Responses.no_limit), 'number': 45,
(Requests.search, ( 'labels': [],
200, 'user': {'login': 'chromium-wpt-export-bot'},
{ 'author_association': 'COLLABORATOR',
'items': [ 'closed_at': None,
{ }
'number': 23, expected_traffic = []
'labels': [],
'closed_at': None,
'user': {'login': 'chromium-wpt-export-bot'},
'author_association': 'COLLABORATOR'
}
],
'incomplete_results': False
}
))
]
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) assert same_members(expected_traffic, actual_traffic)
def test_synchronize_ignore_untrusted_contributor(): def test_update_mirror_refs_ignore_untrusted_contributor():
expected_traffic = [ pull_request = {
(Requests.get_rate, Responses.no_limit), 'number': 45,
(Requests.search, ( 'labels': [],
200, 'user': {'login': 'stephenmcgruer'},
{ 'author_association': 'CONTRIBUTOR',
'items': [ 'closed_at': None,
{ }
'number': 23, expected_traffic = []
'labels': [],
'closed_at': None,
'user': {'login': 'grace'},
'author_association': 'CONTRIBUTOR'
}
],
'incomplete_results': False
}
))
]
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) 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 = [ 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.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_open, (200, {})),
(Requests.ref_create_trusted, (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) assert same_members(expected_traffic, actual_traffic)
def test_synchronize_sync_bot_with_label(): 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 = [ 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.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_open, (200, {})),
(Requests.ref_create_trusted, (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) 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 = [ 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.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_open, (200, {})),
(Requests.ref_update_trusted, (200, {})), (Requests.ref_update_trusted, (200, {})),
(Requests.deployment_create, (200, {}))
] ]
refs = { refs = {
'refs/pull/23/head': 'HEAD', 'refs/pull/45/head': 'HEAD',
'refs/prs-open/23': 'HEAD~', 'refs/prs-open/45': 'HEAD~',
'refs/prs-trusted-for-preview/23': '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) assert same_members(expected_traffic, actual_traffic)
def test_synchronize_update_member(): def test_synchronize_update_member():
pull_request = {
'number': 45,
'labels': [],
'user': {'login': 'jgraham'},
'author_association': 'MEMBER',
'closed_at': None,
}
expected_traffic = [ 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.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_open, (200, {})),
(Requests.ref_update_trusted, (200, {})) (Requests.ref_update_trusted, (200, {}))
] ]
refs = { refs = {
'refs/pull/23/head': 'HEAD', 'refs/pull/45/head': 'HEAD',
'refs/prs-open/23': 'HEAD~', 'refs/prs-open/45': 'HEAD~',
'refs/prs-trusted-for-preview/23': '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 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
) )
assert returncode == 0 assert not method_threw
assert len(actual_github_traffic) == 0 assert same_members(expected_traffic, actual_traffic)
assert len(actual_preview_traffic) == 0
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 = [ expected_github_traffic = [
(Requests.get_rate, ( (Requests.get_rate, (
200, 200,
@ -788,99 +529,107 @@ def test_detect_fail_search_throttled():
)) ))
] ]
expected_preview_traffic = [] expected_preview_traffic = []
event = {
'deployment': {
'id': 24601,
'environment': 'wpt-preview-45',
'sha': '3232'
}
}
returncode, actual_github_traffic, actual_preview_traffic = detect( pr_num = 45
event, expected_github_traffic, expected_preview_traffic 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_github_traffic == expected_github_traffic
assert actual_preview_traffic == expected_preview_traffic assert actual_preview_traffic == expected_preview_traffic
def test_detect_success(): def test_deploy_success():
pr_num = 45
revision = 'abcdef123'
expected_github_traffic = [ 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.get_rate, Responses.no_limit),
(Requests.deployment_status_create_pending, (200, {})), (Requests.deployment_status_create_pending, (200, {})),
(Requests.get_rate, Responses.no_limit), (Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_success, (200, {})) (Requests.deployment_status_create_success, (200, {}))
] ]
expected_preview_traffic = [ 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( method_threw, actual_github_traffic, actual_preview_traffic = deploy(
event, expected_github_traffic, expected_preview_traffic 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_github_traffic == expected_github_traffic
assert actual_preview_traffic == expected_preview_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 = [ 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.get_rate, Responses.no_limit),
(Requests.deployment_status_create_pending, (200, {})), (Requests.deployment_status_create_pending, (200, {})),
(Requests.get_rate, Responses.no_limit), (Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_error, (200, {})) (Requests.deployment_status_create_error, (200, {}))
] ]
expected_preview_traffic = [ 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( method_threw, actual_github_traffic, actual_preview_traffic = deploy(
event, expected_github_traffic, expected_preview_traffic pr_num, revision, expected_github_traffic, expected_preview_traffic
) )
assert returncode == 1 assert method_threw
assert expected_github_traffic == actual_github_traffic assert expected_github_traffic == actual_github_traffic
ping_count = len(actual_preview_traffic) ping_count = len(actual_preview_traffic)
assert ping_count > 0 assert ping_count > 0
assert actual_preview_traffic == expected_preview_traffic * ping_count 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 = [ 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.get_rate, Responses.no_limit),
(Requests.deployment_status_create_pending, (200, {})), (Requests.deployment_status_create_pending, (200, {})),
(Requests.get_rate, Responses.no_limit), (Requests.get_rate, Responses.no_limit),
(Requests.deployment_status_create_error, (200, {})) (Requests.deployment_status_create_error, (200, {}))
] ]
expected_preview_traffic = [ 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( method_threw, actual_github_traffic, actual_preview_traffic = deploy(
event, expected_github_traffic, expected_preview_traffic pr_num, revision, expected_github_traffic, expected_preview_traffic
) )
assert returncode == 1 assert method_threw
assert expected_github_traffic == actual_github_traffic assert expected_github_traffic == actual_github_traffic
ping_count = len(actual_preview_traffic) ping_count = len(actual_preview_traffic)
assert ping_count > 0 assert ping_count > 0

View file

@ -54,13 +54,12 @@
}); });
}, "`window.setInterval(null)` throws."); }, "`window.setInterval(null)` throws.");
let policy = window.trustedTypes.createPolicy("default", { createScript: x => "0" });
// After default policy creation string assignment implicitly calls createScript. // After default policy creation string assignment implicitly calls createScript.
test(t => { test(t => {
let policy = window.trustedTypes.createPolicy("default", { createScript: createScriptJS }, true);
setTimeout(INPUTS.SCRIPT); setTimeout(INPUTS.SCRIPT);
setInterval(INPUTS.SCRIPT); setInterval(INPUTS.SCRIPT);
}, "`setTimeout(string)`, `setInterval(string)` via default policy (successful Script transformation)."); }, "`setTimeout(string)`, `setInterval(string)` via default policy (successful Script transformation).");
// After default policy creation null assignment implicitly calls createScript. // After default policy creation null assignment implicitly calls createScript.
test(t => { test(t => {
setTimeout(null); 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 \ -primaryURL $wpt_test_http_origin/web-bundle/resources/wbn/resource.js \
-dir nested/ \ -dir nested/ \
-o wbn/nested-main.wbn -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'); assert_equals(module.result, 'resource1 from network');
}, 'Subresource URL must be same-origin with bundle URL'); }, '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 () => { promise_test(async () => {
const wbn_url = 'http://web-platform.test:8001/web-bundle/resources/wbn/subresource.wbn?test-resources-update'; 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'; const resource_url = 'http://web-platform.test:8001/web-bundle/resources/wbn/submodule.js';