Update web-platform-tests to revision b704e37ec97fe90b3a3d59c10f78c21907b5b576

This commit is contained in:
WPT Sync Bot 2018-10-30 21:33:00 -04:00
parent cc0ac89e1a
commit 9f516d3717
70 changed files with 1688 additions and 806 deletions

View file

@ -1,4 +1,5 @@
[url-in-tags-revoke.window.html]
expected: TIMEOUT
[Fetching a blob URL immediately before revoking it works in an iframe.]
expected: FAIL
@ -14,3 +15,6 @@
[Opening a blob URL in a new window by clicking an <a> tag works immediately before revoking the URL.]
expected: FAIL
[Fetching a blob URL immediately before revoking it works in <script> tags.]
expected: TIMEOUT

View file

@ -14,6 +14,9 @@
[Revoke blob URL after creating Request, will fetch]
expected: FAIL
[Revoke blob URL after calling fetch, fetch should succeed]
expected: FAIL
[url-with-fetch.any.html]
[Untitled]
@ -34,3 +37,6 @@
[Revoke blob URL after creating Request, will fetch]
expected: FAIL
[Revoke blob URL after calling fetch, fetch should succeed]
expected: FAIL

View file

@ -147133,6 +147133,18 @@
{}
]
],
"css/css-transforms/text-perspective-001.html": [
[
"/css/css-transforms/text-perspective-001.html",
[
[
"about:blank",
"!="
]
],
{}
]
],
"css/css-transforms/transform-3d-rotateY-stair-above-001.xht": [
[
"/css/css-transforms/transform-3d-rotateY-stair-above-001.xht",
@ -188569,6 +188581,18 @@
{}
]
],
"html/rendering/replaced-elements/the-select-element/select-1-block-size.html": [
[
"/html/rendering/replaced-elements/the-select-element/select-1-block-size.html",
[
[
"/html/rendering/replaced-elements/the-select-element/select-1-block-size-ref.html",
"=="
]
],
{}
]
],
"html/rendering/replaced-elements/the-select-element/select-1-line-height.html": [
[
"/html/rendering/replaced-elements/the-select-element/select-1-line-height.html",
@ -188581,6 +188605,18 @@
{}
]
],
"html/rendering/replaced-elements/the-select-element/select-empty.html": [
[
"/html/rendering/replaced-elements/the-select-element/select-empty.html",
[
[
"/html/rendering/replaced-elements/the-select-element/select-empty-ref.html",
"=="
]
],
{}
]
],
"html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change.html": [
[
"/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change.html",
@ -197254,6 +197290,11 @@
{}
]
],
"IndexedDB/key-generators/reading-autoincrement-common.js": [
[
{}
]
],
"IndexedDB/nested-cloning-common.js": [
[
{}
@ -280139,6 +280180,11 @@
{}
]
],
"css/vendor-imports/mozilla/mozilla-central-reftests/values3/support/blue-32x32.png": [
[
{}
]
],
"css/vendor-imports/mozilla/mozilla-central-reftests/variables/reftest.list": [
[
{}
@ -291834,11 +291880,21 @@
{}
]
],
"html/rendering/replaced-elements/the-select-element/select-1-block-size-ref.html": [
[
{}
]
],
"html/rendering/replaced-elements/the-select-element/select-1-line-height-ref.html": [
[
{}
]
],
"html/rendering/replaced-elements/the-select-element/select-empty-ref.html": [
[
{}
]
],
"html/rendering/replaced-elements/tools/gen-svgsizing-tests.py": [
[
{}
@ -316839,6 +316895,11 @@
{}
]
],
"webdriver/tests/support/fixtures.py": [
[
{}
]
],
"webdriver/tests/support/helpers.py": [
[
{}
@ -316849,6 +316910,11 @@
{}
]
],
"webdriver/tests/support/image.py": [
[
{}
]
],
"webdriver/tests/support/inline.py": [
[
{}
@ -326161,6 +326227,16 @@
{}
]
],
"IndexedDB/get-databases.any.js": [
[
"/IndexedDB/get-databases.any.html",
{}
],
[
"/IndexedDB/get-databases.any.worker.html",
{}
]
],
"IndexedDB/globalscope-indexedDB-SameObject.html": [
[
"/IndexedDB/globalscope-indexedDB-SameObject.html",
@ -327949,6 +328025,78 @@
}
]
],
"IndexedDB/key-generators/reading-autoincrement-indexes-cursors.any.js": [
[
"/IndexedDB/key-generators/reading-autoincrement-indexes-cursors.any.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-indexes-cursors.any.serviceworker.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-indexes-cursors.any.sharedworker.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-indexes-cursors.any.worker.html",
{}
]
],
"IndexedDB/key-generators/reading-autoincrement-indexes.any.js": [
[
"/IndexedDB/key-generators/reading-autoincrement-indexes.any.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-indexes.any.serviceworker.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-indexes.any.sharedworker.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-indexes.any.worker.html",
{}
]
],
"IndexedDB/key-generators/reading-autoincrement-store-cursors.any.js": [
[
"/IndexedDB/key-generators/reading-autoincrement-store-cursors.any.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-store-cursors.any.serviceworker.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-store-cursors.any.sharedworker.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-store-cursors.any.worker.html",
{}
]
],
"IndexedDB/key-generators/reading-autoincrement-store.any.js": [
[
"/IndexedDB/key-generators/reading-autoincrement-store.any.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-store.any.serviceworker.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-store.any.sharedworker.html",
{}
],
[
"/IndexedDB/key-generators/reading-autoincrement-store.any.worker.html",
{}
]
],
"IndexedDB/key_invalid.htm": [
[
"/IndexedDB/key_invalid.htm",
@ -400575,6 +400723,12 @@
{}
]
],
"service-workers/service-worker/postmessage-to-client-message-queue.https.html": [
[
"/service-workers/service-worker/postmessage-to-client-message-queue.https.html",
{}
]
],
"service-workers/service-worker/postmessage-to-client.https.html": [
[
"/service-workers/service-worker/postmessage-to-client.https.html",
@ -436035,7 +436189,7 @@
"support"
],
".travis.yml": [
"b14cfafbdf7a3b4380e746e939d1512ef55785ea",
"9d3431bc444a2da1dc674ef6673f692742191ad6",
"support"
],
".well-known/README.md": [
@ -440750,6 +440904,10 @@
"5db452ebafe68a095f083b65a713ba3e0ad40cf5",
"testharness"
],
"IndexedDB/get-databases.any.js": [
"f054e0fec2f0543eae7978303ae2a24858bdf245",
"testharness"
],
"IndexedDB/globalscope-indexedDB-SameObject.html": [
"7263aaaace5331310558e3f80b8509ac8246ed69",
"testharness"
@ -441343,7 +441501,7 @@
"testharness"
],
"IndexedDB/idbindex-rename-abort.html": [
"b14d30122610bc7025a478c146fbac42128f7428",
"b61988e9b5c764b5b72b4937b8d76fd3aed15e67",
"testharness"
],
"IndexedDB/idbindex-rename-errors.html": [
@ -441523,7 +441681,7 @@
"testharness"
],
"IndexedDB/idbobjectstore-rename-abort.html": [
"6e16c31e339f731e218044ee9320edba3d965661",
"75893cd84c012d4bc662ba11fd79ef1eacaf49cc",
"testharness"
],
"IndexedDB/idbobjectstore-rename-errors.html": [
@ -441926,6 +442084,26 @@
"4dd5d9005b6cb6e4c2c7dccaef62804bfcdb73ec",
"testharness"
],
"IndexedDB/key-generators/reading-autoincrement-common.js": [
"45c8ffef923870909cb9b0e2af128081e5bce41a",
"support"
],
"IndexedDB/key-generators/reading-autoincrement-indexes-cursors.any.js": [
"d7938768fa152c20b4b58538350a51b78b38e445",
"testharness"
],
"IndexedDB/key-generators/reading-autoincrement-indexes.any.js": [
"d945b78bf3743bccd632dabdcb846af52c183110",
"testharness"
],
"IndexedDB/key-generators/reading-autoincrement-store-cursors.any.js": [
"05971098fa893ebe58de4b2ad05d84a4965b6e0d",
"testharness"
],
"IndexedDB/key-generators/reading-autoincrement-store.any.js": [
"bbba6a335139d5b2946b5f6d71a635fd5ee014c3",
"testharness"
],
"IndexedDB/key_invalid.htm": [
"30759d5ef3e594852990808621ce75c840d0ac39",
"testharness"
@ -442035,7 +442213,7 @@
"testharness"
],
"IndexedDB/support-promises.js": [
"21e2163ec60eb3eee05269b83f583f77fad780eb",
"433af5092701d617e2964f7d1ea3ea089b50c441",
"support"
],
"IndexedDB/support.js": [
@ -442131,7 +442309,7 @@
"support"
],
"WebCryptoAPI/META.yml": [
"ec95611b3fed980e2897a5fe2bedc6bb7fa2913d",
"8f27e484996c51eb9ebbbd96902fa1f885af7be0",
"support"
],
"WebCryptoAPI/README.md": [
@ -445311,7 +445489,7 @@
"manual"
],
"annotation-vocab/META.yml": [
"030df88318ccaec889feaee3e25134f5504a678d",
"f99f921d352cbffa724fb0fa031eb60d2ceb2b83",
"support"
],
"annotation-vocab/tools/.gitignore": [
@ -549075,7 +549253,7 @@
"manual"
],
"css/css-grid/META.yml": [
"f8482e16f983ca24e3333ae8989277f82b297ad1",
"18c574ac68bac7e5b95085d96c49347d5eb36814",
"support"
],
"css/css-grid/README.md": [
@ -555963,7 +556141,7 @@
"testharness"
],
"css/css-properties-values-api/registered-property-computation.html": [
"180cdf601edd4a82398d29239141bf38e55bd0c1",
"2525e43ef51e1e5af94426346e51a68b87fea055",
"testharness"
],
"css/css-properties-values-api/registered-property-cssom.html": [
@ -555971,15 +556149,15 @@
"testharness"
],
"css/css-properties-values-api/registered-property-initial.html": [
"24543d5c5fdc84706a28502d8cf8c2c7f925978e",
"77aa9cd11a3eabfd154cc98869699e6f24c64c5c",
"testharness"
],
"css/css-properties-values-api/resources/utils.js": [
"c4dc3fd5a8d98106395e3e99566f05d6d9a30c0c",
"bef59560f68d84ed0d681bb22e0611ea2466024f",
"support"
],
"css/css-properties-values-api/self-utils.html": [
"530c5f677ad25858a06281a9e526584e57081af5",
"05aa4b2fb03b29feee47177624715230768a44a0",
"testharness"
],
"css/css-properties-values-api/support/alt/alt.css": [
@ -568398,6 +568576,10 @@
"2145190fec4cddb59f18a5ac1d5bfead0ce548fa",
"support"
],
"css/css-transforms/text-perspective-001.html": [
"e98b3e73b36a323a60ad5ef74e090ff193ad41b9",
"reftest"
],
"css/css-transforms/transform-2d-getComputedStyle-001.html": [
"a085b794fc4dbd01abd8cf779b691b541d0bf8e3",
"testharness"
@ -584891,7 +585073,7 @@
"testharness"
],
"css/geometry/META.yml": [
"9e3a7979ed80210ccba78da767526aad875e00c2",
"98a2ae9860b2d682220dbf21eb2f3606ca68ff61",
"support"
],
"css/geometry/WebKitCSSMatrix.html": [
@ -595359,19 +595541,19 @@
"reftest"
],
"css/vendor-imports/mozilla/mozilla-central-reftests/values3/calc-background-position-1-ref.html": [
"a9d8056ccab98b795824ab62f7ca86175bc46c39",
"b64aa46bf2eeddfc89791c13f1cea946faefece5",
"support"
],
"css/vendor-imports/mozilla/mozilla-central-reftests/values3/calc-background-position-1.html": [
"2a8bfdf65b0d08eef12775c1abe0698615ddbb7d",
"68351aef6e57dff3f065bad14630e81db171c0a6",
"reftest"
],
"css/vendor-imports/mozilla/mozilla-central-reftests/values3/calc-background-size-1-ref.html": [
"16b89c1d805078574f3557b3c7791cb2ebef3d21",
"5385c46ef52592fa4535608ee09be865bff719ef",
"support"
],
"css/vendor-imports/mozilla/mozilla-central-reftests/values3/calc-background-size-1.html": [
"e91f8c26ce1ec937f5225eb570b104033edfc26a",
"f52ceeeabe16a0011beb170d3bcc75852ae4e0cb",
"reftest"
],
"css/vendor-imports/mozilla/mozilla-central-reftests/values3/calc-border-radius-1-ref.html": [
@ -595570,6 +595752,10 @@
"0f236cb3df999e9fb7d90ebf5ad05e97b3c269a1",
"reftest"
],
"css/vendor-imports/mozilla/mozilla-central-reftests/values3/support/blue-32x32.png": [
"deefd19b2ac53bef91c82ed2f6f4ea5f53a9e34f",
"support"
],
"css/vendor-imports/mozilla/mozilla-central-reftests/variables/reftest.list": [
"baa90e4e9000c2df1f6bc487e25ecdf999a99cf5",
"support"
@ -608659,7 +608845,7 @@
"testharness"
],
"html/dom/elements/the-innertext-idl-attribute/getter-tests.js": [
"5139ec97b05ce6d3cd9b84a20770065e9f81adb0",
"4dd2b6be20619cde5f244ca76d239aa9bbc50044",
"support"
],
"html/dom/elements/the-innertext-idl-attribute/getter.html": [
@ -614194,6 +614380,14 @@
"3b8d992cc2a07dfd902dbb1011e2c348e862baaa",
"reftest"
],
"html/rendering/replaced-elements/the-select-element/select-1-block-size-ref.html": [
"3e437494c0ad8dadd4d58b630194678753516fde",
"support"
],
"html/rendering/replaced-elements/the-select-element/select-1-block-size.html": [
"4aecc596ce693a6cff3f6ffd5dbac1fa4911dfe8",
"reftest"
],
"html/rendering/replaced-elements/the-select-element/select-1-line-height-ref.html": [
"26e5f33282192b10f56cd66fefeda2c1b9d9b389",
"support"
@ -614202,6 +614396,14 @@
"e6383f089ffa5550005f99257ecac7984dd09110",
"reftest"
],
"html/rendering/replaced-elements/the-select-element/select-empty-ref.html": [
"31ba23a5cf86f161b1204ea3f4c9fef4585af909",
"support"
],
"html/rendering/replaced-elements/the-select-element/select-empty.html": [
"6568a6de34985a7fc201ce0bd530b86b145083e8",
"reftest"
],
"html/rendering/replaced-elements/tools/gen-svgsizing-tests.py": [
"5ba69f8ab5ba0a74810c570d9a4f3d7ddd90a3ba",
"support"
@ -636931,7 +637133,7 @@
"testharness"
],
"pointerevents/META.yml": [
"ef94157ef9a9df762f940f1c6b04128dd69f4f3b",
"be61ddddaf62acb3396ada6779ad3f50e6caa709",
"support"
],
"pointerevents/README.md": [
@ -649762,8 +649964,12 @@
"29c056080c79d581975e0929dcebd84d63b86a20",
"testharness"
],
"service-workers/service-worker/postmessage-to-client-message-queue.https.html": [
"caa4f9445d3d8ad0ab92ba73713da17a33af185c",
"testharness"
],
"service-workers/service-worker/postmessage-to-client.https.html": [
"b1dc41a018f273832f4ac90c17b4b981f095b0ef",
"15d2e889337078869e3c3e97d312649e6a8bd8b2",
"testharness"
],
"service-workers/service-worker/postmessage.https.html": [
@ -651987,7 +652193,7 @@
"testharness"
],
"speech-api/META.yml": [
"17263d91d367f5571ac7df3616aa026bdb4f6433",
"ac4b89b03490dbb46c257a2e5fdec3a741acdd52",
"support"
],
"speech-api/SpeechRecognition-abort-manual.https.html": [
@ -653319,7 +653525,7 @@
"testharness"
],
"subresource-integrity/META.yml": [
"740ad1412f671ad86d88098ae437a817cbd620d5",
"2b8891ec6b498cc6b24df46e21fffad437732b54",
"support"
],
"subresource-integrity/alternate.css": [
@ -661615,7 +661821,7 @@
"support"
],
"tools/wpt/run.py": [
"a2cd1c0b115dff81a4cc0f356eb351fab51998f1",
"b977d8a0cd68ccb00b299c7840415f843c16f949",
"support"
],
"tools/wpt/testfiles.py": [
@ -661643,7 +661849,7 @@
"support"
],
"tools/wpt/utils.py": [
"1e622a63859ac5da0d1aa0015a971083b23adbc9",
"fa60230389deb56686f6753171482b69ac28983c",
"support"
],
"tools/wpt/virtualenv.py": [
@ -662111,7 +662317,7 @@
"support"
],
"tools/wptrunner/wptrunner/testrunner.py": [
"2b7c091c84722c750b46d6143c74c8d329a7fd99",
"e819f242e2df532d6e9c62e9292e1f563047c148",
"support"
],
"tools/wptrunner/wptrunner/tests/__init__.py": [
@ -662255,7 +662461,7 @@
"support"
],
"tools/wptrunner/wptrunner/wptrunner.py": [
"0a5b1352e212fcb702f1824293a774ea48d78ef1",
"d0c1106b0f9bb1f2aa727e263cd1e74ab1b04c01",
"support"
],
"tools/wptrunner/wptrunner/wpttest.py": [
@ -663307,7 +663513,7 @@
"testharness"
],
"url/META.yml": [
"459152f6f078042714905b3e1d2eaba1c415807b",
"3a789a0d513f0a78bf362dd73c8a807d0cd55a50",
"support"
],
"url/README.md": [
@ -665667,7 +665873,7 @@
"support"
],
"webaudio/META.yml": [
"37276da53345515f79453de8c5ade6037ff6f9eb",
"e8f8cc59489e381dba1fd35e9d088a6de86a3783",
"support"
],
"webaudio/README.md": [
@ -666755,7 +666961,7 @@
"wdspec"
],
"webdriver/tests/conftest.py": [
"d16883256aca4f600400f060719d0a7ac8d16dfd",
"42b82c9ecf29a1aed48bfa959c20aba6edd1319e",
"support"
],
"webdriver/tests/delete_all_cookies/__init__.py": [
@ -666819,7 +667025,7 @@
"wdspec"
],
"webdriver/tests/element_click/center_point.py": [
"b3d2ea7a1098266530cb6e835861dcc7336b7091",
"21bf8f31fd8dd1c961008ab74330956dd11e14f2",
"wdspec"
],
"webdriver/tests/element_click/click.py": [
@ -667475,23 +667681,31 @@
"support"
],
"webdriver/tests/support/asserts.py": [
"cdf313bc04b3e96e999b559de506b5595efe8419",
"1dea6ce68779269b005061a391f7553a6741157f",
"support"
],
"webdriver/tests/support/defaults.py": [
"c2020527a6f38a628b2c1a4199caaff46cf0c36b",
"support"
],
"webdriver/tests/support/fixtures.py": [
"d16883256aca4f600400f060719d0a7ac8d16dfd",
"support"
],
"webdriver/tests/support/helpers.py": [
"b2db3e7b0d338f1325214cd8019b7cac613dd8b1",
"5dd7a323d8cc1a166acdb1b6445015b8b19d8c76",
"support"
],
"webdriver/tests/support/http_request.py": [
"5e46d97017e14ba009d4d1ed6d62bb5012f48d15",
"support"
],
"webdriver/tests/support/image.py": [
"1eb3a187a43b86ffe752d4d73f6e639bdd9e96da",
"support"
],
"webdriver/tests/support/inline.py": [
"3bf56c84bedb47e024a88983fa15c232ddba7899",
"8b4f1657cffb193511da346bc4c236aac1d70308",
"support"
],
"webdriver/tests/support/merge_dictionaries.py": [
@ -675751,7 +675965,7 @@
"testharness"
],
"xhr/send-content-type-charset.htm": [
"4e75df234682c91b7effa0de52f5f95a3ac438a4",
"0a91e1fbd7e061df3123163e049cc8aab90a67e2",
"testharness"
],
"xhr/send-content-type-string.htm": [

View file

@ -53,9 +53,6 @@
[Matching font-weight: '500' should prefer '501 550' over '502 560']
expected: FAIL
[Matching font-weight: '501' should prefer '501' over '502 510']
expected: FAIL
[Matching font-weight: '501' should prefer '502 510' over '503 520']
expected: FAIL
@ -137,9 +134,6 @@
[Matching font-style: 'oblique 21deg' should prefer 'oblique -50deg -20deg' over 'oblique -40deg -30deg']
expected: FAIL
[Matching font-style: 'oblique 10deg' should prefer 'oblique 10deg' over 'oblique 5deg']
expected: FAIL
[Matching font-style: 'oblique 10deg' should prefer 'oblique 5deg' over 'oblique 15deg 20deg']
expected: FAIL
@ -203,9 +197,6 @@
[Matching font-style: 'oblique -10deg' should prefer 'oblique 0deg 10deg' over 'oblique 40deg 50deg']
expected: FAIL
[Matching font-style: 'oblique -20deg' should prefer 'oblique -20deg' over 'oblique -60deg -40deg']
expected: FAIL
[Matching font-style: 'oblique -20deg' should prefer 'oblique -10deg' over 'italic']
expected: FAIL
@ -332,3 +323,6 @@
[Matching font-weight: '399' should prefer '350 399' over '340 360']
expected: FAIL
[Matching font-weight: '500' should prefer '450 460' over '400']
expected: FAIL

View file

@ -71,51 +71,9 @@
[text-indent intermediate]
expected: FAIL
[clip end]
[opacity end]
expected: FAIL
[border-top-width end]
expected: FAIL
[border-left-width end]
expected: FAIL
[font-size end]
expected: FAIL
[vertical-align end]
expected: FAIL
[border-right-color end]
expected: FAIL
[margin-right end]
expected: FAIL
[max-width end]
expected: FAIL
[height end]
expected: FAIL
[bottom end]
expected: FAIL
[top end]
expected: FAIL
[font-weight end]
expected: FAIL
[border-left-color end]
expected: FAIL
[margin-bottom end]
expected: FAIL
[border-right-width end]
expected: FAIL
[padding-bottom intermediate]
[z-index end]
expected: FAIL

View file

@ -2,7 +2,7 @@
type: testharness
[single-byte-decoder.html?document]
expected: TIMEOUT
expected: CRASH
[windows-1254: iso_8859-9 (document.characterSet and document.inputEncoding)]
expected: FAIL
@ -509,6 +509,7 @@
[single-byte-decoder.html?XMLHttpRequest]
expected: CRASH
[ISO-8859-2: iso_8859-2:1987 (XMLHttpRequest)]
expected: FAIL

View file

@ -1,5 +0,0 @@
[beforeunload-canceling.html]
expected: TIMEOUT
[Returning 0 with a real iframe unloading; setting returnValue to foo]
expected: TIMEOUT

View file

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

View file

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

View file

@ -24,9 +24,6 @@
[Set HTTP URL frame location.protocol to gopher]
expected: FAIL
[Set HTTP URL frame location.protocol to x]
expected: FAIL
[Set HTTP URL frame location.protocol to http+x]
expected: FAIL

View file

@ -266,3 +266,15 @@
[Whitespace around <img> should not be collapsed ("<div>abc <img width=1 height=1> def")]
expected: FAIL
[text-transform handles Turkish casing ("<div><div lang='tr' style='text-transform:uppercase'>i ı")]
expected: FAIL
[display:table-row on the element itself ("<div style='display:table-row'>")]
expected: FAIL
[display:table-cell on the element itself ("<div style='display:table-cell'>")]
expected: FAIL
[<br> ("<br>")]
expected: FAIL

View file

@ -0,0 +1,2 @@
[select-1-block-size.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[select-empty.html]
expected: FAIL

View file

@ -5,5 +5,5 @@
expected: NOTRUN
[calling play() on a sufficiently long video should trigger timeupdate event]
expected: NOTRUN
expected: FAIL

View file

@ -0,0 +1,7 @@
[toggleEvent.html]
[Calling open twice on 'details' fires only one toggle event]
expected: FAIL
[Setting open=true to opened 'details' element should not fire a toggle event at the 'details' element]
expected: FAIL

View file

@ -0,0 +1,2 @@
[script-onerror-insertion-point-2.html]
expected: TIMEOUT

View file

@ -2,6 +2,3 @@
[document.open() after parser is aborted]
expected: FAIL
[async document.open() after parser is aborted]
expected: FAIL

View file

@ -2,10 +2,16 @@
[request.formData() with input: &&&a=b&&&&c=d&]
expected: FAIL
[response.formData() with input: a&b&c]
[request.formData() with input: a&b&c]
expected: FAIL
[request.formData() with input: a&b&c]
[request.formData() with input: _charset_=windows-1252&test=%C2x]
expected: FAIL
[response.formData() with input: _charset_=windows-1252&test=%C2x]
expected: FAIL
[request.formData() with input: a=b&c=d&]
expected: FAIL
@ -25,6 +31,6 @@
[request.formData() with input: &&&a=b&&&&c=d&]
expected: FAIL
[response.formData() with input: _charset_=windows-1252&test=%C2x]
[request.formData() with input: a=b&c=d&]
expected: FAIL

View file

@ -1,4 +1,5 @@
[import-in-moduleworker.html]
expected: ERROR
[Base URL in module dedicated workers: import]
expected: FAIL

View file

@ -45,3 +45,21 @@
[unknown parameters need to be preserved]
expected: FAIL
[charset with space that is UTF-8 does not change]
expected: FAIL
[charset in double quotes with backslashes that is UTF-8 does not change]
expected: FAIL
[If charset= param is UTF-8 (case-insensitive), it should not be changed (bogus charset)]
expected: FAIL
[Multiple non-UTF-8 charset parameters deduplicate, bogus parameter dropped]
expected: FAIL
[If charset= param is UTF-8 (case-insensitive), it should not be changed]
expected: FAIL
[charset in double quotes that is UTF-8 does not change]
expected: FAIL

View file

@ -0,0 +1,2 @@
[transition_calc_implicit.html]
expected: TIMEOUT

View file

@ -1,2 +0,0 @@
[unitless-length.html]
expected: TIMEOUT

View file

@ -74,7 +74,7 @@ matrix:
os: linux
python: "3.6"
env: JOB=tools_unittest TOXENV=py36 HYPOTHESIS_PROFILE=ci SCRIPT=tools/ci/ci_tools_unittest.sh
- name: "tools/wpt/ unittests"
- name: "tools/wpt/ tests"
if: type = pull_request
os: linux
python: "2.7"

View file

@ -0,0 +1,62 @@
// META: script=support-promises.js
promise_test(async testCase => {
// Delete any databases that may not have been cleaned up after
// previous test runs.
await deleteAllDatabases(testCase);
const db_name = "TestDatabase";
const db = await createNamedDatabase(testCase, db_name, ()=>{});
const databases_promise = await indexedDB.databases();
const expected_result = [
{"name": db_name, "version": 1},
];
assert_object_equals(
databases_promise,
expected_result,
"Call to databases() did not retrieve the single expected result.");
}, "Enumerate one database.");
promise_test(async testCase => {
// Delete any databases that may not have been cleaned up after previous test
// runs.
await deleteAllDatabases(testCase);
const db_name1 = "TestDatabase1";
const db_name2 = "TestDatabase2";
const db_name3 = "TestDatabase3";
const db1 = await createNamedDatabase(testCase, db_name1, ()=>{});
const db2 = await createNamedDatabase(testCase, db_name2, ()=>{});
const db3 = await createNamedDatabase(testCase, db_name3, ()=>{});
const databases_promise = await indexedDB.databases();
const expected_result = [
{"name": db_name1, "version": 1},
{"name": db_name2, "version": 1},
{"name": db_name3, "version": 1},
];
assert_object_equals(
databases_promise,
expected_result,
"Call to databases() did not retrieve the multiple expected results");
}, "Enumerate multiple databases.");
promise_test(async testCase => {
// Add some databases and close their connections.
const db1 = await createNamedDatabase(testCase, "DB1", ()=>{});
const db2 = await createNamedDatabase(testCase, "DB2", ()=>{});
db1.onversionchange = () => { db1.close() };
db2.onversionchange = () => { db2.close() };
// Delete any databases that may not have been cleaned up after previous test
// runs as well as the two databases made above.
await deleteAllDatabases(testCase);
// Make sure the databases are no longer returned.
const databases_promise = await indexedDB.databases();
assert_object_equals(
databases_promise,
[],
"Call to databases() found database it should not have.")
}, "Make sure an empty list is returned for the case of no databases.");
done();

View file

@ -39,9 +39,8 @@ promise_test(testCase => {
'versionchange transaction is aborted');
const request = indexedDB.open(dbName, 1);
return requestWatcher(testCase, request).wait_for('success');
}).then(event => {
const database = event.target.result;
return promiseForRequest(testCase, request);
}).then(database => {
const transaction = database.transaction('books', 'readonly');
const store = transaction.objectStore('books');
assert_array_equals(
@ -95,9 +94,8 @@ promise_test(testCase => {
'versionchange transaction is aborted');
const request = indexedDB.open(dbName, 1);
return requestWatcher(testCase, request).wait_for('success');
}).then(event => {
const database = event.target.result;
return promiseForRequest(testCase, request);
}).then(database => {
const transaction = database.transaction('not_books', 'readonly');
const store = transaction.objectStore('not_books');
assert_array_equals(

View file

@ -40,9 +40,8 @@ promise_test(testCase => {
'IDBObjectStore.name should not reflect the rename any more ' +
'after the versionchange transaction is aborted');
const request = indexedDB.open(dbName, 1);
return requestWatcher(testCase, request).wait_for('success');
}).then(event => {
const database = event.target.result;
return promiseForRequest(testCase, request);
}).then(database => {
assert_array_equals(
database.objectStoreNames, ['books'],
'IDBDatabase.objectStoreNames should not reflect the rename ' +
@ -107,9 +106,8 @@ promise_test(testCase => {
'should be empty after the versionchange transaction is aborted ' +
'returns');
const request = indexedDB.open(dbName, 1);
return requestWatcher(testCase, request).wait_for('success');
}).then(event => {
const database = event.target.result;
return promiseForRequest(testCase, request);
}).then(database => {
assert_array_equals(
database.objectStoreNames, [],
'IDBDatabase.objectStoreNames should not reflect the creation or ' +

View file

@ -0,0 +1,93 @@
// Returns the "name" property written to the object with the given ID.
function nameForId(id) {
return `Object ${id}`;
}
// Initial database setup used by all the reading-autoincrement tests.
async function setupAutoincrementDatabase(testCase) {
const database = await createDatabase(testCase, database => {
const store = database.createObjectStore(
'store', { autoIncrement: true, keyPath: 'id' });
store.createIndex('by_name', 'name', { unique: true });
store.createIndex('by_id', 'id', { unique: true });
// Cover writing from the initial upgrade transaction.
for (let i = 1; i <= 16; ++i) {
if (i % 2 == 0) {
store.put({name: nameForId(i), id: i});
} else {
store.put({name: nameForId(i)});
}
}
});
// Cover writing from a subsequent transaction.
const transaction = database.transaction(['store'], 'readwrite');
const store = transaction.objectStore('store');
for (let i = 17; i <= 32; ++i) {
if (i % 2 == 0) {
store.put({name: nameForId(i), id: i});
} else {
store.put({name: nameForId(i)});
}
}
await promiseForTransaction(testCase, transaction);
return database;
}
// Returns the IDs used by the object store, sorted as strings.
//
// This is used to determine the correct order of records when retrieved from an
// index that uses stringified IDs.
function idsSortedByStringCompare() {
const stringIds = [];
for (let i = 1; i <= 32; ++i)
stringIds.push(i);
stringIds.sort((a, b) => indexedDB.cmp(`${a}`, `${b}`));
return stringIds;
}
async function iterateCursor(testCase, cursorRequest, callback) {
// This uses requestWatcher() directly instead of using promiseForRequest()
// inside the loop to avoid creating multiple EventWatcher instances. In turn,
// this avoids ending up with O(N) listeners for the request and O(N^2)
// dispatched events.
const eventWatcher = requestWatcher(testCase, cursorRequest);
while (true) {
const event = await eventWatcher.wait_for('success');
const cursor = event.target.result;
if (cursor === null)
return;
callback(cursor);
cursor.continue();
}
}
// Returns equivalent information to getAllKeys() by iterating a cursor.
//
// Returns an array with one dictionary per entry in the source. The dictionary
// has the properties "key" and "primaryKey".
async function getAllKeysViaCursor(testCase, cursorSource) {
const results = [];
await iterateCursor(testCase, cursorSource.openKeyCursor(), cursor => {
results.push({ key: cursor.key, primaryKey: cursor.primaryKey });
});
return results;
}
// Returns equivalent information to getAll() by iterating a cursor.
//
// Returns an array with one dictionary per entry in the source. The dictionary
// has the properties "key", "primaryKey" and "value".
async function getAllViaCursor(testCase, cursorSource) {
const results = [];
await iterateCursor(testCase, cursorSource.openCursor(), cursor => {
results.push({
key: cursor.key,
primaryKey: cursor.primaryKey,
value: cursor.value,
});
});
return results;
}

View file

@ -0,0 +1,88 @@
// META: global=window,dedicatedworker,sharedworker,serviceworker
// META: script=../support-promises.js
// META: script=./reading-autoincrement-common.js
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_id');
const result = await getAllViaCursor(testCase, index);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].key, i, 'Autoincrement index key');
assert_equals(result[i - 1].primaryKey, i, 'Autoincrement primary key');
assert_equals(result[i - 1].value.id, i, 'Autoincrement key in value');
assert_equals(result[i - 1].value.name, nameForId(i),
'String property in value');
}
database.close();
}, 'IDBIndex.openCursor() iterates over an index on the autoincrement key');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_id');
const result = await getAllKeysViaCursor(testCase, index);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].key, i, 'Autoincrement index key');
assert_equals(result[i - 1].primaryKey, i, 'Autoincrement primary key');
}
database.close();
}, 'IDBIndex.openKeyCursor() iterates over an index on the autoincrement key');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_name');
const stringSortedIds = idsSortedByStringCompare();
const result = await getAllViaCursor(testCase, index);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].key, nameForId(stringSortedIds[i - 1]),
'Index key');
assert_equals(result[i - 1].primaryKey, stringSortedIds[i - 1],
'Autoincrement primary key');
assert_equals(result[i - 1].value.id, stringSortedIds[i - 1],
'Autoincrement key in value');
assert_equals(result[i - 1].value.name, nameForId(stringSortedIds[i - 1]),
'String property in value');
}
database.close();
}, 'IDBIndex.openCursor() iterates over an index not covering the ' +
'autoincrement key');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_name');
const stringSortedIds = idsSortedByStringCompare();
const result = await getAllKeysViaCursor(testCase, index);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].key, nameForId(stringSortedIds[i - 1]),
'Index key');
assert_equals(result[i - 1].primaryKey, stringSortedIds[i - 1],
'Autoincrement primary key');
}
database.close();
}, 'IDBIndex.openKeyCursor() iterates over an index not covering the ' +
'autoincrement key');

