browser_tester 1.5.0

Deterministic lightweight browser runtime for Rust tests
Documentation
use super::*;

#[test]
fn named_node_map_get_named_item_item_and_live_length() -> Result<()> {
    let html = r#"
        <div id='box' class='green'></div>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const box = document.getElementById('box');
            const attrs = box.attributes;
            const beforeLen = box.attributes.length;
            const idAttr = attrs.getNamedItem('ID');
            const first = attrs.item(0);

            box.setAttribute('data-x', '1');
            const afterLen = box.attributes.length;
            const missing = attrs.getNamedItem('missing') === null;
            const dataValue = attrs.getNamedItem('data-x').value;

            document.getElementById('result').textContent = [
              idAttr !== null ? idAttr.value === 'box' : false,
              first !== null,
              beforeLen,
              afterLen,
              missing,
              dataValue
            ].join(':');
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text("#result", "true:true:2:3:true:1")?;
    Ok(())
}

#[test]
fn named_node_map_set_named_item_adds_and_replaces() -> Result<()> {
    let html = r#"
        <div id='box'></div>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const box = document.getElementById('box');
            const attrs = box.attributes;

            const first = document.createAttribute('data-state');
            first.value = 'ready';
            const replacedFirst = attrs.setNamedItem(first);

            const second = document.createAttribute('DATA-STATE');
            second.value = 'done';
            const replacedSecond = attrs.setNamedItem(second);

            document.getElementById('result').textContent = [
              replacedFirst === null,
              replacedSecond !== null ? replacedSecond.value : 'none',
              replacedSecond !== null ? replacedSecond.ownerElement === null : false,
              second.ownerElement === box,
              box.getAttribute('data-state')
            ].join(':');
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text("#result", "true:ready:true:true:done")?;
    Ok(())
}

#[test]
fn named_node_map_remove_named_item_returns_removed_attr_and_throws_when_missing() -> Result<()> {
    let html = r#"
        <div id='box' lang='en-US'></div>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const box = document.getElementById('box');
            const attrs = box.attributes;
            const removed = attrs.removeNamedItem('lang');

            let threw = false;
            try {
              attrs.removeNamedItem('lang');
            } catch (e) {
              threw = String(e).includes('NotFoundError');
            }

            document.getElementById('result').textContent = [
              removed.name,
              removed.value,
              removed.ownerElement === null,
              box.hasAttribute('lang'),
              threw
            ].join(':');
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text("#result", "lang:en-US:true:false:true")?;
    Ok(())
}

#[test]
fn named_node_map_namespaced_get_and_remove() -> Result<()> {
    let html = r#"
        <div id='box' xmlns:spec='http://example.com/ns' spec:align='left'></div>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const ns = 'http://example.com/ns';
            const box = document.getElementById('box');
            const attrs = box.attributes;
            const found = attrs.getNamedItemNS(ns, 'align');
            const removed = attrs.removeNamedItemNS(ns, 'align');

            let threw = false;
            try {
              attrs.removeNamedItemNS(ns, 'align');
            } catch (e) {
              threw = String(e).includes('NotFoundError');
            }

            document.getElementById('result').textContent = [
              found !== null ? found.name : 'none',
              removed !== null ? removed.value : 'none',
              removed !== null ? removed.ownerElement === null : false,
              box.getAttributeNS(ns, 'align') === null,
              threw
            ].join(':');
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text("#result", "spec:align:left:true:true:true")?;
    Ok(())
}

