browser_tester 1.5.0

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

#[test]
fn object_data_type_size_name_and_fallback_content_work() -> Result<()> {
    let html = r#"
        <object
          id='asset'
          type='video/mp4'
          data='/shared-assets/videos/flower.mp4'
          width='250'
          height='200'
          name='promo'>
          <img
            id='fallback'
            src='/shared-assets/images/examples/flowers.jpg'
            alt='Some beautiful flowers'>
        </object>

        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const asset = document.getElementById('asset');
            const fallback = document.getElementById('fallback');

            const initial =
              asset.role + ':' +
              asset.tagName + ':' +
              asset.getAttribute('data') + ':' +
              asset.type + ':' +
              asset.width + ':' +
              asset.height + ':' +
              asset.name + ':' +
              fallback.tagName + ':' +
              fallback.getAttribute('alt');

            asset.setAttribute('data', '/media/movie.webm');
            asset.type = 'video/webm';
            asset.width = 600;
            asset.height = 140;
            asset.name = 'promo-player';
            asset.setAttribute('usemap', '#infographic');

            const assigned =
              asset.getAttribute('data') + ':' +
              asset.type + ':' +
              asset.width + ':' +
              asset.height + ':' +
              asset.getAttribute('width') + ':' +
              asset.getAttribute('height') + ':' +
              asset.name + ':' +
              asset.getAttribute('name') + ':' +
              asset.getAttribute('usemap');

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

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        ":OBJECT:/shared-assets/videos/flower.mp4:video/mp4:250:200:promo:IMG:Some beautiful flowers|/media/movie.webm:video/webm:600:140:600:140:promo-player:promo-player:#infographic",
    )?;
    Ok(())
}

#[test]
fn object_role_override_form_attr_and_transparent_content_work() -> Result<()> {
    let html = r#"
        <form id='upload-form' action='#'></form>
        <object id='control' form='upload-form' data='/bin/resource.bin' type='application/octet-stream'>
          <p id='inside'>Fallback <a id='alt-link' href='/fallback'>link</a></p>
        </object>

        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const control = document.getElementById('control');
            const inside = document.getElementById('inside');

            const initial =
              control.role + ':' +
              control.getAttribute('form') + ':' +
              inside.textContent.replace(/\s+/g, ' ').trim() + ':' +
              document.getElementById('alt-link').href + ':' +
              control.querySelectorAll('param').length;

            control.role = 'img';
            const assigned = control.role + ':' + control.getAttribute('role');
            control.removeAttribute('role');
            const restored = control.role + ':' + (control.getAttribute('role') === null);

            control.setAttribute('archive', '/a.jar /b.jar');
            control.setAttribute('standby', 'Loading...');
            const deprecated =
              control.getAttribute('archive').includes('/a.jar') + ':' +
              control.getAttribute('standby');

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

    let mut h = Harness::from_html_with_url("https://app.local/index.html", html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        ":upload-form:Fallback link:https://app.local/fallback:0|img:img|:true|true:Loading...",
    )?;
    Ok(())
}

#[test]
fn object_data_and_usemap_shadow_define_property_delete_and_fast_path_parity_work() -> Result<()> {
    let html = r#"
        <object id='asset' data='/media/movie.webm' usemap='#diagram'></object>
        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const asset = document.getElementById('asset');
            const shadow = {
              data: 'shadow-data',
              useMap: 'shadow-usemap'
            };

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

            document.getElementById('asset').data = '/next.bin';
            asset.useMap = '#next';

            const first = [
              document.getElementById('asset').data,
              asset['data'],
              shadow.data,
              asset.getAttribute('data'),
              asset.useMap,
              asset['useMap'],
              shadow.useMap,
              asset.getAttribute('usemap')
            ].join(':');

            Reflect.set(asset, 'data', 'reflect.bin');
            Reflect.set(asset, 'useMap', '#reflect');

            const second = [
              document.getElementById('asset').data,
              asset['data'],
              shadow.data,
              asset.getAttribute('data'),
              asset.useMap,
              asset['useMap'],
              shadow.useMap,
              asset.getAttribute('usemap')
            ].join(':');

            delete asset.data;
            delete asset.useMap;

            const third = [
              document.getElementById('asset').data,
              asset['data'],
              asset.getAttribute('data'),
              asset.useMap,
              asset['useMap'],
              asset.getAttribute('usemap')
            ].join(':');

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

    let mut h = Harness::from_html_with_url("https://app.local/base/index.html", html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "set:/next.bin:set:/next.bin:set:/next.bin:/media/movie.webm:set:#next:set:#next:set:#next:#diagram|set:reflect.bin:set:reflect.bin:set:reflect.bin:/media/movie.webm:set:#reflect:set:#reflect:set:#reflect:#diagram|https://app.local/media/movie.webm:https://app.local/media/movie.webm:/media/movie.webm:#diagram:#diagram:#diagram",
    )?;
    Ok(())
}

#[test]
fn object_reflective_own_property_surface_and_object_copy_work() -> Result<()> {
    let html = r#"
        <object id='asset' data='/media/movie.webm' usemap='#diagram'></object>
        <p id='result'></p>
        <script>
          const asset = document.getElementById('asset');
          const beforeAssigned = Object.assign({}, asset);
          const beforeSpread = { ...asset };

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

          Object.defineProperty(asset, 'data', {
            value: 'shadow-data',
            writable: true,
            enumerable: true,
            configurable: true
          });
          Object.defineProperty(asset, 'useMap', {
            value: 'shadow-usemap',
            writable: true,
            enumerable: true,
            configurable: true
          });
          asset.extra = 'expando';

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

          const shadowed = [
            asset.data,
            asset.useMap,
            String(Object.keys(asset).join(',') === 'data,extra,useMap'),
            shadowAssigned.data,
            shadowAssigned.useMap,
            shadowAssigned.extra,
            shadowSpread.data,
            shadowSpread.useMap,
            shadowSpread.extra
          ].join(':');

          delete asset.data;
          delete asset.useMap;

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

          const restored = [
            asset.data,
            asset.useMap,
            String(Object.hasOwn(asset, 'data')),
            String(Object.hasOwn(asset, 'useMap')),
            restoredAssigned.extra,
            String('data' in restoredAssigned),
            String('useMap' in restoredAssigned),
            restoredSpread.extra,
            String('data' in restoredSpread),
            String('useMap' in restoredSpread)
          ].join(':');

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

    let h = Harness::from_html_with_url("https://app.local/base/index.html", html)?;
    h.assert_text(
        "#result",
        "https://app.local/media/movie.webm:#diagram:false:false:true:true:false:false:false:false|shadow-data:shadow-usemap:true:shadow-data:shadow-usemap:expando:shadow-data:shadow-usemap:expando|https://app.local/media/movie.webm:#diagram:false:false:expando:false:false:expando:false:false",
    )?;
    Ok(())
}

#[test]
fn object_manual_load_error_dispatch_and_resource_surface_work() -> Result<()> {
    let html = r#"
        <object id='asset' data='/media/movie.webm' usemap='#diagram'></object>
        <p id='result'></p>
        <script>
          const asset = document.getElementById('asset');
          const log = [];
          const render = () => {
            document.getElementById('result').textContent = [
              log.join(','),
              asset.data,
              asset.useMap
            ].join('|');
          };

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

    let mut h = Harness::from_html_with_url("https://app.local/base/index.html", html)?;
    h.dispatch("#asset", "load")?;
    h.dispatch("#asset", "error")?;
    h.assert_text(
        "#result",
        "load:load:true,error:error:true|https://app.local/media/movie.webm|#diagram",
    )?;
    Ok(())
}