View file

@ -0,0 +1,108 @@
// META: global=window,dedicatedworker,sharedworker,serviceworker
// META: script=../support-promises.js
// META: script=./reading-autoincrement-common.js
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_id');
const request = index.getAll();
const result = await promiseForRequest(testCase, request);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].id, i, 'Autoincrement key');
assert_equals(result[i - 1].name, nameForId(i), 'String property');
}
database.close();
}, 'IDBIndex.getAll() for an index on the autoincrement key');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_id');
const request = index.getAllKeys();
const result = await promiseForRequest(testCase, request);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i)
assert_equals(result[i - 1], i, 'Autoincrement key');
database.close();
}, 'IDBIndex.getAllKeys() for an index on the autoincrement key');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_id');
for (let i = 1; i <= 32; ++i) {
const request = index.get(i);
const result = await promiseForRequest(testCase, request);
assert_equals(result.id, i, 'autoincrement key');
assert_equals(result.name, nameForId(i), 'string property');
}
database.close();
}, 'IDBIndex.get() for an index on the autoincrement key');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const stringSortedIds = idsSortedByStringCompare();
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_name');
const request = index.getAll();
const result = await promiseForRequest(testCase, request);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].id, stringSortedIds[i - 1],
'autoincrement key');
assert_equals(result[i - 1].name, nameForId(stringSortedIds[i - 1]),
'string property');
}
database.close();
}, 'IDBIndex.getAll() for an index not covering the autoincrement key');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const stringSortedIds = idsSortedByStringCompare();
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_name');
const request = index.getAllKeys();
const result = await promiseForRequest(testCase, request);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i)
assert_equals(result[i - 1], stringSortedIds[i - 1], 'String property');
database.close();
}, 'IDBIndex.getAllKeys() returns correct result for an index not covering ' +
'the autoincrement key');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const index = store.index('by_name');
for (let i = 1; i <= 32; ++i) {
const request = index.get(nameForId(i));
const result = await promiseForRequest(testCase, request);
assert_equals(result.id, i, 'Autoincrement key');
assert_equals(result.name, nameForId(i), 'String property');
}
database.close();
}, 'IDBIndex.get() for an index not covering the autoincrement key');