#[test]
fn named_node_map_set_named_item_ns_adds_and_replaces() -> Result<()> {
    let html = r#"
        <div id='one' xmlns:m='http://example.com/ns' m:flag='old'></div>
        <div id='two' xmlns:m='http://example.com/ns'></div>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const ns = 'http://example.com/ns';
            const one = document.getElementById('one');
            const two = document.getElementById('two');

            const donor = one.attributes.getNamedItemNS(ns, 'flag');
            const replacedFirst = two.attributes.setNamedItemNS(donor);
            const firstValue = two.getAttributeNS(ns, 'flag');

            const host = document.createElement('div');
            host.innerHTML =
              "<span xmlns:m='http://example.com/ns' m:flag='new'></span>";
            const secondAttr = host.firstElementChild.getAttributeNodeNS(ns, 'flag');
            const replacedSecond = two.attributes.setNamedItemNS(secondAttr);

            document.getElementById('result').textContent = [
              replacedFirst === null,
              firstValue,
              replacedSecond !== null ? replacedSecond.value : 'none',
              replacedSecond !== null ? replacedSecond.ownerElement === null : false,
              secondAttr.ownerElement === two,
              two.getAttributeNS(ns, 'flag')
            ].join(':');
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text("#result", "true:old:old:true:true:new")?;
    Ok(())
}

#[test]
fn named_node_map_raw_getter_methods_and_iterators_are_receiver_aware_work() -> Result<()> {
    let html = r#"
        <div id='box' class='green' data-state='ready'></div>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const attrs = document.getElementById('box').attributes;
            const item = attrs.item;
            const getNamedItem = attrs.getNamedItem;
            const values = attrs.values;
            const entries = attrs.entries;
            const iter = attrs[Symbol.iterator];

            let incompatible = false;
            try {
              Object.create(attrs).getNamedItem('class');
            } catch (e) {
              incompatible = String(e).includes('incompatible receiver');
            }

            document.getElementById('result').textContent = [
              item.call(attrs, 0).name,
              getNamedItem.call(attrs, 'class').value,
              Array.from(values.call(attrs)).map((attr) => attr.name).join(','),
              Array.from(entries.call(attrs))
                .map((pair) => pair[0] + ':' + pair[1].name)
                .join(','),
              Array.from(iter.call(attrs)).map((attr) => attr.value).join(','),
              typeof attrs.getNamedItem,
              typeof attrs.values,
              incompatible
            ].join('|');
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "class|green|class,data-state,id|0:class,1:data-state,2:id|green,ready,box|function|function|true",
    )?;
    Ok(())
}

#[test]
fn named_node_map_named_property_collisions_follow_builtin_visibility_work() -> Result<()> {
    let html = r#"
        <div normal='ok' item='one' getNamedItem='two' values='three' toString='four' constructor='five' length='six'></div>
        <p id='result'></p>
        <script>
          const attrs = document.querySelector('div').attributes;
          const assigned = Object.assign({}, attrs);
          const spread = { ...attrs };

          document.getElementById('result').textContent = [
            typeof attrs.item,
            typeof attrs.getNamedItem,
            typeof attrs.values,
            typeof attrs['toString'],
            typeof attrs['constructor'],
            attrs.normal.value,
            Object.keys(attrs).includes('item'),
            Object.keys(attrs).includes('values'),
            Object.keys(attrs).includes('normal'),
            Object.getOwnPropertyNames(attrs).includes('toString'),
            Object.getOwnPropertyNames(attrs).includes('constructor'),
            Object.hasOwn(attrs, 'item'),
            Object.hasOwn(attrs, 'toString'),
            Object.hasOwn(attrs, 'constructor'),
            Object.getOwnPropertyDescriptor(attrs, 'item') === undefined,
            Object.getOwnPropertyDescriptor(attrs, 'values') === undefined,
            Object.getOwnPropertyDescriptor(attrs, 'toString') === undefined,
            attrs.length,
            Object.getOwnPropertyDescriptor(attrs, 'length').value,
            assigned.normal.value,
            'item' in assigned,
            'values' in spread
          ].join('|');
        </script>
        "#;

    let h = Harness::from_html(html)?;
    h.assert_text(
        "#result",
        "function|function|function|function|function|ok|false|false|true|false|false|false|false|false|true|true|true|7|7|ok|false|false",
    )?;
    Ok(())
}