<!DOCTYPE html>
<html>
<head>
<title>Shadow DOM: slotchange event in fallback content</title>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<link rel="help" href="https://dom.spec.whatwg.org/#signaling-slot-change">
<link rel="help" href="https://dom.spec.whatwg.org/#concept-node-insert">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>

function generateTests(...args) {
    testMutatingSlot('closed', true, ...args);
    testMutatingSlot('closed', false, ...args);
    testMutatingSlot('open', true, ...args);
    testMutatingSlot('open', false, ...args);
}

function testMutatingSlot(mode, connectedToDocument, hostContent, slotName, prepareSlot, mutateSlot, description)
{
    promise_test(async function () {
        const host = document.createElement('div');
        if (connectedToDocument)
            document.body.appendChild(host);
        if (hostContent)
            host.innerHTML = hostContent;

        const shadowRoot = host.attachShadow({mode});

        const slot = document.createElement('slot');
        let eventCount = 0;
        if (slotName)
            slot.name = slotName;
        prepareSlot(slot);
        slot.addEventListener('slotchange', () => eventCount++);

        shadowRoot.appendChild(slot);

        await Promise.resolve();
        eventCount = 0;

        mutateSlot(slot);
        await Promise.resolve();
        assert_equals(eventCount, hostContent ? 0 : 1);

        host.remove();
    }, description + ` in a ${connectedToDocument ? 'connected' : 'disconnected'} ${mode} mode shadow root`);
}

generateTests(null, null, () => { }, (slot) => slot.append('some text'),
    'slotchange event should be fired when appending a text node to a default slot whose assigned nodes is empty');
generateTests(null, 'foo', () => { },  (slot) => slot.append('some text'),
    'slotchange event should be fired when appending a text node to a named slot whose assigned nodes is empty');
generateTests(null, null, () => { }, (slot) => slot.append(document.createTextNode('div')),
    'slotchange event should be fired when appending a div to a default slot whose assigned nodes is empty');
generateTests(null, 'foo', () => { }, (slot) => slot.append(document.createTextNode('div')),
    'slotchange event should be fired when appending a div to a named slot whose assigned nodes is empty');

generateTests(null, null, (slot) => slot.append('hello'), (slot) => slot.firstChild.remove(),
    'slotchange event should be fired when removing a text node from a default slot whose assigned nodes is empty');
generateTests(null, 'foo', (slot) => slot.append('hello'), (slot) => slot.firstChild.remove(),
    'slotchange event should be fired when removing a text node from a named slot whose assigned nodes is empty');
generateTests(null, 'foo', (slot) => slot.append(document.createElement('div')), (slot) => slot.firstChild.remove(),
    'slotchange event should be fired when removing a div from a default slot whose assigned nodes is empty');
generateTests(null, 'foo', (slot) => slot.append(document.createElement('div')), (slot) => slot.firstChild.remove(),
    'slotchange event should be fired when removing a div from a named slot whose assigned nodes is empty');

generateTests('hello', null, () => { }, (slot) => slot.append('some text'),
    'slotchange event should not be fired when appending a text node to a default slot whose assigned nodes is not empty');
generateTests('<div slot="foo"></div>', 'foo', () => { }, (slot) => slot.append('some text'),
    'slotchange event should not be fired when appending a text node to a named slot whose assigned nodes is not empty');
generateTests('<div></div>', null, () => { }, (slot) => slot.append(document.createTextNode('div')),
    'slotchange event should not be fired when appending a div to a default slot whose assigned nodes is not empty');
generateTests('<div slot="foo"></div>', 'foo', () => { }, (slot) => slot.append(document.createTextNode('div')),
    'slotchange event should not be fired when appending a div to a named slot whose assigned nodes is not empty');

generateTests('<span></span>', null, (slot) => slot.append('hello'), (slot) => slot.firstChild.remove(),
    'slotchange event should not be fired when removing a text node from a default slot whose assigned nodes is empty');
generateTests('<span slot="foo"></span>', 'foo', (slot) => slot.append('hello'), (slot) => slot.firstChild.remove(),
    'slotchange event should not be fired when removing a text node from a named slot whose assigned nodes is empty');
generateTests('<span></span>', null, (slot) => slot.append(document.createElement('div')), (slot) => slot.firstChild.remove(),
    'slotchange event should not be fired when removing a div from a default slot whose assigned nodes is empty');
generateTests('<span slot="foo"></span>', 'foo', (slot) => slot.append(document.createElement('div')), (slot) => slot.firstChild.remove(),
    'slotchange event should not be fired when removing a div from a named slot whose assigned nodes is empty');

</script>
</body>
</html>