View file

@ -0,0 +1,38 @@
// META: global=window,dedicatedworker,sharedworker,serviceworker
// META: script=../support-promises.js
// META: script=./reading-autoincrement-common.js
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const result = await getAllViaCursor(testCase, store);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].key, i, 'Autoincrement key');
assert_equals(result[i - 1].primaryKey, i, 'Autoincrement primary key');
assert_equals(result[i - 1].value.id, i, 'Autoincrement key in value');
assert_equals(result[i - 1].value.name, nameForId(i),
'string property in value');
}
database.close();
}, 'IDBObjectStore.openCursor() iterates over an autoincrement store');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const result = await getAllKeysViaCursor(testCase, store);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].key, i, 'Incorrect autoincrement key');
assert_equals(result[i - 1].primaryKey, i, 'Incorrect primary key');
}
database.close();
}, 'IDBObjectStore.openKeyCursor() iterates over an autoincrement store');

View file

@ -0,0 +1,49 @@
// META: global=window,dedicatedworker,sharedworker,serviceworker
// META: script=../support-promises.js
// META: script=./reading-autoincrement-common.js
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const request = store.getAll();
const result = await promiseForRequest(testCase, request);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i) {
assert_equals(result[i - 1].id, i, 'Autoincrement key');
assert_equals(result[i - 1].name, nameForId(i), 'String property');
}
database.close();
}, 'IDBObjectStore.getAll() for an autoincrement store');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
const request = store.getAllKeys();
const result = await promiseForRequest(testCase, request);
assert_equals(result.length, 32);
for (let i = 1; i <= 32; ++i)
assert_equals(result[i - 1], i, 'Autoincrement key');
database.close();
}, 'IDBObjectStore.getAllKeys() for an autoincrement store');
promise_test(async testCase => {
const database = await setupAutoincrementDatabase(testCase);
const transaction = database.transaction(['store'], 'readonly');
const store = transaction.objectStore('store');
for (let i = 1; i <= 32; ++i) {
const request = store.get(i);
const result = await promiseForRequest(testCase, request);
assert_equals(result.id, i, 'Autoincrement key');
assert_equals(result.name, nameForId(i), 'String property');
}
database.close();
}, 'IDBObjectStore.get() for an autoincrement store');

