browser_tester 1.5.0

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

#[test]
fn iframe_defaults_and_src_name_reflection_work() -> Result<()> {
    let html = r#"
        <iframe id='frame' title='Inline Frame Example' src='/embedded/page.html'></iframe>
        <iframe id='blank'></iframe>
        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const frame = document.getElementById('frame');
            const blank = document.getElementById('blank');
            const initial =
              blank.width + 'x' + blank.height + ':' +
              frame.src + ':' +
              frame.getAttribute('src') + ':' +
              frame.role + ':' +
              frame.getAttribute('title');

            blank.width = 640;
            blank.height = 360;
            frame.src = '/next.html';
            frame.name = 'inlineFrameExample';

            const assigned =
              blank.width + 'x' + blank.height + ':' +
              blank.getAttribute('width') + ':' +
              blank.getAttribute('height') + ':' +
              frame.src + ':' +
              frame.getAttribute('src') + ':' +
              frame.name + ':' +
              frame.getAttribute('name');

            document.getElementById('result').textContent = initial + '|' + assigned;
          });
        </script>
        "#;

    let mut h = Harness::from_html_with_url("https://app.local/index.html", html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "300x150:https://app.local/embedded/page.html:/embedded/page.html::Inline Frame Example|640x360:640:360:https://app.local/next.html:/next.html:inlineFrameExample:inlineFrameExample",
    )?;
    Ok(())
}

#[test]
fn iframe_referrerpolicy_sandbox_and_role_roundtrip_work() -> Result<()> {
    let html = r#"
        <iframe id='frame' sandbox='allow-forms' srcdoc='<p>Hello</p>'></iframe>
        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const frame = document.getElementById('frame');
            const initial =
              frame.role + ':' +
              frame.referrerPolicy + ':' +
              frame.getAttribute('sandbox') + ':' +
              frame.getAttribute('srcdoc').includes('<p>');

            frame.referrerPolicy = 'no-referrer';
            frame.setAttribute('sandbox', 'allow-scripts allow-same-origin');
            frame.setAttribute('loading', 'lazy');
            const assigned =
              frame.referrerPolicy + ':' +
              frame.getAttribute('referrerpolicy') + ':' +
              frame.getAttribute('sandbox') + ':' +
              frame.getAttribute('loading');

            frame.role = 'document';
            const roleAssigned = frame.role + ':' + frame.getAttribute('role');
            frame.removeAttribute('role');
            const roleRestored = frame.role + ':' + (frame.getAttribute('role') === null);

            document.getElementById('result').textContent =
              initial + '|' + assigned + '|' + roleAssigned + '|' + roleRestored;
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "::allow-forms:true|no-referrer:no-referrer:allow-scripts allow-same-origin:lazy|document:document|:true",
    )?;
    Ok(())
}

#[test]
fn iframe_srcdoc_shadow_define_property_delete_and_fast_path_parity_work() -> Result<()> {
    let html = r#"
        <iframe id='frame' srcdoc='<p>Hello</p>'></iframe>
        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const frame = document.getElementById('frame');
            const shadow = { srcdoc: 'shadow-srcdoc' };

            Object.defineProperty(frame, 'srcdoc', {
              get() { return shadow.srcdoc; },
              set(value) { shadow.srcdoc = 'set:' + value; },
              configurable: true
            });

            document.getElementById('frame').srcdoc = '<p>Next</p>';

            const first = [
              document.getElementById('frame').srcdoc,
              frame['srcdoc'],
              shadow.srcdoc,
              frame.getAttribute('srcdoc')
            ].join(':');

            Reflect.set(frame, 'srcdoc', '<p>Reflect</p>');

            const second = [
              document.getElementById('frame').srcdoc,
              frame['srcdoc'],
              shadow.srcdoc,
              frame.getAttribute('srcdoc')
            ].join(':');

            delete frame.srcdoc;

            const third = [
              document.getElementById('frame').srcdoc,
              frame['srcdoc'],
              frame.getAttribute('srcdoc')
            ].join(':');

            document.getElementById('result').textContent = [
              first,
              second,
              third
            ].join('|');
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "set:<p>Next</p>:set:<p>Next</p>:set:<p>Next</p>:<p>Hello</p>|set:<p>Reflect</p>:set:<p>Reflect</p>:set:<p>Reflect</p>:<p>Hello</p>|<p>Hello</p>:<p>Hello</p>:<p>Hello</p>",
    )?;
    Ok(())
}

