browser_tester 1.5.0

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

#[test]
fn element_attributes_returns_live_named_node_map() -> Result<()> {
    let html = r#"
        <p id='paragraph' class='green' contenteditable>Sample Paragraph</p>
        <button id='run'>run</button>
        <pre id='result'></pre>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const paragraph = document.getElementById('paragraph');
            const attrs = paragraph.attributes;
            const sameRef = attrs === paragraph.attributes;
            const firstLength = paragraph.attributes.length;

            paragraph.setAttribute('data-state', 'ready');
            const afterLength = paragraph.attributes.length;
            let seen = '';
            let ownerMatch = true;
            for (const attr of attrs) {
              seen += `${attr.name}:${attr.value}|`;
              ownerMatch = ownerMatch && attr.ownerElement === paragraph;
            }

            document.getElementById('result').textContent = [
              sameRef,
              typeof attrs.map,
              firstLength,
              afterLength,
              seen.includes('data-state:ready'),
              ownerMatch
            ].join(':');
          });
        </script>
        "#;

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

#[test]
fn element_attributes_is_iterable_and_yields_attr_nodes() -> Result<()> {
    let html = r#"
        <p id='paragraph' class='green' contenteditable>Sample Paragraph</p>
        <button id='run'>run</button>
        <pre id='result'></pre>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const paragraph = document.getElementById('paragraph');
            let output = '';
            for (const attr of paragraph.attributes) {
              output += `${attr.name}->${attr.value}|`;
            }
            document.getElementById('result').textContent = output.slice(0, -1);
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "class->green|contenteditable->true|id->paragraph",
    )?;
    Ok(())
}

#[test]
fn element_attributes_own_keys_and_descriptors_track_live_entries_work() -> Result<()> {
    let html = r#"
        <p id='paragraph' class='green' contenteditable>Sample Paragraph</p>
        <button id='run'>run</button>
        <pre id='result'></pre>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const paragraph = document.getElementById('paragraph');
            const attrs = paragraph.attributes;

            const firstDesc = Object.getOwnPropertyDescriptor(attrs, '0');
            const classDesc = Object.getOwnPropertyDescriptor(attrs, 'class');
            const before = [
              String(Object.keys(attrs).includes('0')),
              String(Object.keys(attrs).includes('class')),
              String(Object.getOwnPropertyNames(attrs).includes('length')),
              String(Reflect.ownKeys(attrs).includes('class')),
              String(Object.hasOwn(attrs, 'class')),
              firstDesc.value.name,
              classDesc.value.value
            ].join(':');

            Object.defineProperty(attrs, 'class', {
              value: 'shadow',
              enumerable: true,
              configurable: true
            });

            const shadow = [
              String(attrs.class === 'shadow'),
              String(Object.getOwnPropertyDescriptor(attrs, 'class').value === 'shadow'),
              String(Object.keys(attrs).includes('class'))
            ].join(':');

            delete attrs.class;
            paragraph.setAttribute('data-state', 'ready');

            const dataDesc = Object.getOwnPropertyDescriptor(attrs, 'data-state');
            const after = [
              attrs.class.value,
              attrs['data-state'].value,
              String(Object.keys(attrs).includes('data-state')),
              String(Object.hasOwn(attrs, 'data-state')),
              dataDesc.value.value,
              Object.getOwnPropertyDescriptor(attrs, '0').value.name
            ].join(':');

            document.getElementById('result').textContent = [before, shadow, after].join('|');
          });
        </script>
        "#;

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

#[test]
fn element_attributes_expando_assignment_and_explicit_prototype_mutation_work() -> Result<()> {
    let html = r#"
        <p id='paragraph' class='green' contenteditable>Sample Paragraph</p>
        <pre id='result'></pre>
        <script>
          const paragraph = document.getElementById('paragraph');
          const attrs = paragraph.attributes;
          attrs.marker = 'own';

          const proto = {
            7: 'proto-index',
            protoOnly: 'proto',
            pick() {
              return this[7];
            }
          };

          const sameAttrs = paragraph.attributes;
          Object.setPrototypeOf(attrs, proto);
          const child = Object.create(attrs);

          document.getElementById('result').textContent = [
            String(sameAttrs === attrs),
            sameAttrs.marker,
            String(Object.getPrototypeOf(attrs) === proto),
            attrs[0].name,
            attrs[7],
            attrs.protoOnly,
            String(attrs.pick() === 'proto-index'),
            child[0].name,
            child[7],
            child.marker,
            String('7' in attrs),
            String('marker' in child),
            String(typeof attrs.item === 'undefined')
          ].join('|');
        </script>
        "#;

    let h = Harness::from_html(html)?;
    h.assert_text(
        "#result",
        "true|own|true|class|proto-index|proto|true|class|proto-index|own|true|true|true",
    )?;
    Ok(())
}