View file

@ -5,11 +5,39 @@ function databaseName(testCase) {
return 'db' + self.location.pathname + '-' + testCase.name;
}
// Creates an EventWatcher covering all the events that can be issued by
// IndexedDB requests and transactions.
// EventWatcher covering all the events defined on IndexedDB requests.
//
// The events cover IDBRequest and IDBOpenDBRequest.
function requestWatcher(testCase, request) {
return new EventWatcher(testCase, request,
['abort', 'blocked', 'complete', 'error', 'success', 'upgradeneeded']);
['blocked', 'error', 'success', 'upgradeneeded']);
}
// EventWatcher covering all the events defined on IndexedDB transactions.
//
// The events cover IDBTransaction.
function transactionWatcher(testCase, request) {
return new EventWatcher(testCase, request, ['abort', 'complete', 'error']);
}
// Promise that resolves with an IDBRequest's result.
//
// The promise only resolves if IDBRequest receives the "success" event. Any
// other event causes the promise to reject with an error. This is correct in
// most cases, but insufficient for indexedDB.open(), which issues
// "upgradeneded" events under normal operation.
function promiseForRequest(testCase, request) {
const eventWatcher = requestWatcher(testCase, request);
return eventWatcher.wait_for('success').then(event => event.target.result);
}
// Promise that resolves when an IDBTransaction completes.
//
// The promise resolves with undefined if IDBTransaction receives the "complete"
// event, and rejects with an error for any other event.
function promiseForTransaction(testCase, request) {
const eventWatcher = transactionWatcher(testCase, request);
return eventWatcher.wait_for('complete').then(() => {});
}
// Migrates an IndexedDB database whose name is unique for the test case.
@ -64,7 +92,7 @@ function migrateNamedDatabase(
requestEventPromise = new Promise((resolve, reject) => {
request.onerror = event => {
event.preventDefault();
resolve(event);
resolve(event.target.error);
};
request.onsuccess = () => reject(new Error(
'indexedDB.open should not succeed for an aborted ' +
@ -79,8 +107,7 @@ function migrateNamedDatabase(
if (!shouldBeAborted) {
request.onerror = null;
request.onsuccess = null;
requestEventPromise =
requestWatcher(testCase, request).wait_for('success');
requestEventPromise = promiseForRequest(testCase, request);
}
// requestEventPromise needs to be the last promise in the chain, because
@ -95,12 +122,10 @@ function migrateNamedDatabase(
'indexedDB.open should not succeed without creating a ' +
'versionchange transaction'));
};
}).then(event => {
const database = event.target.result;
if (database) {
testCase.add_cleanup(() => { database.close(); });
}
return database || event.target.error;
}).then(databaseOrError => {
if (databaseOrError instanceof IDBDatabase)
testCase.add_cleanup(() => { databaseOrError.close(); });
return databaseOrError;
});
}
@ -126,9 +151,7 @@ function createDatabase(testCase, setupCallback) {
// close the database.
function createNamedDatabase(testCase, databaseName, setupCallback) {
const request = indexedDB.deleteDatabase(databaseName);
const eventWatcher = requestWatcher(testCase, request);
return eventWatcher.wait_for('success').then(event => {
return promiseForRequest(testCase, request).then(() => {
testCase.add_cleanup(() => { indexedDB.deleteDatabase(databaseName); });
return migrateNamedDatabase(testCase, databaseName, 1, setupCallback)
});
@ -152,9 +175,7 @@ function openDatabase(testCase, version) {
// close the database.
function openNamedDatabase(testCase, databaseName, version) {
const request = indexedDB.open(databaseName, version);
const eventWatcher = requestWatcher(testCase, request);
return eventWatcher.wait_for('success').then(() => {
const database = request.result;
return promiseForRequest(testCase, request).then(database => {
testCase.add_cleanup(() => { database.close(); });
return database;
});
@ -215,9 +236,7 @@ function checkStoreIndexes (testCase, store, errorMessage) {
function checkStoreGenerator(testCase, store, expectedKey, errorMessage) {
const request = store.put(
{ title: 'Bedrock Nights ' + expectedKey, author: 'Barney' });
const eventWatcher = requestWatcher(testCase, request);
return eventWatcher.wait_for('success').then(() => {
const result = request.result;
return promiseForRequest(testCase, request).then(result => {
assert_equals(result, expectedKey, errorMessage);
});
}
@ -230,9 +249,7 @@ function checkStoreGenerator(testCase, store, expectedKey, errorMessage) {
// is using it incorrectly.
function checkStoreContents(testCase, store, errorMessage) {
const request = store.get(123456);
const eventWatcher = requestWatcher(testCase, request);
return eventWatcher.wait_for('success').then(() => {
const result = request.result;
return promiseForRequest(testCase, request).then(result => {
assert_equals(result.isbn, BOOKS_RECORD_DATA[0].isbn, errorMessage);
assert_equals(result.author, BOOKS_RECORD_DATA[0].author, errorMessage);
assert_equals(result.title, BOOKS_RECORD_DATA[0].title, errorMessage);
@ -247,9 +264,7 @@ function checkStoreContents(testCase, store, errorMessage) {
// is using it incorrectly.
function checkAuthorIndexContents(testCase, index, errorMessage) {
const request = index.get(BOOKS_RECORD_DATA[2].author);
const eventWatcher = requestWatcher(testCase, request);
return eventWatcher.wait_for('success').then(() => {
const result = request.result;
return promiseForRequest(testCase, request).then(result => {
assert_equals(result.isbn, BOOKS_RECORD_DATA[2].isbn, errorMessage);
assert_equals(result.title, BOOKS_RECORD_DATA[2].title, errorMessage);
});
@ -263,9 +278,7 @@ function checkAuthorIndexContents(testCase, index, errorMessage) {
// is using it incorrectly.
function checkTitleIndexContents(testCase, index, errorMessage) {
const request = index.get(BOOKS_RECORD_DATA[2].title);
const eventWatcher = requestWatcher(testCase, request);
return eventWatcher.wait_for('success').then(() => {
const result = request.result;
return promiseForRequest(testCase, request).then(result => {
assert_equals(result.isbn, BOOKS_RECORD_DATA[2].isbn, errorMessage);
assert_equals(result.author, BOOKS_RECORD_DATA[2].author, errorMessage);
});
@ -290,3 +303,12 @@ function largeValue(size, seed) {
return buffer;
}
async function deleteAllDatabases(testCase) {
const dbs_to_delete = await indexedDB.databases();
for( const db_info of dbs_to_delete) {
let request = indexedDB.deleteDatabase(db_info.name);
let eventWatcher = requestWatcher(testCase, request);
await eventWatcher.wait_for('success');
}
}

View file

@ -1,4 +1,3 @@
spec: https://w3c.github.io/webcrypto/
suggested_reviewers:
- Wafflespeanut
- jimsch

View file

@ -1,4 +1,3 @@
spec: https://www.w3.org/TR/annotation-vocab/
suggested_reviewers:
- halindrome
- gkellogg

View file

@ -1,7 +1,6 @@
spec: https://drafts.csswg.org/css-grid/
suggested_reviewers:
- mrego
- tomalec
- plinss
- jxs
- tabatkins

View file

@ -18,18 +18,18 @@
<script>
for (let element of [divWithFontSizeSet, divWithFontSizeInherited]) {
let id = element.id;
// Generate a property and temporarily set its value. Then call 'fn' with
// the name of the generated property.
function with_custom_property(reg, value, fn) {
function with_custom_property(element, reg, value, fn) {
if (element.id.length == 0)
throw 'The specified element must have an ID';
let name = generate_property(reg);
// Because we want to include the parsing step, insert a stylesheet
// node with textContent.
let node = document.createElement('style');
node.textContent = `#${id} { ${name}:${value}; }`;
node.textContent = `#${element.id} { ${name}:${value}; }`;
document.body.append(node);
try {
@ -39,8 +39,8 @@ for (let element of [divWithFontSizeSet, divWithFontSizeInherited]) {
}
}
function assert_computed_value(syntax, value, expected) {
with_custom_property(syntax, value, (name) => {
function assert_computed_value(element, syntax, value, expected) {
with_custom_property(element, syntax, value, (name) => {
let actual = getComputedStyle(element).getPropertyValue(name);
assert_equals(actual, expected);
});
@ -65,87 +65,100 @@ for (let element of [divWithFontSizeSet, divWithFontSizeInherited]) {
}
}
function test_computed_value(syntax, value, expected) {
test(function() {
assert_computed_value('<length>', '12px', '12px');
assert_computed_value('<length>', '13vw', length_ref('13vw'));
assert_computed_value('<length>', '14em', '140px');
assert_computed_value('<length>', '15vmin', length_ref('15vmin'));
assert_computed_value('<length>', 'calc(16px - 7em + 10vh)', length_ref('calc(10vh - 54px)'));
with_custom_property('<length>', '14em', (name) => {
assert_computed_value('<length>', `var(${name})`, '140px');
});
}, "<length> values are computed correctly for " + id);
test(function() {
assert_computed_value('<length-percentage>', '17em', '170px');
assert_computed_value('<length-percentage>', '18%', '18%');
assert_computed_value('<length-percentage>', 'calc(19em - 2%)', 'calc(190px + -2%)');
}, "<length-percentage> values are computed correctly for " + id);
test(function() {
assert_computed_value('<length>#', '10px, 3em', '10px, 30px');
assert_computed_value('<length>#', '10px, 3em', '10px, 30px');
assert_computed_value('<length>#', '4em ,9px', '40px, 9px');
assert_computed_value('<length>#', '8em', '80px');
}, "<length># values are computed correctly for " + id);
test(function() {
assert_computed_value('<length-percentage>#', '3% , 10vmax , 22px', ['3%', length_ref('10vmax'), '22px'].join(', '));
assert_computed_value('<length-percentage>#', 'calc(50% + 1em), 4px', 'calc(10px + 50%), 4px');
assert_computed_value('<length-percentage>#', 'calc(13% + 37px)', 'calc(37px + 13%)');
}, "<length-percentage># values are computed correctly for " + id);
test(function() {
assert_computed_value('<length>+', '10px 3em', '10px 30px');
assert_computed_value('<length>+', '4em 9px', '40px 9px');
}, "<length>+ values are computed correctly for " + id);
test(function() {
assert_computed_value('<length-percentage>+', '3% 10vmax 22px', ['3%', length_ref('10vmax'), '22px'].join(' '));
assert_computed_value('<length-percentage>+', 'calc(50% + 1em) 4px', 'calc(10px + 50%) 4px');
}, "<length-percentage>+ values are computed correctly for " + id);
test(function() {
assert_computed_value('<transform-function>', 'translateX(2px)', 'translateX(2px)');
assert_computed_value('<transform-function>', 'translateX(10em)', 'translateX(100px)');
assert_computed_value('<transform-function>', 'translateX(calc(11em + 10%))', 'translateX(calc(110px + 10%))');
assert_computed_value('<transform-function>+', 'translateX(10%) scale(2)', 'translateX(10%) scale(2)');
}, "<transform-function> values are computed correctly for " + id);
test(function() {
assert_computed_value('<integer>', '15', '15');
assert_computed_value('<integer>', 'calc(15 + 15)', '30');
assert_computed_value('<integer>', 'calc(2.4)', '2');
assert_computed_value('<integer>', 'calc(2.6)', '3');
assert_computed_value('<integer>', 'calc(2.6 + 3.1)', '6');
}, "<integer> values are computed correctly for " + id);
test(function() {
assert_computed_value('<integer>+', '15 calc(2.4) calc(2.6)', '15 2 3');
}, "<integer>+ values are computed correctly for " + id);
test(function() {
assert_computed_value('<color>', '#ff0000', 'rgb(255, 0, 0)');
assert_computed_value('<color>', '#000f00', 'rgb(0, 15, 0)');
assert_computed_value('<color>', '#00000a', 'rgb(0, 0, 10)');
assert_computed_value('<color>', '#badbee', 'rgb(186, 219, 238)');
assert_computed_value('<color>', '#badbee33', 'rgba(186, 219, 238, 0.2)');
assert_computed_value('<color>', 'tomato', 'rgb(255, 99, 71)');
assert_computed_value('<color>', 'plum', 'rgb(221, 160, 221)');
assert_computed_value('<color>', 'currentcolor', 'currentcolor');
}, "<color> values are computed correctly for " + id);
test(function() {
assert_computed_value('*', 'tomato', 'tomato');
assert_computed_value('tomato | plum', 'plum', 'plum');
assert_computed_value('tomato | plum | <color>', 'plum', 'plum');
}, "ident values that look like color keywords are not converted to colors" + id);
test(function() {
assert_computed_value('*', '-50grad', '-50grad');
assert_computed_value('<angle>', '180deg', '180deg');
assert_computed_value('<angle>', '400grad', '360deg');
assert_computed_value('<angle>', 'calc(360deg + 400grad)', '720deg');
}, "<angle> values computed correctly for " + id);
assert_computed_value(divWithFontSizeSet, syntax, value, expected);
}, `${syntax} values are computed correctly [${value}]`);
}
test(function(){
const element = divWithFontSizeSet;
with_custom_property(element, '<length>', '14em', (name) => {
assert_computed_value(element, '<length>', `var(${name})`, '140px');
});
}, '<length> values computed are correctly via var()-reference');
test(function(){
const element = divWithFontSizeInherited;
with_custom_property(element, '<length>', '14em', (name) => {
assert_computed_value(element, '<length>', `var(${name})`, '140px');
});
}, '<length> values computed are correctly via var()-reference when font-size is inherited');
test(function(){
const element = divWithFontSizeInherited;
assert_computed_value(element, '<length>', '14em', '140px');
}, '<length> values are computed correctly when font-size is inherited [14em]');
test(function(){
const element = divWithFontSizeInherited;
assert_computed_value(element, '<length>', 'calc(14em + 10px)', '150px');
}, '<length> values are computed correctly when font-size is inherited [calc(14em + 10px)]');
test_computed_value('<length>', '12px', '12px');
test_computed_value('<length>', '13vw', length_ref('13vw'));
test_computed_value('<length>', '14em', '140px');
test_computed_value('<length>', '15vmin', length_ref('15vmin'));
test_computed_value('<length>', 'calc(16px - 7em + 10vh)', length_ref('calc(10vh - 54px)'));
test_computed_value('<length-percentage>', '17em', '170px');
test_computed_value('<length-percentage>', '18%', '18%');
test_computed_value('<length-percentage>', 'calc(19em - 2%)', 'calc(190px + -2%)');
test_computed_value('<length>#', '10px, 3em', '10px, 30px');
test_computed_value('<length>#', '4em ,9px', '40px, 9px');
test_computed_value('<length>#', '8em', '80px');
test_computed_value('<length-percentage>#', '3% , 10vmax , 22px', ['3%', length_ref('10vmax'), '22px'].join(', '));
test_computed_value('<length-percentage>#', 'calc(50% + 1em), 4px', 'calc(10px + 50%), 4px');
test_computed_value('<length-percentage>#', 'calc(13% + 37px)', 'calc(37px + 13%)');
test_computed_value('<length>+', '10px 3em', '10px 30px');
test_computed_value('<length>+', '4em 9px', '40px 9px');
test_computed_value('<length-percentage>+', '3% 10vmax 22px', ['3%', length_ref('10vmax'), '22px'].join(' '));
test_computed_value('<length-percentage>+', 'calc(50% + 1em) 4px', 'calc(10px + 50%) 4px');
test_computed_value('<transform-function>', 'translateX(2px)', 'translateX(2px)');
test_computed_value('<transform-function>', 'translateX(10em)', 'translateX(100px)');
test_computed_value('<transform-function>', 'translateX(calc(11em + 10%))', 'translateX(calc(110px + 10%))');
test_computed_value('<transform-function>+', 'translateX(10%) scale(2)', 'translateX(10%) scale(2)');
test_computed_value('<integer>', '15', '15');
test_computed_value('<integer>', 'calc(15 + 15)', '30');
test_computed_value('<integer>', 'calc(2.4)', '2');
test_computed_value('<integer>', 'calc(2.6)', '3');
test_computed_value('<integer>', 'calc(2.6 + 3.1)', '6');
test_computed_value('<integer>+', '15 calc(2.4) calc(2.6)', '15 2 3');
test_computed_value('<color>', '#ff0000', 'rgb(255, 0, 0)');
test_computed_value('<color>', '#000f00', 'rgb(0, 15, 0)');
test_computed_value('<color>', '#00000a', 'rgb(0, 0, 10)');
test_computed_value('<color>', '#badbee', 'rgb(186, 219, 238)');
test_computed_value('<color>', '#badbee33', 'rgba(186, 219, 238, 0.2)');
test_computed_value('<color>', 'tomato', 'rgb(255, 99, 71)');
test_computed_value('<color>', 'plum', 'rgb(221, 160, 221)');
test_computed_value('<color>', 'currentcolor', 'currentcolor');
// Custom ident values that look like color keywords should not be converted.
test_computed_value('*', 'tomato', 'tomato');
test_computed_value('tomato | plum', 'plum', 'plum');
test_computed_value('tomato | plum | <color>', 'plum', 'plum');
test_computed_value('*', '-50grad', '-50grad');
test_computed_value('<angle>', '180deg', '180deg');
test_computed_value('<angle>', '400grad', '360deg');
test_computed_value('<angle>', 'calc(360deg + 400grad)', '720deg');
test_computed_value('*', '50s', '50s');
test_computed_value('<time>', '1s', '1s');
test_computed_value('<time>', '1000ms', '1s');
test_computed_value('<time>', 'calc(1000ms + 1s)', '2s');
test_computed_value('*', '50dpi', '50dpi');
test_computed_value('<resolution>', '1dppx', '1dppx');
test_computed_value('<resolution>', '96dpi', '1dppx');
test_computed_value('<resolution>', 'calc(1dppx + 96dpi)', '2dppx');
</script>

View file

@ -1,35 +1,45 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-propertydescriptor-initialvalue" />
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#register-a-custom-property" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#target {
background: var(--inherited-color);
color: var(--non-inherited-color);
}
</style>
<script src="./resources/utils.js"></script>
<div id=target></div>
<script>
function test_initial_value(reg, expected) {
let suffix = reg.inherits === true ? ', inherits' : '';
test(function(){
CSS.registerProperty({name: '--length', syntax: '<length>', initialValue: 'calc(10px + 15px)', inherits: false});
CSS.registerProperty({name: '--length-percentage', syntax: '<length-percentage>', initialValue: 'calc(1in + 10% + 4px)', inherits: false});
CSS.registerProperty({name: '--inherited-color', syntax: '<color>', initialValue: 'pink', inherits: true});
CSS.registerProperty({name: '--non-inherited-color', syntax: '<color>', initialValue: 'purple', inherits: false});
CSS.registerProperty({name: '--transform-function', syntax: '<transform-function>', initialValue: 'rotate(42deg)', inherits: false});
CSS.registerProperty({name: '--single-transform-list', syntax: '<transform-list>', initialValue: 'scale(calc(2 + 2))', inherits: false});
CSS.registerProperty({name: '--multiple-transform-list', syntax: '<transform-list>', initialValue: 'scale(calc(2 + 1)) translateX(calc(3px + 1px))', inherits: false});
let name = generate_property(reg);
let actual = getComputedStyle(target).getPropertyValue(name);
assert_equals(actual, expected);
}, `Initial value for ${reg.syntax} correctly computed [${reg.initialValue}${suffix}]`);
}
computedStyle = getComputedStyle(target);
assert_equals(computedStyle.getPropertyValue('--length'), '25px');
assert_equals(computedStyle.getPropertyValue('--length-percentage'), 'calc(100px + 10%)');
assert_equals(computedStyle.getPropertyValue('--inherited-color'), 'rgb(255, 192, 203)');
assert_equals(computedStyle.getPropertyValue('--non-inherited-color'), 'rgb(128, 0, 128)');
assert_equals(computedStyle.getPropertyValue('--transform-function'), 'rotate(42deg)');
assert_equals(computedStyle.getPropertyValue('--single-transform-list'), 'scale(4)');
assert_equals(computedStyle.getPropertyValue('--multiple-transform-list'), 'scale(3) translateX(4px)');
test_initial_value({ syntax: '<length>', initialValue: 'calc(10px + 15px)' }, '25px');
test_initial_value({ syntax: '<length-percentage>', initialValue: 'calc(1in + 10% + 4px)' }, 'calc(100px + 10%)');
test_initial_value({ syntax: '<color>', initialValue: 'pink', inherits: true }, 'rgb(255, 192, 203)');
test_initial_value({ syntax: '<color>', initialValue: 'purple' }, 'rgb(128, 0, 128)');
test_initial_value({ syntax: '<transform-function>', initialValue: 'rotate(42deg)' }, 'rotate(42deg)');
test_initial_value({ syntax: '<transform-list>', initialValue: 'scale(calc(2 + 2))' }, 'scale(4)');
test_initial_value({ syntax: '<transform-list>', initialValue: 'scale(calc(2 + 1)) translateX(calc(3px + 1px))' }, 'scale(3) translateX(4px)');
// Test that the initial value of the custom property 'reg' is successfully
// substituted into 'property'.
function test_substituted_value(reg, property, expected) {
let inherits_text = reg.inherits === true ? 'inherited' : 'non-inherited';
test(function(){
try {
let name = generate_property(reg);
target.style = `${property}:var(${name});`;
assert_equals(getComputedStyle(target).getPropertyValue(property), expected);
} finally {
target.style = '';
}
}, `Initial ${inherits_text} value can be substituted [${reg.initialValue}, ${property}]`);
}
test_substituted_value({ syntax: '<color>', initialValue: 'purple', inherits: true }, 'color', 'rgb(128, 0, 128)');
test_substituted_value({ syntax: '<color>', initialValue: 'pink' }, 'background-color', 'rgb(255, 192, 203)');
assert_equals(computedStyle.backgroundColor, 'rgb(255, 192, 203)');
assert_equals(computedStyle.color, 'rgb(128, 0, 128)');
}, "Initial values of registered properties can be referenced when no custom properties are explicitly set.");
</script>

View file

@ -50,6 +50,14 @@ function any_initial_value(syntax) {
// generated. If a single string is used as the argument, it is assumed to be
// the syntax.
function generate_property(reg) {
// Verify that only valid keys are specified. This prevents the caller from
// accidentally supplying 'inherited' instead of 'inherits', for example.
if (typeof(reg) === 'object') {
const permitted = new Set(['name', 'syntax', 'initialValue', 'inherits']);
if (!Object.keys(reg).every(k => permitted.has(k)))
throw new Error('generate_property: invalid parameter');
}
let syntax = typeof(reg) === 'string' ? reg : reg.syntax;
let initial = typeof(reg.initialValue) === 'undefined' ? any_initial_value(syntax)
: reg.initialValue;

View file

@ -32,4 +32,10 @@ test(function(){
}
}, 'Generated properties respect inherits flag');
test(function(){
assert_throws(new Error(), () => generate_property({syntax: '<length>', foo: 1}));
assert_throws(new Error(), () => generate_property({syntax: '<length>', inherited: false}));
assert_throws(new Error(), () => generate_property({syntax: '<length>', initial: '10px'}));
}, 'Can\'t generate property with unknown fields');
</script>

View file

@ -0,0 +1,27 @@
<!doctype>
<meta charset="utf-8">
<title>CSS Test: Text is not incorrectly clipped in presence of perspective.</title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1494685">
<link rel="mismatch" href="about:blank">
<style>
* {
margin: 0;
padding: 0;
}
.container {
perspective: 1px;
}
.heading {
transform: translateZ(-9px) scale(10);
}
h1 {
font-size: 100px;
}
</style>
<div class="container">
<div class="heading">
<h1>X</h1>
</div>
</div>

View file

@ -1,5 +1,4 @@
spec: https://drafts.fxtf.org/geometry/
suggested_reviewers:
- peterjoel
- tschneidereit
- dirkschulze

View file

@ -9,7 +9,7 @@
p {
height: 50px; width: 200px;
border: thin solid;
background-image: url(../backgrounds/blue-32x32.png);
background-image: url(support/blue-32x32.png);
background-repeat: no-repeat;
}

View file

@ -11,7 +11,7 @@
p {
height: 50px; width: 200px;
border: thin solid;
background-image: url(../backgrounds/blue-32x32.png);
background-image: url(support/blue-32x32.png);
background-repeat: no-repeat;
}

View file

@ -9,7 +9,7 @@
p {
height: 50px; width: 200px;
border: thin solid;
background-image: url(../backgrounds/blue-32x32.png);
background-image: url(support/blue-32x32.png);
background-repeat: no-repeat;
}

View file

@ -11,7 +11,7 @@
p {
height: 50px; width: 200px;
border: thin solid;
background-image: url(../backgrounds/blue-32x32.png);
background-image: url(support/blue-32x32.png);
background-repeat: no-repeat;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

View file

@ -273,6 +273,11 @@ testText("<div>abc<div class='itable'><span class='cell'>def</span></div>ghi", "
testText("<div>abc<div class='itable'><span class='row'><span class='cell'>def</span></span>\n<span class='row'><span class='cell'>123</span></span></div>ghi",
"abcdef\n123ghi", "Single newline in two-row inline-table");
/**** display:table-row/table-cell/table-caption ****/
testText("<div style='display:table-row'>", "", "display:table-row on the element itself");
testText("<div style='display:table-cell'>", "", "display:table-cell on the element itself");
testText("<div style='display:table-caption'>", "", "display:table-caption on the element itself");
/**** Lists ****/
testText("<div><ol><li>abc", "abc", "<ol> list items get no special treatment");
@ -293,6 +298,9 @@ testText("<div>abc<hr><hr>def", "abc\ndef", "<hr><hr> induces just one line brea
testText("<div>abc<hr><hr><hr>def", "abc\ndef", "<hr><hr><hr> induces just one line break");
testText("<div><hr class='poke'>", "abc", "<hr> content rendered");
testText("<div>abc<!--comment-->def", "abcdef", "comment ignored");
testText("<br>", "", "<br>");
testText("<p>", "", "empty <p>");
testText("<div>", "", "empty <div>");
/**** text-transform ****/
@ -308,11 +316,13 @@ testText("<div>abc<span>123<div>456</div>789</span>def", "abc123\n456\n789def",
testText("<div>abc<div style='float:left'>123</div>def", "abc\n123\ndef", "floats induce a block boundary");
testText("<div>abc<span style='float:left'>123</span>def", "abc\n123\ndef", "floats induce a block boundary");
testText("<div style='float:left'>123", "123", "float on the element itself");
/**** position ****/
testText("<div>abc<div style='position:absolute'>123</div>def", "abc\n123\ndef", "position:absolute induces a block boundary");
testText("<div>abc<span style='position:absolute'>123</span>def", "abc\n123\ndef", "position:absolute induces a block boundary");
testText("<div style='position:absolute'>123", "123", "position:absolute on the element itself");
testText("<div>abc<div style='position:relative'>123</div>def", "abc\n123\ndef", "position:relative has no effect");
testText("<div>abc<span style='position:relative'>123</span>def", "abc123def", "position:relative has no effect");

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html><head>
<meta charset="utf-8">
<title>Reference: Combobox block-size test</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
<style>
html,body {
color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
}
select { -webkit-appearance: none; }
.big { font-size: 48pt; min-height: 40pt; }
.lh { line-height: 48pt; min-height: 40pt; }
.mask { position:fixed; left:20px; right:0; top:0; bottom:0; background: black; }
</style>
</head>
<body>
<!-- mask off differences on the right side -->
<div class="mask"></div>
<select><optgroup label="label"><option>option</option></select><br>
<select class="big"><optgroup label="label"><option>option</option></select><br>
<select class="lh"><optgroup label="label"><option>option</option></select><br>
</body>
</html>

View file

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html><head>
<meta charset="utf-8">
<title>Test: Combobox block-size test</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
<link rel="match" href="select-1-block-size-ref.html">
<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1499578">
<style>
html,body {
color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
}
select { -webkit-appearance: none; }
optgroup { font-size: 32pt; }
option { font-size: 24pt; }
.big { font-size: 48pt; }
.lh { line-height: 48pt; }
.mask { position:fixed; left:20px; right:0; top:0; bottom:0; background: black; }
</style>
</head>
<body>
<!-- mask off differences on the right side -->
<div class="mask"></div>
<select><optgroup label="label"><option>option</option></select><br>
<select class="big"><optgroup label="label"><option>option</option></select><br>
<select class="lh"><optgroup label="label"><option>option</option></select><br>
</body>
</html>

View file

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html><head>
<meta charset="utf-8">
<title>Reference: empty SELECT</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
<style>
.none { display: none; }
</style>
</head>
<body>
<table border="1" cellpadding="10">
<tr>
<td><select size="4"><option class="none"></select>
<td><select size="4" style="-webkit-appearance: none"><option class="none">option</select>
<td><select size="4" style="-webkit-appearance: none; border: 1px solid black"><option class="none">option</select>
<td><select size="4" style="border: 1px solid black"><option class="none">option</select>
</table>
<table border="1" cellpadding="10">
<tr>
<td><select size="1"><option class="none"></select>
<td><select size="1" style="-webkit-appearance: none"><option class="none"></select>
<td><select size="1" style="-webkit-appearance: none; border: 1px solid black"><option class="none"></select>
<td><select size="1" style="border: 1px solid black"><option class="none"></select>
</table>
</body>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html><head>
<meta charset="utf-8">
<title>Test: empty SELECT</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
<link rel="match" href="select-empty-ref.html">
<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1499230">
</head>
<body>
<table border="1" cellpadding="10">
<tr>
<td><select size="4"></select>
<td><select size="4" style="-webkit-appearance: none"></select>
<td><select size="4" style="-webkit-appearance: none; border: 1px solid black"></select>
<td><select size="4" style="border: 1px solid black"></select>
</table>
<table border="1" cellpadding="10">
<tr>
<td><select size="1"></select>
<td><select size="1" style="-webkit-appearance: none"></select>
<td><select size="1" style="-webkit-appearance: none; border: 1px solid black"></select>
<td><select size="1" style="border: 1px solid black"></select>
</table>
</body>
</html>

View file

@ -4,6 +4,5 @@ suggested_reviewers:
- jacobrossi
- plehegar
- scottgonzalez
- staktrace
- RByers
- NavidZ

View file

@ -0,0 +1,211 @@
<!DOCTYPE html>
<title>Service Worker: postMessage to Client (message queue)</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
// This function creates a message listener that captures all messages
// sent to this window and matches them with corresponding requests.
// This frees test code from having to use clunky constructs just to
// avoid race conditions, since the relative order of message and
// request arrival doesn't matter.
function create_message_listener(t) {
const listener = {
messages: new Set(),
requests: new Set(),
waitFor: function(predicate) {
for (const event of this.messages) {
// If a message satisfying the predicate has already
// arrived, it gets matched to this request.
if (predicate(event)) {
this.messages.delete(event);
return Promise.resolve(event);
}
}
// If no match was found, the request is stored and a
// promise is returned.
const request = { predicate };
const promise = new Promise(resolve => request.resolve = resolve);
this.requests.add(request);
return promise;
}
};
window.onmessage = t.step_func(event => {
for (const request of listener.requests) {
// If the new message matches a stored request's
// predicate, the request's promise is resolved with this
// message.
if (request.predicate(event)) {
listener.requests.delete(request);
request.resolve(event);
return;
}
};
// No outstanding request for this message, store it in case
// it's requested later.
listener.messages.add(event);
});
return listener;
}
async function service_worker_register_and_activate(t, script, scope) {
const registration = await service_worker_unregister_and_register(t, script, scope);
t.add_cleanup(() => registration.unregister());
const worker = registration.installing;
await wait_for_state(t, worker, 'activated');
return worker;
}
// Add an iframe (parent) whose document contains a nested iframe
// (child), then set the child's src attribute to child_url and return
// its Window (without waiting for it to finish loading).
async function with_nested_iframes(t, child_url) {
const parent = await with_iframe('resources/nested-iframe-parent.html?role=parent');
t.add_cleanup(() => parent.remove());
const child = parent.contentWindow.document.getElementById('child');
child.setAttribute('src', child_url);
return child.contentWindow;
}
// Returns a predicate matching a fetch message with the specified
// key.
function fetch_message(key) {
return event => event.data.type === 'fetch' && event.data.key === key;
}
// Returns a predicate matching a ping message with the specified
// payload.
function ping_message(data) {
return event => event.data.type === 'ping' && event.data.data === data;
}
// A client message queue test is a testharness.js test with some
// additional setup:
// 1. A listener (see create_message_listener)
// 2. An active service worker
// 3. Two nested iframes
// 4. A state transition function that controls the order of events
// during the test
function client_message_queue_test(url, test_function, description) {
promise_test(async t => {
t.listener = create_message_listener(t);
const script = 'resources/stalling-service-worker.js';
const scope = 'resources/';
t.service_worker = await service_worker_register_and_activate(t, script, scope);
// We create two nested iframes such that both are controlled by
// the newly installed service worker.
const child_url = url + '?role=child';
t.frame = await with_nested_iframes(t, child_url);
t.state_transition = async function(from, to, scripts) {
// A state transition begins with the child's parser
// fetching a script due to a <script> tag. The request
// arrives at the service worker, which notifies the
// parent, which in turn notifies the test. Note that the
// event loop keeps spinning while the parser is waiting.
const request = await this.listener.waitFor(fetch_message(to));
// The test instructs the service worker to send two ping
// messages through the Client interface: first to the
// child, then to the parent.
this.service_worker.postMessage(from);
// When the parent receives the ping message, it forwards
// it to the test. Assuming that messages to both child
// and parent are mapped to the same task queue (this is
// not [yet] required by the spec), receiving this message
// guarantees that the child has already dispatched its
// message if it was allowed to do so.
await this.listener.waitFor(ping_message(from));
// Finally, reply to the service worker's fetch
// notification with the script it should use as the fetch
// request's response. This is a defensive mechanism that
// ensures the child's parser really is blocked until the
// test is ready to continue.
request.ports[0].postMessage([`state = '${to}';`].concat(scripts));
};
await test_function(t);
}, description);
}
function client_message_queue_enable_test(
install_script,
start_script,
earliest_dispatch,
description)
{
function later_state(state1, state2) {
const states = ['init', 'install', 'start', 'finish', 'loaded'];
const index1 = states.indexOf(state1);
const index2 = states.indexOf(state2);
const max_index = Math.max(index1, index2);
return states[max_index];
}
client_message_queue_test('enable-client-message-queue.html', async t => {
// While parsing the child's document, the child transitions
// from the 'init' state all the way to the 'finish' state.
// Once parsing is finished it would enter the final 'loaded'
// state. All but the last transition require assitance from
// the test.
await t.state_transition('init', 'install', [install_script]);
await t.state_transition('install', 'start', [start_script]);
await t.state_transition('start', 'finish', []);
// Wait for all messages to get dispatched on the child's
// ServiceWorkerContainer and then verify that each message
// was dispatched while the child was in the correct state.
const report = await t.frame.report;
['init', 'install', 'start'].forEach(state => {
const dispatch = later_state(state, earliest_dispatch);
assert_equals(report[state], dispatch,
`Message sent in state '${state}' dispatched in state '${dispatch}'`);
});
}, description);
}
const empty_script = ``;
const add_event_listener =
`navigator.serviceWorker.addEventListener('message', handle_message);`;
const set_onmessage = `navigator.serviceWorker.onmessage = handle_message;`;
const start_messages = `navigator.serviceWorker.startMessages();`;
client_message_queue_enable_test(add_event_listener, empty_script, 'loaded',
'Messages from ServiceWorker to Client only received after DOMContentLoaded event.');
client_message_queue_enable_test(add_event_listener, start_messages, 'start',
'Messages from ServiceWorker to Client only received after calling startMessages().');
client_message_queue_enable_test(set_onmessage, empty_script, 'install',
'Messages from ServiceWorker to Client only received after setting onmessage.');
const resolve_manual_promise = `resolve_manual_promise();`
async function test_microtasks_when_client_message_queue_enabled(t, scripts) {
await t.state_transition('init', 'start', scripts.concat([resolve_manual_promise]));
let result = await t.frame.result;
assert_equals(result[0], 'microtask', 'The microtask was executed first.');
assert_equals(result[1], 'message', 'The message was dispatched.');
}
client_message_queue_test('message-vs-microtask.html', t => {
return test_microtasks_when_client_message_queue_enabled(t, [
add_event_listener,
start_messages,
]);
}, 'Microtasks run before dispatching messages after calling startMessages().');
client_message_queue_test('message-vs-microtask.html', t => {
return test_microtasks_when_client_message_queue_enabled(t, [set_onmessage]);
}, 'Microtasks run before dispatching messages after setting onmessage.');
</script>

View file

@ -51,208 +51,4 @@ promise_test(t => {
})
.then(e => { assert_equals(e.data, 'quit'); });
}, 'postMessage from ServiceWorker to Client.');
// This function creates a message listener that captures all messages
// sent to this window and matches them with corresponding requests.
// This frees test code from having to use clunky constructs just to
// avoid race conditions, since the relative order of message and
// request arrival doesn't matter.
function create_message_listener(t) {
const listener = {
messages: new Set(),
requests: new Set(),
waitFor: function(predicate) {
for (const event of this.messages) {
// If a message satisfying the predicate has already
// arrived, it gets matched to this request.
if (predicate(event)) {
this.messages.delete(event);
return Promise.resolve(event);
}
}
// If no match was found, the request is stored and a
// promise is returned.
const request = { predicate };
const promise = new Promise(resolve => request.resolve = resolve);
this.requests.add(request);
return promise;
}
};
window.onmessage = t.step_func(event => {
for (const request of listener.requests) {
// If the new message matches a stored request's
// predicate, the request's promise is resolved with this
// message.
if (request.predicate(event)) {
listener.requests.delete(request);
request.resolve(event);
return;
}
};
// No outstanding request for this message, store it in case
// it's requested later.
listener.messages.add(event);
});
return listener;
}
async function service_worker_register_and_activate(t, script, scope) {
const registration = await service_worker_unregister_and_register(t, script, scope);
t.add_cleanup(() => registration.unregister());
const worker = registration.installing;
await wait_for_state(t, worker, 'activated');
return worker;
}
// Add an iframe (parent) whose document contains a nested iframe
// (child), then set the child's src attribute to child_url and return
// its Window (without waiting for it to finish loading).
async function with_nested_iframes(t, child_url) {
const parent = await with_iframe('resources/nested-iframe-parent.html?role=parent');
t.add_cleanup(() => parent.remove());
const child = parent.contentWindow.document.getElementById('child');
child.setAttribute('src', child_url);
return child.contentWindow;
}
// Returns a predicate matching a fetch message with the specified
// key.
function fetch_message(key) {
return event => event.data.type === 'fetch' && event.data.key === key;
}
// Returns a predicate matching a ping message with the specified
// payload.
function ping_message(data) {
return event => event.data.type === 'ping' && event.data.data === data;
}
// A client message queue test is a testharness.js test with some
// additional setup:
// 1. A listener (see create_message_listener)
// 2. An active service worker
// 3. Two nested iframes
// 4. A state transition function that controls the order of events
// during the test
function client_message_queue_test(url, test_function, description) {
promise_test(async t => {
t.listener = create_message_listener(t);
const script = 'resources/stalling-service-worker.js';
const scope = 'resources/';
t.service_worker = await service_worker_register_and_activate(t, script, scope);
// We create two nested iframes such that both are controlled by
// the newly installed service worker.
const child_url = url + '?role=child';
t.frame = await with_nested_iframes(t, child_url);
t.state_transition = async function(from, to, scripts) {
// A state transition begins with the child's parser
// fetching a script due to a <script> tag. The request
// arrives at the service worker, which notifies the
// parent, which in turn notifies the test. Note that the
// event loop keeps spinning while the parser is waiting.
const request = await this.listener.waitFor(fetch_message(to));
// The test instructs the service worker to send two ping
// messages through the Client interface: first to the
// child, then to the parent.
this.service_worker.postMessage(from);
// When the parent receives the ping message, it forwards
// it to the test. Assuming that messages to both child
// and parent are mapped to the same task queue (this is
// not [yet] required by the spec), receiving this message
// guarantees that the child has already dispatched its
// message if it was allowed to do so.
await this.listener.waitFor(ping_message(from));
// Finally, reply to the service worker's fetch
// notification with the script it should use as the fetch
// request's response. This is a defensive mechanism that
// ensures the child's parser really is blocked until the
// test is ready to continue.
request.ports[0].postMessage([`state = '${to}';`].concat(scripts));
};
await test_function(t);
}, description);
}
function client_message_queue_enable_test(
install_script,
start_script,
earliest_dispatch,
description)
{
function later_state(state1, state2) {
const states = ['init', 'install', 'start', 'finish', 'loaded'];
const index1 = states.indexOf(state1);
const index2 = states.indexOf(state2);
const max_index = Math.max(index1, index2);
return states[max_index];
}
client_message_queue_test('enable-client-message-queue.html', async t => {
// While parsing the child's document, the child transitions
// from the 'init' state all the way to the 'finish' state.
// Once parsing is finished it would enter the final 'loaded'
// state. All but the last transition require assitance from
// the test.
await t.state_transition('init', 'install', [install_script]);
await t.state_transition('install', 'start', [start_script]);
await t.state_transition('start', 'finish', []);
// Wait for all messages to get dispatched on the child's
// ServiceWorkerContainer and then verify that each message
// was dispatched while the child was in the correct state.
const report = await t.frame.report;
['init', 'install', 'start'].forEach(state => {
const dispatch = later_state(state, earliest_dispatch);
assert_equals(report[state], dispatch,
`Message sent in state '${state}' dispatched in state '${dispatch}'`);
});
}, description);
}
const empty_script = ``;
const add_event_listener =
`navigator.serviceWorker.addEventListener('message', handle_message);`;
const set_onmessage = `navigator.serviceWorker.onmessage = handle_message;`;
const start_messages = `navigator.serviceWorker.startMessages();`;
client_message_queue_enable_test(add_event_listener, empty_script, 'loaded',
'Messages from ServiceWorker to Client only received after DOMContentLoaded event.');
client_message_queue_enable_test(add_event_listener, start_messages, 'start',
'Messages from ServiceWorker to Client only received after calling startMessages().');
client_message_queue_enable_test(set_onmessage, empty_script, 'install',
'Messages from ServiceWorker to Client only received after setting onmessage.');
const resolve_manual_promise = `resolve_manual_promise();`
async function test_microtasks_when_client_message_queue_enabled(t, scripts) {
await t.state_transition('init', 'start', scripts.concat([resolve_manual_promise]));
let result = await t.frame.result;
assert_equals(result[0], 'microtask', 'The microtask was executed first.');
assert_equals(result[1], 'message', 'The message was dispatched.');
}
client_message_queue_test('message-vs-microtask.html', t => {
return test_microtasks_when_client_message_queue_enabled(t, [
add_event_listener,
start_messages,
]);
}, 'Microtasks run before dispatching messages after calling startMessages().');
client_message_queue_test('message-vs-microtask.html', t => {
return test_microtasks_when_client_message_queue_enabled(t, [set_onmessage]);
}, 'Microtasks run before dispatching messages after setting onmessage.');
</script>

View file

@ -1,4 +1,3 @@
spec: https://w3c.github.io/speech-api/
suggested_reviewers:
- andrenatal
- gshires

View file

@ -1,8 +1,6 @@
spec: https://w3c.github.io/webappsec-subresource-integrity/
suggested_reviewers:
- metromoxie
- fmarier
- jonathanKingston
- mikewest
- hillbrad
- mastahyeti

View file

@ -264,6 +264,8 @@ class Chrome(BrowserSetup):
if kwargs["browser_channel"] == "dev":
logger.info("Automatically turning on experimental features for Chrome Dev")
kwargs["binary_args"].append("--enable-experimental-web-platform-features")
# TODO(foolip): remove after unified plan is enabled on Chrome stable
kwargs["binary_args"].append("--enable-features=RTCUnifiedPlanByDefault")
# Allow audio autoplay without a user gesture.
kwargs["binary_args"].append("--autoplay-policy=no-user-gesture-required")

View file

@ -1,7 +1,6 @@
import logging
import os
import subprocess
import sys
import tarfile
import zipfile
from io import BytesIO
@ -49,20 +48,6 @@ def call(*args):
raise
def get_git_cmd(repo_path):
"""Create a function for invoking git commands as a subprocess."""
def git(cmd, *args):
full_cmd = ["git", cmd] + list(args)
try:
logger.debug(" ".join(full_cmd))
return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subprocess.STDOUT).strip()
except subprocess.CalledProcessError as e:
logger.error("Git command exited with status %i" % e.returncode)
logger.error(e.output)
sys.exit(1)
return git
def seekable(fileobj):
"""Attempt to use file.seek on given file, with fallbacks."""
try:
@ -94,21 +79,6 @@ def unzip(fileobj, dest=None, limit=None):
os.chmod(os.path.join(dest, info.filename), perm)
class pwd(object):
"""Create context for temporarily changing present working directory."""
def __init__(self, dir):
self.dir = dir
self.old_dir = None
def __enter__(self):
self.old_dir = os.path.abspath(os.curdir)
os.chdir(self.dir)
def __exit__(self, *args, **kwargs):
os.chdir(self.old_dir)
self.old_dir = None
def get(url):
"""Issue GET request to a given URL and return the response."""
import requests

View file

@ -818,14 +818,10 @@ class ManagerGroup(object):
self.pool.add(manager)
self.wait()
def is_alive(self):
"""Boolean indicating whether any manager in the group is still alive"""
return any(manager.is_alive() for manager in self.pool)
def wait(self):
"""Wait for all the managers in the group to finish"""
for item in self.pool:
item.join()
for manager in self.pool:
manager.join()
def stop(self):
"""Set the stop flag so that all managers in the group stop as soon
@ -834,7 +830,7 @@ class ManagerGroup(object):
self.logger.debug("Stop flag set in ManagerGroup")
def test_count(self):
return sum(item.test_count for item in self.pool)
return sum(manager.test_count for manager in self.pool)
def unexpected_count(self):
return sum(item.unexpected_count for item in self.pool)
return sum(manager.unexpected_count for manager in self.pool)

View file

@ -1,7 +1,6 @@
spec: https://url.spec.whatwg.org/
suggested_reviewers:
- mikewest
- smola
- domenic
- Sebmaster
- annevk

View file

@ -1,5 +1,4 @@
spec: https://webaudio.github.io/web-audio-api/
suggested_reviewers:
- chrislo
- padenot
- rtoy

View file

@ -1,234 +1 @@
import copy
import json
import os
import urlparse
import pytest
import webdriver
from tests.support import defaults
from tests.support.helpers import cleanup_session
from tests.support.http_request import HTTPRequest
from tests.support.sync import Poll
_current_session = None
_custom_session = False
def pytest_configure(config):
# register the capabilities marker
config.addinivalue_line("markers",
"capabilities: mark test to use capabilities")
@pytest.fixture
def capabilities():
"""Default capabilities to use for a new WebDriver session."""
return {}
def pytest_generate_tests(metafunc):
if "capabilities" in metafunc.fixturenames:
marker = metafunc.definition.get_closest_marker(name="capabilities")
if marker:
metafunc.parametrize("capabilities", marker.args, ids=None)
@pytest.fixture
def add_event_listeners(session):
"""Register listeners for tracked events on element."""
def add_event_listeners(element, tracked_events):
element.session.execute_script("""
let element = arguments[0];
let trackedEvents = arguments[1];
if (!("events" in window)) {
window.events = [];
}
for (var i = 0; i < trackedEvents.length; i++) {
element.addEventListener(trackedEvents[i], function (event) {
window.events.push(event.type);
});
}
""", args=(element, tracked_events))
return add_event_listeners
@pytest.fixture
def create_cookie(session, url):
"""Create a cookie"""
def create_cookie(name, value, **kwargs):
if kwargs.get("path", None) is not None:
session.url = url(kwargs["path"])
session.set_cookie(name, value, **kwargs)
return session.cookies(name)
return create_cookie
@pytest.fixture
def create_frame(session):
"""Create an `iframe` element in the current browsing context and insert it
into the document. Return a reference to the newly-created element."""
def create_frame():
append = """
var frame = document.createElement('iframe');
document.body.appendChild(frame);
return frame;
"""
return session.execute_script(append)
return create_frame
@pytest.fixture
def create_window(session):
"""Open new window and return the window handle."""
def create_window():
windows_before = session.handles
name = session.execute_script("window.open()")
assert len(session.handles) == len(windows_before) + 1
new_windows = list(set(session.handles) - set(windows_before))
return new_windows.pop()
return create_window
@pytest.fixture
def http(configuration):
return HTTPRequest(configuration["host"], configuration["port"])
@pytest.fixture
def server_config():
return json.loads(os.environ.get("WD_SERVER_CONFIG"))
@pytest.fixture(scope="session")
def configuration():
host = os.environ.get("WD_HOST", defaults.DRIVER_HOST)
port = int(os.environ.get("WD_PORT", str(defaults.DRIVER_PORT)))
capabilities = json.loads(os.environ.get("WD_CAPABILITIES", "{}"))
return {
"host": host,
"port": port,
"capabilities": capabilities
}
@pytest.fixture(scope="function")
def session(capabilities, configuration, request):
"""Create and start a session for a test that does not itself test session creation.
By default the session will stay open after each test, but we always try to start a
new one and assume that if that fails there is already a valid session. This makes it
possible to recover from some errors that might leave the session in a bad state, but
does not demand that we start a new session per test."""
global _current_session
# Update configuration capabilities with custom ones from the
# capabilities fixture, which can be set by tests
caps = copy.deepcopy(configuration["capabilities"])
caps.update(capabilities)
caps = {"alwaysMatch": caps}
# If there is a session with different capabilities active, end it now
if _current_session is not None and (
caps != _current_session.requested_capabilities):
_current_session.end()
_current_session = None
if _current_session is None:
_current_session = webdriver.Session(
configuration["host"],
configuration["port"],
capabilities=caps)
try:
_current_session.start()
except webdriver.error.SessionNotCreatedException:
if not _current_session.session_id:
raise
# Enforce a fixed default window size
_current_session.window.size = defaults.WINDOW_SIZE
yield _current_session
cleanup_session(_current_session)
@pytest.fixture(scope="function")
def current_session():
return _current_session
@pytest.fixture
def url(server_config):
def inner(path, protocol="http", query="", fragment=""):
port = server_config["ports"][protocol][0]
host = "%s:%s" % (server_config["browser_host"], port)
return urlparse.urlunsplit((protocol, host, path, query, fragment))
inner.__name__ = "url"
return inner
@pytest.fixture
def create_dialog(session):
"""Create a dialog (one of "alert", "prompt", or "confirm") and provide a
function to validate that the dialog has been "handled" (either accepted or
dismissed) by returning some value."""
def create_dialog(dialog_type, text=None):
assert dialog_type in ("alert", "confirm", "prompt"), (
"Invalid dialog type: '%s'" % dialog_type)
if text is None:
text = ""
assert isinstance(text, basestring), "`text` parameter must be a string"
# Script completes itself when the user prompt has been opened.
# For prompt() dialogs, add a value for the 'default' argument,
# as some user agents (IE, for example) do not produce consistent
# values for the default.
session.execute_async_script("""
let dialog_type = arguments[0];
let text = arguments[1];
setTimeout(function() {
if (dialog_type == 'prompt') {
window.dialog_return_value = window[dialog_type](text, '');
} else {
window.dialog_return_value = window[dialog_type](text);
}
}, 0);
""", args=(dialog_type, text))
wait = Poll(
session,
timeout=15,
ignored_exceptions=webdriver.NoSuchAlertException,
message="No user prompt with text '{}' detected".format(text))
wait.until(lambda s: s.alert.text == text)
return create_dialog
@pytest.fixture
def closed_window(session, create_window):
original_handle = session.window_handle
new_handle = create_window()
session.window_handle = new_handle
session.close()
assert new_handle not in session.handles, "Unable to close window {}".format(new_handle)
yield new_handle
session.window_handle = original_handle
pytest_plugins = "tests.support.fixtures"

View file

@ -51,7 +51,7 @@ def square(size):
<script>
window.clicks = [];
let div = document.querySelector("div");
div.addEventListener("click", ({{clientX, clientY}}) => window.clicks.push([clientX, clientY]));
div.addEventListener("click", function(e) {{ window.clicks.push([e.clientX, e.clientY]) }});
</script>
""".format(size=size))

View file

@ -1,3 +1,6 @@
import base64
import imghdr
from webdriver import Element, NoSuchAlertException, WebDriverException
@ -189,3 +192,10 @@ def assert_move_to_coordinates(point, target, events):
assert e["pageX"] == point["x"]
assert e["pageY"] == point["y"]
assert e["target"] == target
def assert_png(screenshot):
"""Test that screenshot is a Base64 encoded PNG file."""
image = base64.decodestring(screenshot)
mime_type = imghdr.what("", image)
assert mime_type == "png", "Expected image to be PNG, but it was {}".format(mime_type)

View file

@ -0,0 +1,234 @@
import copy
import json
import os
import urlparse
import pytest
import webdriver
from tests.support import defaults
from tests.support.helpers import cleanup_session
from tests.support.http_request import HTTPRequest
from tests.support.sync import Poll
_current_session = None
_custom_session = False
def pytest_configure(config):
# register the capabilities marker
config.addinivalue_line("markers",
"capabilities: mark test to use capabilities")
@pytest.fixture
def capabilities():
"""Default capabilities to use for a new WebDriver session."""
return {}
def pytest_generate_tests(metafunc):
if "capabilities" in metafunc.fixturenames:
marker = metafunc.definition.get_closest_marker(name="capabilities")
if marker:
metafunc.parametrize("capabilities", marker.args, ids=None)
@pytest.fixture
def add_event_listeners(session):
"""Register listeners for tracked events on element."""
def add_event_listeners(element, tracked_events):
element.session.execute_script("""
let element = arguments[0];
let trackedEvents = arguments[1];
if (!("events" in window)) {
window.events = [];
}
for (var i = 0; i < trackedEvents.length; i++) {
element.addEventListener(trackedEvents[i], function (event) {
window.events.push(event.type);
});
}
""", args=(element, tracked_events))
return add_event_listeners
@pytest.fixture
def create_cookie(session, url):
"""Create a cookie"""
def create_cookie(name, value, **kwargs):
if kwargs.get("path", None) is not None:
session.url = url(kwargs["path"])
session.set_cookie(name, value, **kwargs)
return session.cookies(name)
return create_cookie
@pytest.fixture
def create_frame(session):
"""Create an `iframe` element in the current browsing context and insert it
into the document. Return a reference to the newly-created element."""
def create_frame():
append = """
var frame = document.createElement('iframe');
document.body.appendChild(frame);
return frame;
"""
return session.execute_script(append)
return create_frame
@pytest.fixture
def create_window(session):
"""Open new window and return the window handle."""
def create_window():
windows_before = session.handles
name = session.execute_script("window.open()")
assert len(session.handles) == len(windows_before) + 1
new_windows = list(set(session.handles) - set(windows_before))
return new_windows.pop()
return create_window
@pytest.fixture
def http(configuration):
return HTTPRequest(configuration["host"], configuration["port"])
@pytest.fixture
def server_config():
return json.loads(os.environ.get("WD_SERVER_CONFIG"))
@pytest.fixture(scope="session")
def configuration():
host = os.environ.get("WD_HOST", defaults.DRIVER_HOST)
port = int(os.environ.get("WD_PORT", str(defaults.DRIVER_PORT)))
capabilities = json.loads(os.environ.get("WD_CAPABILITIES", "{}"))
return {
"host": host,
"port": port,
"capabilities": capabilities
}
@pytest.fixture(scope="function")
def session(capabilities, configuration, request):
"""Create and start a session for a test that does not itself test session creation.
By default the session will stay open after each test, but we always try to start a
new one and assume that if that fails there is already a valid session. This makes it
possible to recover from some errors that might leave the session in a bad state, but
does not demand that we start a new session per test."""
global _current_session
# Update configuration capabilities with custom ones from the
# capabilities fixture, which can be set by tests
caps = copy.deepcopy(configuration["capabilities"])
caps.update(capabilities)
caps = {"alwaysMatch": caps}
# If there is a session with different capabilities active, end it now
if _current_session is not None and (
caps != _current_session.requested_capabilities):
_current_session.end()
_current_session = None
if _current_session is None:
_current_session = webdriver.Session(
configuration["host"],
configuration["port"],
capabilities=caps)
try:
_current_session.start()
except webdriver.error.SessionNotCreatedException:
if not _current_session.session_id:
raise
# Enforce a fixed default window size
_current_session.window.size = defaults.WINDOW_SIZE
yield _current_session
cleanup_session(_current_session)
@pytest.fixture(scope="function")
def current_session():
return _current_session
@pytest.fixture
def url(server_config):
def inner(path, protocol="http", query="", fragment=""):
port = server_config["ports"][protocol][0]
host = "%s:%s" % (server_config["browser_host"], port)
return urlparse.urlunsplit((protocol, host, path, query, fragment))
inner.__name__ = "url"
return inner
@pytest.fixture
def create_dialog(session):
"""Create a dialog (one of "alert", "prompt", or "confirm") and provide a
function to validate that the dialog has been "handled" (either accepted or
dismissed) by returning some value."""
def create_dialog(dialog_type, text=None):
assert dialog_type in ("alert", "confirm", "prompt"), (
"Invalid dialog type: '%s'" % dialog_type)
if text is None:
text = ""
assert isinstance(text, basestring), "`text` parameter must be a string"
# Script completes itself when the user prompt has been opened.
# For prompt() dialogs, add a value for the 'default' argument,
# as some user agents (IE, for example) do not produce consistent
# values for the default.
session.execute_async_script("""
let dialog_type = arguments[0];
let text = arguments[1];
setTimeout(function() {
if (dialog_type == 'prompt') {
window.dialog_return_value = window[dialog_type](text, '');
} else {
window.dialog_return_value = window[dialog_type](text);
}
}, 0);
""", args=(dialog_type, text))
wait = Poll(
session,
timeout=15,
ignored_exceptions=webdriver.NoSuchAlertException,
message="No user prompt with text '{}' detected".format(text))
wait.until(lambda s: s.alert.text == text)
return create_dialog
@pytest.fixture
def closed_window(session, create_window):
original_handle = session.window_handle
new_handle = create_window()
session.window_handle = new_handle
session.close()
assert new_handle not in session.handles, "Unable to close window {}".format(new_handle)
yield new_handle
session.window_handle = original_handle

View file

@ -117,3 +117,11 @@ def is_element_in_viewport(session, element):
return !(rect.right < 0 || rect.bottom < 0 ||
rect.left > viewport.width || rect.top > viewport.height)
""", args=(element,))
def document_dimensions(session):
return tuple(session.execute_script("""
let {devicePixelRatio} = window;
let {width, height} = document.documentElement.getBoundingClientRect();
return [width * devicePixelRatio, height * devicePixelRatio];
"""))

View file

@ -0,0 +1,11 @@
import base64
import struct
from tests.support.asserts import assert_png
def png_dimensions(screenshot):
assert_png(screenshot)
image = base64.decodestring(screenshot)
width, height = struct.unpack(">LL", image[16:24])
return int(width), int(height)

View file

@ -2,7 +2,7 @@ import urllib
def inline(doc, doctype="html", mime="text/html;charset=utf-8", protocol="http"):
from ..conftest import server_config, url
from .fixtures import server_config, url
build_url = url(server_config())
if doctype == "html":

View file

@ -4,8 +4,6 @@
<title>XMLHttpRequest: send() - charset parameter of Content-Type</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<link rel="help" href="https://xhr.spec.whatwg.org/#the-send()-method" data-tested-assertations="following::ol[1]/li[4]/p/code[contains(text(),'Content-Type')]/.. following::ol[1]/li[4]/p/code[contains(text(),'Content-Type')]/../following-sibling::p" />
<link rel="help" href="https://xhr.spec.whatwg.org/#dom-XMLHttpRequest-send-a-string" data-tested-assertations="following::p[2]" />
</head>
<body>
<div id="log"></div>
@ -45,8 +43,8 @@
)
request(
"text/plain;charset=utf-8",
"text/plain;charset=UTF-8",
"Correct text/plain MIME with charset"
"text/plain;charset=utf-8",
"If charset= param is UTF-8 (case-insensitive), it should not be changed"
)
request(
"text/x-pink-unicorn",
@ -70,8 +68,8 @@
)
request(
"text/plain;charset=utf-8;charset=waddup",
"text/plain;charset=UTF-8",
"charset given but wrong, fix it (known MIME, bogus charset)"
"text/plain;charset=utf-8;charset=waddup",
"If charset= param is UTF-8 (case-insensitive), it should not be changed (bogus charset)"
)
request(
"text/plain;charset=shift-jis",
@ -81,7 +79,7 @@
request(
"text/x-pink-unicorn; charset=windows-1252; charset=bogus; notrelated; charset=ascii",
"text/x-pink-unicorn;charset=UTF-8",
"Multiple charset parameters deduplicate, bogus parameter dropped"
"Multiple non-UTF-8 charset parameters deduplicate, bogus parameter dropped"
)
request(
null,
@ -90,20 +88,20 @@
)
request(
"text/plain;charset= utf-8",
"text/plain;charset=UTF-8",
"charset with space")
"text/plain;charset= utf-8",
"charset with space that is UTF-8 does not change")
request(
"text/plain;charset=\"utf-8\"",
"text/plain;charset=UTF-8",
"charset in double quotes")
"text/plain;charset=\"utf-8\"",
"charset in double quotes that is UTF-8 does not change")
request(
"text/plain;charset=\" utf-8\"",
"text/plain;charset=UTF-8",
"charset in double quotes with space")
request(
"text/plain;charset=\"u\\t\\f-8\"",
"text/plain;charset=UTF-8",
"charset in double quotes with backslashes")
"text/plain;charset=\"u\\t\\f-8\"",
"charset in double quotes with backslashes that is UTF-8 does not change")
request(
"YO/yo;charset=x;yo=YO; X=y",
"yo/yo;charset=UTF-8;yo=YO;x=y",