Automatic update from web-platform-tests HTML: headingoffset computation should not use the flat tree See https://github.com/whatwg/html/pull/12562 for context. -- wpt-commits: 691d45da216ab3677fa2b51daa903ec0b381ae3f wpt-pr: 60525
722 lines
28 KiB
HTML
722 lines
28 KiB
HTML
<!doctype html>
|
|
<meta charset="utf-8" />
|
|
<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" />
|
|
<link rel="help" href="https://github.com/whatwg/html/pull/11086" />
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
|
|
<div>
|
|
<h1 id="testMutation"></h1>
|
|
</div>
|
|
|
|
<div id="testNestedOuter">
|
|
<div id="testNestedInner">
|
|
<h1 id="testNested"></h1>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="testResetMutationContainer" headingoffset="3">
|
|
<h1 id="testResetMutation"></h1>
|
|
</div>
|
|
|
|
<div id="testResetInteractionOuter" headingoffset="5">
|
|
<div id="testResetInteractionInner">
|
|
<h1 id="testResetInteraction"></h1>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="testAllHeadingsContainer">
|
|
<h1 id="testAllH1"></h1>
|
|
<h2 id="testAllH2"></h2>
|
|
<h3 id="testAllH3"></h3>
|
|
<h4 id="testAllH4"></h4>
|
|
<h5 id="testAllH5"></h5>
|
|
<h6 id="testAllH6"></h6>
|
|
</div>
|
|
|
|
<div id="testDeepNestingContainer">
|
|
<div>
|
|
<div>
|
|
<h1 id="testDeepNesting"></h1>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="testShadowHost">
|
|
<template shadowrootmode="open">
|
|
<h1 id="testShadowHostH1"></h1>
|
|
</template>
|
|
</div>
|
|
|
|
<div id="testShadowAncestor">
|
|
<div id="testShadowAncestorHost">
|
|
<template shadowrootmode="open">
|
|
<h1 id="testShadowAncestorH1"></h1>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="testShadowInner">
|
|
<template shadowrootmode="open">
|
|
<div id="testShadowInnerContainer">
|
|
<h1 id="testShadowInnerH1"></h1>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<div id="testSlotted">
|
|
<template shadowrootmode="open">
|
|
<slot id="testSlottedSlot"></slot>
|
|
</template>
|
|
<h1 id="testSlottedH1"></h1>
|
|
</div>
|
|
|
|
<div id="testSlottedNested">
|
|
<template shadowrootmode="open">
|
|
<slot id="testSlottedNestedSlot"></slot>
|
|
</template>
|
|
<div id="testSlottedNestedChild">
|
|
<template shadowrootmode="open">
|
|
<h1 id="testSlottedNestedH1"></h1>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h1 id="testClamping"></h1>
|
|
</div>
|
|
|
|
<div>
|
|
<h1 id="testInvalid"></h1>
|
|
</div>
|
|
|
|
<div id="testSlotNameChange">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="2">
|
|
<slot name="a" id="testSlotNameChangeSlotA"></slot>
|
|
</div>
|
|
<div headingoffset="4">
|
|
<slot name="b" id="testSlotNameChangeSlotB"></slot>
|
|
</div>
|
|
</template>
|
|
<h1 slot="a" id="testSlotNameChangeH1"></h1>
|
|
</div>
|
|
|
|
<div id="testSlotAttrChange">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="1">
|
|
<slot name="x" id="testSlotAttrChangeSlotX"></slot>
|
|
</div>
|
|
<div headingoffset="3">
|
|
<slot name="y" id="testSlotAttrChangeSlotY"></slot>
|
|
</div>
|
|
</template>
|
|
<h1 slot="x" id="testSlotAttrChangeH1"></h1>
|
|
</div>
|
|
|
|
<div id="testSlotRemoval">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="5">
|
|
<slot id="testSlotRemovalSlot"></slot>
|
|
</div>
|
|
</template>
|
|
<h1 id="testSlotRemovalH1"></h1>
|
|
</div>
|
|
|
|
<div id="testSlotDefault">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="2">
|
|
<slot id="testSlotDefaultSlot"></slot>
|
|
</div>
|
|
</template>
|
|
<h1 id="testSlotDefaultH1"></h1>
|
|
</div>
|
|
|
|
<div id="testSlotMultiple">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="1">
|
|
<slot name="first" id="testSlotMultipleSlotFirst"></slot>
|
|
</div>
|
|
<div headingoffset="3">
|
|
<slot name="second" id="testSlotMultipleSlotSecond"></slot>
|
|
</div>
|
|
</template>
|
|
<h1 slot="first" id="testSlotMultipleH1A"></h1>
|
|
<h1 slot="second" id="testSlotMultipleH1B"></h1>
|
|
</div>
|
|
|
|
<div id="testNestedShadow" headingoffset="1">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="2" id="testNestedShadowInner">
|
|
<slot id="testNestedShadowSlot"></slot>
|
|
</div>
|
|
</template>
|
|
<div id="testNestedShadowChild">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="3" id="testNestedShadowChildInner">
|
|
<h1 id="testNestedShadowH1"></h1>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="testTripleShadow">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="1" id="testTripleShadowL1">
|
|
<slot id="testTripleShadowSlot1"></slot>
|
|
</div>
|
|
</template>
|
|
<div id="testTripleShadowChild1">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="1" id="testTripleShadowL2">
|
|
<slot id="testTripleShadowSlot2"></slot>
|
|
</div>
|
|
</template>
|
|
<div id="testTripleShadowChild2">
|
|
<template shadowrootmode="open">
|
|
<h1 id="testTripleShadowH1"></h1>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="testSvgSlotted">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="2">
|
|
<slot id="testSvgSlottedSlot"></slot>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<div id="testNestedShadowHeadings">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="1" id="testNestedShadowHeadingsL1">
|
|
<slot id="testNestedShadowHeadingsSlot"></slot>
|
|
<h1 id="testNestedShadowHeadingsInnerH1"></h1>
|
|
</div>
|
|
</template>
|
|
<div id="testNestedShadowHeadingsChild">
|
|
<template shadowrootmode="open">
|
|
<div headingoffset="2" id="testNestedShadowHeadingsL2">
|
|
<h1 id="testNestedShadowHeadingsChildH1"></h1>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="testManualSlot"></div>
|
|
|
|
<div id="testManualSlotMultiple"></div>
|
|
|
|
<script>
|
|
const levels = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
const matchAllLevels = (el) => levels.map((l) => el.matches(`:heading(${l})`));
|
|
const assertLevel = (el, expected, msg) => {
|
|
const bools = matchAllLevels(el);
|
|
const expected_bools = levels.map((l) => l == expected);
|
|
assert_array_equals(bools, expected_bools, msg);
|
|
};
|
|
|
|
for (const style of ["property", "attribute"]) {
|
|
const set = (el, val) => {
|
|
if (style === "property") {
|
|
el.headingOffset = val;
|
|
} else {
|
|
el.setAttribute("headingoffset", String(val));
|
|
}
|
|
};
|
|
const clear = (el) => {
|
|
if (style === "property") {
|
|
el.headingOffset = 0;
|
|
} else {
|
|
el.removeAttribute("headingoffset");
|
|
}
|
|
};
|
|
|
|
test(t => {
|
|
const container = testMutation.parentElement;
|
|
t.add_cleanup(() => clear(container));
|
|
|
|
assertLevel(testMutation, 1, "h1 starts at level 1");
|
|
set(container, 2);
|
|
assertLevel(testMutation, 3, "h1 becomes level 3 after parent gets headingoffset=2");
|
|
set(container, 5);
|
|
assertLevel(testMutation, 6, "h1 becomes level 6 after parent headingoffset changes to 5");
|
|
clear(container);
|
|
assertLevel(testMutation, 1, "h1 returns to level 1 after parent headingoffset removed");
|
|
}, `headingoffset ${style} mutations update descendant headings`);
|
|
|
|
test(t => {
|
|
t.add_cleanup(() => {
|
|
clear(testNestedOuter);
|
|
clear(testNestedInner);
|
|
});
|
|
|
|
assertLevel(testNested, 1, "h1 starts at level 1");
|
|
set(testNestedOuter, 1);
|
|
assertLevel(testNested, 2, "h1 becomes level 2 after outer headingOffset=1");
|
|
set(testNestedInner, 2);
|
|
assertLevel(testNested, 4, "h1 becomes level 4 after inner headingOffset=2 (1+2+1)");
|
|
set(testNestedOuter, 3);
|
|
assertLevel(testNested, 6, "h1 becomes level 6 after outer headingOffset changes to 3 (3+2+1)");
|
|
clear(testNestedInner);
|
|
assertLevel(testNested, 4, "h1 becomes level 4 after inner headingOffset=0 (3+0+1)");
|
|
}, `nested headingoffset ${style} mutations propagate to descendants`);
|
|
|
|
test(t => {
|
|
t.add_cleanup(() => { testAllHeadingsContainer.headingOffset = 0; });
|
|
|
|
assertLevel(testAllH1, 1, "h1 starts at level 1");
|
|
assertLevel(testAllH2, 2, "h2 starts at level 2");
|
|
assertLevel(testAllH3, 3, "h3 starts at level 3");
|
|
assertLevel(testAllH4, 4, "h4 starts at level 4");
|
|
assertLevel(testAllH5, 5, "h5 starts at level 5");
|
|
assertLevel(testAllH6, 6, "h6 starts at level 6");
|
|
|
|
set(testAllHeadingsContainer, 2);
|
|
|
|
assertLevel(testAllH1, 3, "h1 becomes level 3");
|
|
assertLevel(testAllH2, 4, "h2 becomes level 4");
|
|
assertLevel(testAllH3, 5, "h3 becomes level 5");
|
|
assertLevel(testAllH4, 6, "h4 becomes level 6");
|
|
assertLevel(testAllH5, 7, "h5 becomes level 7");
|
|
assertLevel(testAllH6, 8, "h6 becomes level 8");
|
|
}, `headingoffset ${style} mutations update all heading types`);
|
|
|
|
test(t => {
|
|
t.add_cleanup(() => clear(testDeepNestingContainer));
|
|
|
|
assertLevel(testDeepNesting, 1, "deeply nested h1 starts at level 1");
|
|
set(testDeepNestingContainer, 3);
|
|
assertLevel(testDeepNesting, 4, "deeply nested h1 becomes level 4 after ancestor mutation");
|
|
}, `headingoffset ${style} mutations propagate to deeply nested desendants`);
|
|
|
|
test(t => {
|
|
const h1 = testShadowHost.shadowRoot.getElementById("testShadowHostH1");
|
|
t.add_cleanup(() => clear(testShadowHost));
|
|
|
|
assertLevel(h1, 1, "h1 in shadow DOM starts at level 1");
|
|
set(testShadowHost, 2);
|
|
assertLevel(h1, 3, "h1 in shadow DOM becomes level 3 after host headingoffset=2");
|
|
clear(testShadowHost);
|
|
assertLevel(h1, 1, "h1 in shadow DOM returns to level 1 after host headingoffset=0");
|
|
}, `headingoffset ${style} mutations on shadow host update shadow DOM headings`);
|
|
|
|
test(t => {
|
|
const h1 = testShadowAncestorHost.shadowRoot.getElementById("testShadowAncestorH1");
|
|
t.add_cleanup(() => {
|
|
clear(testShadowAncestor);
|
|
clear(testShadowAncestorHost);
|
|
});
|
|
|
|
assertLevel(h1, 1, "h1 in shadow DOM starts at level 1");
|
|
set(testShadowAncestor, 2);
|
|
assertLevel(h1, 3, "h1 in shadow DOM becomes level 3 after outer headingoffset=2");
|
|
set(testShadowAncestorHost, 1);
|
|
assertLevel(h1, 4, "h1 in shadow DOM becomes level 4 after host headingoffset=1 (2+1+1)");
|
|
}, `headingoffset ${style} mutations on ancestors of shadow host propagate to shadow DOM`);
|
|
|
|
test(t => {
|
|
const innerContainer = testShadowInner.shadowRoot.getElementById("testShadowInnerContainer");
|
|
const h1 = testShadowInner.shadowRoot.getElementById("testShadowInnerH1");
|
|
t.add_cleanup(() => {
|
|
clear(testShadowInner);
|
|
clear(innerContainer);
|
|
});
|
|
|
|
assertLevel(h1, 1, "h1 starts at level 1");
|
|
set(innerContainer, 2);
|
|
assertLevel(h1, 3, "h1 becomes level 3 after shadow DOM container headingoffset=2");
|
|
set(testShadowInner, 1);
|
|
assertLevel(h1, 4, "h1 becomes level 4 after host headingoffset=1 (1+2+1)");
|
|
}, `headingoffset ${style} mutations inside shadow DOM update correctly`);
|
|
|
|
test(t => {
|
|
const slot = testSlotted.shadowRoot.getElementById("testSlottedSlot");
|
|
t.add_cleanup(() => {
|
|
clear(testSlotted);
|
|
clear(slot);
|
|
});
|
|
|
|
assertLevel(testSlottedH1, 1, "slotted h1 starts at level 1");
|
|
set(slot, 2);
|
|
assertLevel(testSlottedH1, 1, "slot headingoffset=2 does not affect slotted h1");
|
|
set(testSlotted, 1);
|
|
assertLevel(testSlottedH1, 2, "slotted h1 becomes level 2 after host headingoffset=1");
|
|
}, `headingoffset ${style} mutations on host (not slot) update slotted headings`);
|
|
|
|
test(t => {
|
|
const slot = testSlottedNested.shadowRoot.getElementById("testSlottedNestedSlot");
|
|
const h1 = testSlottedNestedChild.shadowRoot.getElementById("testSlottedNestedH1");
|
|
t.add_cleanup(() => {
|
|
clear(testSlottedNested);
|
|
clear(slot);
|
|
});
|
|
|
|
assertLevel(h1, 1, "h1 in nested shadow starts at level 1");
|
|
set(slot, 2);
|
|
assertLevel(h1, 1, "outer slot headingoffset=2 does not affect h1");
|
|
set(testSlottedNested, 1);
|
|
assertLevel(h1, 2, "h1 becomes level 2 after outermost host headingoffset=1");
|
|
}, `headingoffset ${style} mutations propagate via host chain through nested shadow roots`);
|
|
|
|
test(t => {
|
|
const container = testClamping.parentElement;
|
|
t.add_cleanup(() => clear(container));
|
|
|
|
assertLevel(testClamping, 1, "h1 starts at level 1");
|
|
set(container, 10);
|
|
assertLevel(testClamping, 9, "h1 clamped to level 9 with headingoffset=10");
|
|
set(container, 100);
|
|
assertLevel(testClamping, 9, "h1 still clamped to level 9 with headingoffset=100");
|
|
}, `headingoffset ${style} mutations respect level clamping`);
|
|
|
|
test(t => {
|
|
const innerDiv = testNestedShadow.shadowRoot.getElementById("testNestedShadowInner");
|
|
const innerShadowDiv = testNestedShadowChild.shadowRoot.getElementById("testNestedShadowChildInner");
|
|
const h1 = testNestedShadowChild.shadowRoot.getElementById("testNestedShadowH1");
|
|
t.add_cleanup(() => {
|
|
set(testNestedShadow, 1);
|
|
set(innerDiv, 2);
|
|
set(innerShadowDiv, 3);
|
|
});
|
|
|
|
assertLevel(h1, 5, "h1 in nested shadow starts at level 5 (3+1+1)");
|
|
set(testNestedShadow, 0);
|
|
assertLevel(h1, 4, "h1 becomes level 4 after outermost headingoffset=0 (3+0+1)");
|
|
set(innerDiv, 0);
|
|
assertLevel(h1, 4, "outer shadow's slot wrapper does not affect h1");
|
|
set(innerShadowDiv, 0);
|
|
assertLevel(h1, 1, "h1 becomes level 1 after innermost wrapper headingoffset=0");
|
|
set(testNestedShadow, 2);
|
|
assertLevel(h1, 3, "h1 becomes level 3 after outermost headingoffset=2 (0+2+1)");
|
|
}, `headingoffset ${style} mutations propagate via host chain across two levels of shadow DOM`);
|
|
|
|
test(t => {
|
|
const l1 = testTripleShadow.shadowRoot.getElementById("testTripleShadowL1");
|
|
const l2 = testTripleShadowChild1.shadowRoot.getElementById("testTripleShadowL2");
|
|
const h1 = testTripleShadowChild2.shadowRoot.getElementById("testTripleShadowH1");
|
|
t.add_cleanup(() => {
|
|
set(l1, 1);
|
|
set(l2, 1);
|
|
clear(testTripleShadow);
|
|
clear(testTripleShadowChild1);
|
|
});
|
|
|
|
assertLevel(h1, 1, "h1 in triple shadow starts at level 1");
|
|
set(l1, 3);
|
|
assertLevel(h1, 1, "l1 in outer shadow tree does not affect h1");
|
|
set(l2, 2);
|
|
assertLevel(h1, 1, "l2 in middle shadow tree does not affect h1");
|
|
set(testTripleShadow, 2);
|
|
assertLevel(h1, 3, "h1 becomes level 3 after outermost host headingoffset=2 (2+1)");
|
|
set(testTripleShadowChild1, 1);
|
|
assertLevel(h1, 4, "h1 becomes level 4 after middle host headingoffset=1 (2+1+1)");
|
|
clear(testTripleShadow);
|
|
assertLevel(h1, 2, "h1 becomes level 2 after outermost cleared (1+1)");
|
|
}, `headingoffset ${style} propagates only via host chain through three nested shadow DOMs`);
|
|
|
|
test(t => {
|
|
const l1 = testNestedShadowHeadings.shadowRoot.getElementById("testNestedShadowHeadingsL1");
|
|
const l2 = testNestedShadowHeadingsChild.shadowRoot.getElementById("testNestedShadowHeadingsL2");
|
|
const innerH1 = testNestedShadowHeadings.shadowRoot.getElementById("testNestedShadowHeadingsInnerH1");
|
|
const childH1 = testNestedShadowHeadingsChild.shadowRoot.getElementById("testNestedShadowHeadingsChildH1");
|
|
t.add_cleanup(() => {
|
|
set(l1, 1);
|
|
set(l2, 2);
|
|
});
|
|
|
|
assertLevel(innerH1, 2, "inner h1 starts at level 2 (1+1)");
|
|
assertLevel(childH1, 3, "child h1 starts at level 3 (2+1)");
|
|
set(l1, 3);
|
|
assertLevel(innerH1, 4, "inner h1 becomes level 4 after l1 headingoffset=3 (3+1)");
|
|
assertLevel(childH1, 3, "l1 in outer shadow tree does not affect child h1");
|
|
set(l2, 0);
|
|
assertLevel(innerH1, 4, "inner h1 stays at level 4 (l2 change doesn't affect it)");
|
|
assertLevel(childH1, 1, "child h1 becomes level 1 after l2 headingoffset=0");
|
|
}, `headingoffset ${style} mutations update headings at multiple shadow DOM levels independently`);
|
|
}
|
|
|
|
test(t => {
|
|
t.add_cleanup(() => {
|
|
testResetMutationContainer.headingOffset = 3;
|
|
testResetMutationContainer.headingReset = false;
|
|
});
|
|
|
|
assertLevel(testResetMutation, 4, "h1 starts at level 4 (3+1)");
|
|
testResetMutationContainer.headingReset = true;
|
|
assertLevel(testResetMutation, 4, "h1 stays at level 4 after parent gets headingreset (still 3+1, but now reset)");
|
|
testResetMutationContainer.headingOffset = 1;
|
|
assertLevel(testResetMutation, 2, "h1 becomes level 2 after parent headingoffset changes to 1");
|
|
testResetMutationContainer.headingReset = false;
|
|
assertLevel(testResetMutation, 2, "h1 stays level 2 after headingreset removed (still 1+1)");
|
|
}, "headingreset mutations update descendant headings");
|
|
|
|
test(t => {
|
|
t.add_cleanup(() => {
|
|
testResetInteractionOuter.headingOffset = 5;
|
|
testResetInteractionInner.headingOffset = 0;
|
|
testResetInteractionInner.headingReset = false;
|
|
});
|
|
|
|
assertLevel(testResetInteraction, 6, "h1 starts at level 6 (5+1)");
|
|
testResetInteractionInner.headingReset = true;
|
|
assertLevel(testResetInteraction, 1, "h1 becomes level 1 after inner gets headingreset");
|
|
testResetInteractionInner.headingOffset = 2;
|
|
assertLevel(testResetInteraction, 3, "h1 becomes level 3 after inner headingoffset=2 (2+1, reset blocks outer)");
|
|
testResetInteractionInner.headingReset = false;
|
|
assertLevel(testResetInteraction, 8, "h1 becomes level 8 after inner headingreset removed (5+2+1)");
|
|
}, "headingreset and headingoffset mutations interact correctly");
|
|
|
|
test(t => {
|
|
const container = testInvalid.parentElement;
|
|
t.add_cleanup(() => container.removeAttribute("headingoffset"));
|
|
|
|
container.headingOffset = 2;
|
|
assertLevel(testInvalid, 3, "h1 at level 3 with headingoffset=2");
|
|
assert_equals(container.headingOffset, 2, "headingOffset property returns 2");
|
|
|
|
container.setAttribute("headingoffset", "invalid");
|
|
assertLevel(testInvalid, 1, "h1 returns to level 1 with invalid headingoffset");
|
|
assert_equals(container.headingOffset, 0, "headingOffset proprety returns 0 for invalid value");
|
|
|
|
container.setAttribute("headingoffset", "3");
|
|
assertLevel(testInvalid, 4, "h1 at level 4 with headingoffset=3");
|
|
assert_equals(container.headingOffset, 3, "headingOffset property returns 3");
|
|
}, "invalid headingoffset attribute values are handled correctly on mutation");
|
|
|
|
test(t => {
|
|
const slotA = testSlotNameChange.shadowRoot.getElementById("testSlotNameChangeSlotA");
|
|
const slotB = testSlotNameChange.shadowRoot.getElementById("testSlotNameChangeSlotB");
|
|
t.add_cleanup(() => {
|
|
slotA.name = "a";
|
|
testSlotNameChangeH1.slot = "a";
|
|
testSlotNameChange.removeAttribute("headingoffset");
|
|
});
|
|
|
|
assertLevel(testSlotNameChangeH1, 1, "h1 slotted into slot a starts at level 1");
|
|
slotA.name = "c";
|
|
assertLevel(testSlotNameChangeH1, 1, "h1 stays at level 1 when slot a renamed");
|
|
slotB.name = "a";
|
|
assertLevel(testSlotNameChangeH1, 1, "h1 stays at level 1 when reslotted into slot b");
|
|
testSlotNameChange.setAttribute("headingoffset", "2");
|
|
assertLevel(testSlotNameChangeH1, 3, "h1 becomes level 3 after host headingoffset=2");
|
|
}, "slot name attribute changes do not change slotted heading levels");
|
|
|
|
test(t => {
|
|
t.add_cleanup(() => {
|
|
testSlotAttrChangeH1.slot = "x";
|
|
testSlotAttrChange.removeAttribute("headingoffset");
|
|
});
|
|
|
|
assertLevel(testSlotAttrChangeH1, 1, "h1 with slot=x starts at level 1");
|
|
testSlotAttrChangeH1.slot = "y";
|
|
assertLevel(testSlotAttrChangeH1, 1, "h1 stays at level 1 when moved to slot y");
|
|
testSlotAttrChangeH1.slot = "nonexistent";
|
|
assertLevel(testSlotAttrChangeH1, 1, "h1 stays at level 1 when slot points to nonexistent slot");
|
|
testSlotAttrChangeH1.slot = "x";
|
|
testSlotAttrChange.setAttribute("headingoffset", "3");
|
|
assertLevel(testSlotAttrChangeH1, 4, "h1 becomes level 4 after host headingoffset=3");
|
|
}, "element slot attribute changes do not change heading levels");
|
|
|
|
test(t => {
|
|
const slot = testSlotRemoval.shadowRoot.getElementById("testSlotRemovalSlot");
|
|
const container = slot.parentElement;
|
|
t.add_cleanup(() => {
|
|
container.appendChild(slot);
|
|
testSlotRemoval.removeAttribute("headingoffset");
|
|
});
|
|
|
|
assertLevel(testSlotRemovalH1, 1, "h1 starts at level 1");
|
|
slot.remove();
|
|
assertLevel(testSlotRemovalH1, 1, "h1 stays at level 1 when slot removed");
|
|
container.appendChild(slot);
|
|
assertLevel(testSlotRemovalH1, 1, "h1 stays at level 1 when slot re-added");
|
|
testSlotRemoval.setAttribute("headingoffset", "4");
|
|
assertLevel(testSlotRemovalH1, 5, "h1 becomes level 5 after host headingoffset=4");
|
|
}, "slot element removal and addition do not change slotted heading levels");
|
|
|
|
test(t => {
|
|
const slot = testSlotDefault.shadowRoot.getElementById("testSlotDefaultSlot");
|
|
t.add_cleanup(() => {
|
|
slot.removeAttribute("name");
|
|
testSlotDefaultH1.removeAttribute("slot");
|
|
testSlotDefault.removeAttribute("headingoffset");
|
|
});
|
|
|
|
assertLevel(testSlotDefaultH1, 1, "h1 in default slot starts at level 1");
|
|
slot.name = "named";
|
|
assertLevel(testSlotDefaultH1, 1, "h1 stays at level 1 when default slot becomes named");
|
|
testSlotDefaultH1.slot = "named";
|
|
assertLevel(testSlotDefaultH1, 1, "h1 stays at level 1 when explicitly assigned to named slot");
|
|
slot.removeAttribute("name");
|
|
assertLevel(testSlotDefaultH1, 1, "h1 stays at level 1 when slot becomes default but h1 has explicit slot attr");
|
|
testSlotDefaultH1.removeAttribute("slot");
|
|
testSlotDefault.setAttribute("headingoffset", "2");
|
|
assertLevel(testSlotDefaultH1, 3, "h1 becomes level 3 after host headingoffset=2");
|
|
}, "default-to-named slot transitions do not change heading levels");
|
|
|
|
test(t => {
|
|
const slotFirst = testSlotMultiple.shadowRoot.getElementById("testSlotMultipleSlotFirst");
|
|
const slotSecond = testSlotMultiple.shadowRoot.getElementById("testSlotMultipleSlotSecond");
|
|
t.add_cleanup(() => {
|
|
slotFirst.name = "first";
|
|
slotSecond.name = "second";
|
|
testSlotMultiple.removeAttribute("headingoffset");
|
|
});
|
|
|
|
assertLevel(testSlotMultipleH1A, 1, "h1 A in slot first starts at level 1");
|
|
assertLevel(testSlotMultipleH1B, 1, "h1 B in slot second starts at level 1");
|
|
|
|
slotFirst.name = "second";
|
|
slotSecond.name = "first";
|
|
|
|
assertLevel(testSlotMultipleH1A, 1, "h1 A stays at level 1 after slot swap");
|
|
assertLevel(testSlotMultipleH1B, 1, "h1 B stays at level 1 after slot swap");
|
|
|
|
testSlotMultiple.setAttribute("headingoffset", "2");
|
|
|
|
assertLevel(testSlotMultipleH1A, 3, "h1 A becomes level 3 after host headingoffset=2");
|
|
assertLevel(testSlotMultipleH1B, 3, "h1 B becomes level 3 after host headingoffset=2");
|
|
}, "swapping slot names does not change slotted heading levels");
|
|
|
|
test(t => {
|
|
const shadow = testManualSlot.attachShadow({ mode: "open", slotAssignment: "manual" });
|
|
const slotContainer = document.createElement("div");
|
|
slotContainer.setAttribute("headingoffset", "2");
|
|
const slot = document.createElement("slot");
|
|
slotContainer.appendChild(slot);
|
|
shadow.appendChild(slotContainer);
|
|
const h1 = document.createElement("h1");
|
|
const div = document.createElement("div");
|
|
const divH1 = document.createElement("h1");
|
|
div.appendChild(divH1);
|
|
testManualSlot.appendChild(h1);
|
|
testManualSlot.appendChild(div);
|
|
t.add_cleanup(() => {
|
|
slot.assign();
|
|
h1.remove();
|
|
div.remove();
|
|
testManualSlot.removeAttribute("headingoffset");
|
|
});
|
|
|
|
assertLevel(h1, 1, "h1 starts at level 1");
|
|
assertLevel(divH1, 1, "h1 inside div starts at level 1");
|
|
|
|
slot.assign(h1);
|
|
assertLevel(h1, 1, "h1 stays at level 1 after manual slot assignment");
|
|
|
|
slot.assign(div);
|
|
assertLevel(h1, 1, "h1 stays at level 1 after unassignment");
|
|
assertLevel(divH1, 1, "h1 inside div stays at level 1 after div manually assigned");
|
|
|
|
testManualSlot.setAttribute("headingoffset", "1");
|
|
assertLevel(divH1, 2, "h1 inside manually-assigned div becomes level 2 after host headingoffset=1");
|
|
|
|
slot.assign();
|
|
assertLevel(divH1, 2, "h1 inside div stays at level 2 after slot cleared");
|
|
}, "manual slot assignment does not change heading levels");
|
|
|
|
test(t => {
|
|
const shadow = testManualSlotMultiple.attachShadow({ mode: "open", slotAssignment: "manual" });
|
|
const divA = document.createElement("div");
|
|
divA.setAttribute("headingoffset", "1");
|
|
const slotA = document.createElement("slot");
|
|
divA.appendChild(slotA);
|
|
shadow.appendChild(divA);
|
|
const divB = document.createElement("div");
|
|
divB.setAttribute("headingoffset", "3");
|
|
const slotB = document.createElement("slot");
|
|
divB.appendChild(slotB);
|
|
shadow.appendChild(divB);
|
|
const h1 = document.createElement("h1");
|
|
testManualSlotMultiple.appendChild(h1);
|
|
t.add_cleanup(() => {
|
|
slotA.assign();
|
|
slotB.assign();
|
|
h1.remove();
|
|
testManualSlotMultiple.removeAttribute("headingoffset");
|
|
});
|
|
|
|
assertLevel(h1, 1, "h1 starts at level 1 (not assigned)");
|
|
slotA.assign(h1);
|
|
assertLevel(h1, 1, "h1 stays at level 1 after assigned to slot A");
|
|
slotB.assign(h1);
|
|
assertLevel(h1, 1, "h1 stays at level 1 after reassigned to slot B");
|
|
slotA.assign(h1);
|
|
assertLevel(h1, 1, "h1 stays at level 1 after reassigned back to slot A");
|
|
testManualSlotMultiple.setAttribute("headingoffset", "2");
|
|
assertLevel(h1, 3, "h1 becomes level 3 after host headingoffset=2");
|
|
}, "manual slot reassignment between slots does not change heading levels");
|
|
|
|
test(t => {
|
|
const slot = testSvgSlotted.shadowRoot.getElementById("testSvgSlottedSlot");
|
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
|
|
const h1 = document.createElement("h1");
|
|
foreignObject.appendChild(h1);
|
|
svg.appendChild(foreignObject);
|
|
testSvgSlotted.appendChild(svg);
|
|
t.add_cleanup(() => {
|
|
svg.remove();
|
|
testSvgSlotted.removeAttribute("headingoffset");
|
|
});
|
|
|
|
assertLevel(h1, 1, "h1 inside SVG foreignObject slotted starts at level 1");
|
|
slot.parentElement.headingOffset = 4;
|
|
assertLevel(h1, 1, "slot wrapper headingoffset=4 does not affect slotted SVG content");
|
|
testSvgSlotted.setAttribute("headingoffset", "2");
|
|
assertLevel(h1, 3, "h1 becomes level 3 after host headingoffset=2");
|
|
}, "non-HTML elements (SVG) slotted with heading descendants get updated via host chain");
|
|
|
|
test(t => {
|
|
const host = document.createElement("div");
|
|
const shadow = host.attachShadow({ mode: "open" });
|
|
const slotContainer = document.createElement("div");
|
|
slotContainer.setAttribute("headingoffset", "3");
|
|
const slot = document.createElement("slot");
|
|
slotContainer.appendChild(slot);
|
|
shadow.appendChild(slotContainer);
|
|
|
|
const mathml = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math");
|
|
const h1 = document.createElement("h1");
|
|
mathml.appendChild(h1);
|
|
host.appendChild(mathml);
|
|
document.body.appendChild(host);
|
|
t.add_cleanup(() => host.remove());
|
|
|
|
assertLevel(h1, 1, "h1 inside MathML element slotted starts at level 1");
|
|
slotContainer.headingOffset = 5;
|
|
assertLevel(h1, 1, "slot wrapper headingoffset does not affect slotted MathML content");
|
|
host.headingOffset = 1;
|
|
assertLevel(h1, 2, "h1 becomes level 2 after host headingoffset=1");
|
|
}, "non-HTML elements (MathML) slotted with heading descendants get updated via host chain");
|
|
|
|
test(t => {
|
|
const host = document.createElement("div");
|
|
const shadow = host.attachShadow({ mode: "open", slotAssignment: "manual" });
|
|
const slotContainer = document.createElement("div");
|
|
slotContainer.setAttribute("headingoffset", "2");
|
|
const slot = document.createElement("slot");
|
|
slotContainer.appendChild(slot);
|
|
shadow.appendChild(slotContainer);
|
|
|
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
|
|
const h1 = document.createElement("h1");
|
|
foreignObject.appendChild(h1);
|
|
svg.appendChild(foreignObject);
|
|
host.appendChild(svg);
|
|
document.body.appendChild(host);
|
|
t.add_cleanup(() => host.remove());
|
|
|
|
assertLevel(h1, 1, "h1 inside SVG starts at level 1 (not manually assigned)");
|
|
slot.assign(svg);
|
|
assertLevel(h1, 1, "h1 inside SVG stays at level 1 after SVG manually assigned");
|
|
host.headingOffset = 2;
|
|
assertLevel(h1, 3, "h1 becomes level 3 after host headingoffset=2");
|
|
slot.assign();
|
|
assertLevel(h1, 3, "h1 stays at level 3 after manual slot cleared");
|
|
}, "manual slot assignment of non-HTML elements does not change heading levels");
|
|
</script>
|