diff options
Diffstat (limited to 'testing/web-platform/tests/shadow-dom/imperative-slot-api-slotchange.html')
-rw-r--r-- | testing/web-platform/tests/shadow-dom/imperative-slot-api-slotchange.html | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/testing/web-platform/tests/shadow-dom/imperative-slot-api-slotchange.html b/testing/web-platform/tests/shadow-dom/imperative-slot-api-slotchange.html new file mode 100644 index 0000000000..236aaea18a --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/imperative-slot-api-slotchange.html @@ -0,0 +1,394 @@ +<!DOCTYPE html> +<title>Shadow DOM: Imperative Slot API slotchange event</title> +<meta name="author" title="Yu Han" href="mailto:yuzhehan@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="help" href="https://dom.spec.whatwg.org/#signaling-slot-change"> +<script src="resources/shadow-dom.js"></script> + +<div id="test_slotchange"> + <div id="host"> + <template id="shadow_root" data-mode="open" data-slot-assignment="manual"> + <slot id="s1"><div id="fb">fallback</div></slot> + <slot id="s2"></slot> + <div> + <slot id="s2.5"></slot> + </div> + <slot id="s3"></slot> + </template> + <div id="c1"></div> + <div id="c2"></div> + </div> + <div id="c4"></div> +</div> + +<script> +function getDataCollection() { + return { + s1EventCount: 0, + s2EventCount: 0, + s3EventCount: 0, + s1ResolveFn: null, + s2ResolveFn: null, + s3ResolveFn: null, + } +} + +function setupShadowDOM(id, test, data) { + let tTree = createTestTree(id); + tTree.s1.addEventListener('slotchange', (event) => { + if (!event.isFakeEvent) { + test.step(function () { + assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"'); + assert_equals(event.target, tTree.s1, 'slotchange event\'s target must be the slot element'); + assert_equals(event.relatedTarget, undefined, 'slotchange must not set relatedTarget'); + }); + data.s1EventCount++; + } + data.s1ResolveFn(); + }); + tTree.s2.addEventListener('slotchange', (event) => { + if (!event.isFakeEvent) { + test.step(function () { + assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"'); + assert_equals(event.target, tTree.s2, 'slotchange event\'s target must be the slot element'); + assert_equals(event.relatedTarget, undefined, 'slotchange must not set relatedTarget'); + }); + data.s2EventCount++; + } + data.s2ResolveFn(); + }); + tTree.s3.addEventListener('slotchange', (event) => { + if (!event.isFakeEvent) { + test.step(function () { + assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"'); + // listen to bubbling events. + assert_equals(event.relatedTarget, undefined, 'slotchange must not set relatedTarget'); + }); + data.s3EventCount++; + } + data.s3ResolveFn(); + }); + return tTree; +} + +function monitorSlots(data) { + const s1Promise = new Promise((resolve, reject) => { + data.s1ResolveFn = resolve; + }); + const s2Promise = new Promise((resolve, reject) => { + data.s2ResolveFn = resolve; + }); + const s3Promise = new Promise((resolve, reject) => { + data.s3ResolveFn = resolve; + }); + return [s1Promise, s2Promise, s3Promise]; +} +</script> + +<script> +// Tests: +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise, s2Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1); + tTree.s2.assign(tTree.c2); + + assert_equals(data.s1EventCount, 0, 'slotchange event must not be fired synchronously'); + assert_equals(data.s2EventCount, 0); + + Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 1); + assert_equals(data.s2EventCount, 1); + })); +}, 'slotchange event must not fire synchronously.'); + +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise, s2Promise] = monitorSlots(data); + + tTree.s1.assign();; + tTree.s2.assign(); + tTree.host.insertBefore(tTree.c4, tTree.c1); + + Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 0); + assert_equals(data.s2EventCount, 0); + })); + + // use fake event to trigger event handler. + let fakeEvent = new Event('slotchange'); + fakeEvent.isFakeEvent = true; + tTree.s1.dispatchEvent(fakeEvent); + tTree.s2.dispatchEvent(fakeEvent); +}, 'slotchange event should not fire when assignments do not change assignedNodes.'); + +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange,test, data); + let [s1Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1, tTree.c2); + + s1Promise.then(test.step_func(() => { + assert_equals(data.s1EventCount, 1); + + [s1Promise] = monitorSlots(data); + tTree.s1.assign(tTree.c1, tTree.c2); + tTree.s1.assign(tTree.c1, tTree.c2, tTree.c1, tTree.c2, tTree.c2); + + s1Promise.then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 1); + })); + + let fakeEvent = new Event('slotchange'); + fakeEvent.isFakeEvent = true; + tTree.s1.dispatchEvent(fakeEvent); + })); + +}, 'slotchange event should not fire when same node is assigned.'); + +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise, s2Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1); + tTree.s2.assign(tTree.c2); + + Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 1); + assert_equals(data.s2EventCount, 1); + })); +}, "Fire slotchange event when slot's assigned nodes changes."); + +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise, s2Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1); + + s1Promise.then(test.step_func(() => { + assert_equals(data.s1EventCount, 1); + + [s1Promise, s2Promise] = monitorSlots(data); + tTree.s2.assign(tTree.c1); + + Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 2); + assert_equals(data.s2EventCount, 1); + })); + })); +}, "Fire slotchange event on previous slot and new slot when node is reassigned."); + +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1); + + s1Promise.then(test.step_func(() => { + assert_equals(data.s1EventCount, 1); + + [s1Promise] = monitorSlots(data); + tTree.s1.assign(); + + s1Promise.then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 2); + })); + })); +}, "Fire slotchange event on node assignment and when assigned node is removed."); + +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1, tTree.c2); + + s1Promise.then(test.step_func(() => { + assert_equals(data.s1EventCount, 1); + + [s1Promise] = monitorSlots(data); + tTree.s1.assign(tTree.c2, tTree.c1); + + s1Promise.then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 2); + })); + })); +}, "Fire slotchange event when order of assigned nodes changes."); + +promise_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1); + + return s1Promise.then(test.step_func(() => { + assert_equals(data.s1EventCount, 1); + + [s1Promise] = monitorSlots(data); + tTree.c1.remove(); + + return s1Promise; + })) + .then(test.step_func(() => { + assert_equals(data.s1EventCount, 2); + })); +}, "Fire slotchange event when assigned node is removed."); + +promise_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + [s1Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1); + + return s1Promise.then(test.step_func(() => { + assert_equals(data.s1EventCount, 1); + + [s1Promise] = monitorSlots(data); + tTree.s1.remove(); + + return s1Promise; + })) + .then(test.step_func(() => { + assert_equals(data.s1EventCount, 2); + })); +}, "Fire slotchange event when removing a slot from Shadows Root that changes its assigned nodes."); + +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise] = monitorSlots(data); + + tTree.s1.remove(); + + let fakeEvent = new Event('slotchange'); + fakeEvent.isFakeEvent = true; + tTree.s1.dispatchEvent(fakeEvent); + + s1Promise.then(test.step_func(() => { + assert_equals(data.s2EventCount, 0); + + [s1Promise, s2Promise] = monitorSlots(data); + tTree.shadow_root.insertBefore(tTree.s1, tTree.s2); + + tTree.s1.dispatchEvent(fakeEvent); + tTree.s2.dispatchEvent(fakeEvent); + + Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 0); + assert_equals(data.s2EventCount, 0); + })); + })); + +}, "No slotchange event when adding or removing an empty slot."); + +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_slotchange, test, data); + let [s1Promise, s2Promise] = monitorSlots(data); + + tTree.host.appendChild(document.createElement("div")); + + let fakeEvent = new Event('slotchange'); + fakeEvent.isFakeEvent = true; + tTree.s1.dispatchEvent(fakeEvent); + tTree.s2.dispatchEvent(fakeEvent); + + Promise.all([s1Promise, s2Promise]).then(test.step_func(() => { + assert_equals(data.s1EventCount, 0); + assert_equals(data.s2EventCount, 0); + + [s1Promise, s2Promise] = monitorSlots(data); + tTree.shadow_root.insertBefore(document.createElement("div"), tTree.s2); + + tTree.s1.dispatchEvent(fakeEvent); + tTree.s2.dispatchEvent(fakeEvent); + + Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 0); + assert_equals(data.s2EventCount, 0); + })); + })); + +}, "No slotchange event when adding another slotable."); + +</script> + +<div id="test_nested_slotchange"> + <div> + <template data-mode="open" data-slot-assignment="manual"> + <div> + <template data-mode="open" data-slot-assignment="manual"> + <slot id="s2"></slot> + <slot id="s3"></slot> + </template> + <slot id="s1"></slot> + </div> + </template> + <div id="c1"></div> + </div> +</div> + +<script> +async_test((test) => { + const data = getDataCollection(); + let tTree = setupShadowDOM(test_nested_slotchange, test, data); + let [s1Promise, s2Promise, s3Promise] = monitorSlots(data); + + tTree.s3.assign(tTree.s1); + + s3Promise.then(test.step_func(() => { + assert_equals(data.s3EventCount, 1); + [s1Promise, s2Promise, s3Promise] = monitorSlots(data); + + tTree.s1.assign(tTree.c1); + + Promise.all([s1Promise, s3Promise]).then(test.step_func_done(() => { + assert_equals(data.s1EventCount, 1); + assert_equals(data.s3EventCount, 2); + })); + })); +}, "Fire slotchange event when assign node to nested slot, ensure event bubbles ups."); + +promise_test(async t => { + async function mutationObserversRun() { + return new Promise(r => { + t.step_timeout(r, 0); + }); + } + let tTree = createTestTree(test_slotchange); + + tTree.s1.assign(tTree.c1); + tTree["s2.5"].assign(tTree.c2); + + let slotChangedOrder = []; + + // Clears out pending mutation observers + await mutationObserversRun(); + + tTree.s1.addEventListener("slotchange", function() { + slotChangedOrder.push("s1"); + }); + + tTree.s3.addEventListener("slotchange", function() { + slotChangedOrder.push("s3"); + }); + + tTree["s2.5"].addEventListener("slotchange", function() { + slotChangedOrder.push("s2.5"); + }); + + tTree.s3.assign(tTree.c2, tTree.c1); + await mutationObserversRun(); + assert_array_equals(slotChangedOrder, ["s1", "s2.5", "s3"]); +}, 'Signal a slot change should be done in tree order.'); +</script> |