#[test]
fn iframe_reflective_own_property_surface_and_object_copy_work() -> Result<()> {
    let html = r#"
        <iframe id='frame' src='/embedded/page.html' srcdoc='<p>Hello</p>'></iframe>
        <p id='result'></p>
        <script>
          const frame = document.getElementById('frame');
          const beforeAssigned = Object.assign({}, frame);
          const beforeSpread = { ...frame };

          const before = [
            frame.src,
            frame.srcdoc,
            String(Object.hasOwn(frame, 'src')),
            String(Object.hasOwn(frame, 'srcdoc')),
            String(Object.getOwnPropertyDescriptor(frame, 'src') === undefined),
            String(Object.getOwnPropertyDescriptor(frame, 'srcdoc') === undefined),
            String(Object.getOwnPropertyNames(frame).includes('src')),
            String(Reflect.ownKeys(frame).includes('srcdoc')),
            String('src' in beforeAssigned),
            String('srcdoc' in beforeSpread)
          ].join(':');

          Object.defineProperty(frame, 'src', {
            value: 'shadow-src',
            writable: true,
            enumerable: true,
            configurable: true
          });
          Object.defineProperty(frame, 'srcdoc', {
            value: 'shadow-srcdoc',
            writable: true,
            enumerable: true,
            configurable: true
          });
          frame.extra = 'expando';

          const shadowAssigned = Object.assign({}, frame);
          const shadowSpread = { ...frame };

          const shadowed = [
            frame.src,
            frame.srcdoc,
            String(Object.keys(frame).join(',') === 'extra,src,srcdoc'),
            shadowAssigned.src,
            shadowAssigned.srcdoc,
            shadowAssigned.extra,
            shadowSpread.src,
            shadowSpread.srcdoc,
            shadowSpread.extra
          ].join(':');

          delete frame.src;
          delete frame.srcdoc;

          const restoredAssigned = Object.assign({}, frame);
          const restoredSpread = { ...frame };

          const restored = [
            frame.src,
            frame.srcdoc,
            String(Object.hasOwn(frame, 'src')),
            String(Object.hasOwn(frame, 'srcdoc')),
            restoredAssigned.extra,
            String('src' in restoredAssigned),
            String('srcdoc' in restoredAssigned),
            restoredSpread.extra,
            String('src' in restoredSpread),
            String('srcdoc' in restoredSpread)
          ].join(':');

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

    let h = Harness::from_html_with_url("https://app.local/index.html", html)?;
    h.assert_text(
        "#result",
        "https://app.local/embedded/page.html:<p>Hello</p>:false:false:true:true:false:false:false:false|shadow-src:shadow-srcdoc:true:shadow-src:shadow-srcdoc:expando:shadow-src:shadow-srcdoc:expando|https://app.local/embedded/page.html:<p>Hello</p>:false:false:expando:false:false:expando:false:false",
    )?;
    Ok(())
}

#[test]
fn iframe_manual_load_error_dispatch_and_resource_surface_work() -> Result<()> {
    let html = r#"
        <iframe id='frame' src='/embedded/page.html' srcdoc='<p>Hello</p>'></iframe>
        <p id='result'></p>
        <script>
          const frame = document.getElementById('frame');
          const log = [];
          const render = () => {
            document.getElementById('result').textContent = [
              log.join(','),
              frame.src,
              frame.srcdoc
            ].join('|');
          };

          frame.onload = (event) => {
            log.push('load:' + event.type + ':' + String(event.currentTarget === frame));
            render();
          };
          frame.addEventListener('error', (event) => {
            log.push('error:' + event.type + ':' + String(event.currentTarget === frame));
            render();
          });
        </script>
        "#;

    let mut h = Harness::from_html_with_url("https://app.local/index.html", html)?;
    h.dispatch("#frame", "load")?;
    h.dispatch("#frame", "error")?;
    h.assert_text(
        "#result",
        "load:load:true,error:error:true|https://app.local/embedded/page.html|<p>Hello</p>",
    )?;
    Ok(())
}