browser_tester 1.5.0

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

#[test]
fn img_implicit_roles_and_core_properties_reflect_correctly() -> Result<()> {
    let html = r#"
        <img
          id='photo'
          src='/shared-assets/images/examples/grapefruit-slice.jpg'
          alt='Grapefruit slice atop a pile of other slices'>
        <img id='decor' src='/shared-assets/images/examples/favicon72.png' alt=''>
        <img id='missing' src='/shared-assets/images/examples/favicon144.png'>
        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const photo = document.getElementById('photo');
            const decor = document.getElementById('decor');
            const missing = document.getElementById('missing');

            const initial =
              photo.role + ':' +
              decor.role + ':' +
              missing.role + ':' +
              photo.src + ':' +
              photo.width + 'x' + photo.height + ':' +
              photo.getAttribute('alt');

            photo.width = 144;
            photo.height = 96;
            photo.crossOrigin = 'anonymous';
            photo.referrerPolicy = 'no-referrer';
            photo.setAttribute('srcset', '/img-1x.png 1x, /img-2x.png 2x');
            photo.setAttribute('sizes', '(max-width: 600px) 200px, 50vw');
            photo.setAttribute('loading', 'lazy');

            const assigned =
              photo.width + 'x' + photo.height + ':' +
              photo.getAttribute('width') + ':' +
              photo.getAttribute('height') + ':' +
              photo.crossOrigin + ':' +
              photo.getAttribute('crossorigin') + ':' +
              photo.referrerPolicy + ':' +
              photo.getAttribute('referrerpolicy') + ':' +
              photo.getAttribute('srcset').includes('2x') + ':' +
              photo.getAttribute('sizes').includes('50vw') + ':' +
              photo.getAttribute('loading');

            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",
        "img:presentation:img:https://app.local/shared-assets/images/examples/grapefruit-slice.jpg:0x0:Grapefruit slice atop a pile of other slices|144x96:144:96:anonymous:anonymous:no-referrer:no-referrer:true:true:lazy",
    )?;
    Ok(())
}

#[test]
fn img_alt_dependent_role_and_role_attribute_roundtrip_work() -> Result<()> {
    let html = r#"
        <img id='target' src='/shared-assets/images/examples/favicon72.png' alt=''>
        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const target = document.getElementById('target');

            const start = target.role + ':' + (target.getAttribute('alt') === '');

            target.setAttribute('alt', 'Company logo');
            const descriptive = target.role + ':' + target.getAttribute('alt');

            target.setAttribute('alt', '');
            const decorative = target.role + ':' + (target.getAttribute('alt') === '');

            target.role = 'button';
            const assigned = target.role + ':' + target.getAttribute('role');
            target.removeAttribute('role');
            const restored = target.role + ':' + (target.getAttribute('role') === null);

            target.removeAttribute('alt');
            const withoutAlt = target.role + ':' + (target.getAttribute('alt') === null);

            document.getElementById('result').textContent =
              start + '|' +
              descriptive + '|' +
              decorative + '|' +
              assigned + '|' +
              restored + '|' +
              withoutAlt;
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "presentation:true|img:Company logo|presentation:true|button:button|presentation:true|img:true",
    )?;
    Ok(())
}

#[test]
fn img_reflective_own_property_surface_and_object_copy_work() -> Result<()> {
    let html = r#"
        <img
          id='photo'
          src='/shared-assets/images/examples/grapefruit-slice.jpg'
          srcset='/img-1x.png 1x, /img-2x.png 2x'
          sizes='100vw'>
        <p id='result'></p>
        <script>
          const photo = document.getElementById('photo');
          const beforeAssigned = Object.assign({}, photo);
          const beforeSpread = { ...photo };

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

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

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

          const shadowed = [
            photo.src,
            photo.srcset,
            photo.sizes,
            String(Object.keys(photo).sort().join(',') === 'extra,sizes,src,srcset'),
            shadowAssigned.src,
            shadowAssigned.srcset,
            shadowAssigned.sizes,
            shadowAssigned.extra,
            shadowSpread.src,
            shadowSpread.srcset,
            shadowSpread.sizes,
            shadowSpread.extra
          ].join(':');

          delete photo.src;
          delete photo.srcset;
          delete photo.sizes;

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

          const restored = [
            photo.src,
            photo.srcset,
            photo.sizes,
            String(Object.hasOwn(photo, 'src')),
            String(Object.hasOwn(photo, 'srcset')),
            String(Object.hasOwn(photo, 'sizes')),
            restoredAssigned.extra,
            String('src' in restoredAssigned),
            String('srcset' in restoredAssigned),
            String('sizes' in restoredAssigned),
            restoredSpread.extra,
            String('src' in restoredSpread),
            String('srcset' in restoredSpread),
            String('sizes' 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/shared-assets/images/examples/grapefruit-slice.jpg:/img-1x.png 1x, /img-2x.png 2x:100vw:false:false:false:true:false:false:false:false|shadow-src:shadow-srcset:shadow-sizes:true:shadow-src:shadow-srcset:shadow-sizes:expando:shadow-src:shadow-srcset:shadow-sizes:expando|https://app.local/shared-assets/images/examples/grapefruit-slice.jpg:/img-1x.png 1x, /img-2x.png 2x:100vw:false:false:false:expando:false:false:false:expando:false:false:false",
    )?;
    Ok(())
}

#[test]
fn img_cross_origin_and_current_src_shadow_define_property_delete_and_fast_path_parity_work()
-> Result<()> {
    let html = r#"
        <picture>
          <source srcset='/img/hero.webp 1x' type='image/webp'>
          <img id='photo' src='/img/fallback.jpg' crossorigin='anonymous' alt='Artwork'>
        </picture>
        <p id='result'></p>
        <script>
          const photo = document.getElementById('photo');

          const before = [
            photo.crossOrigin,
            photo.currentSrc,
            photo.getAttribute('crossorigin')
          ].join(':');

          Object.defineProperty(photo, 'crossOrigin', {
            value: 'shadow-cors',
            writable: true,
            enumerable: true,
            configurable: true
          });
          Object.defineProperty(photo, 'currentSrc', {
            value: 'shadow-current',
            writable: true,
            enumerable: true,
            configurable: true
          });

          photo.crossOrigin = 'set-cors';
          photo.currentSrc = 'set-current';

          const shadowed = [
            photo.crossOrigin,
            photo.currentSrc,
            photo.getAttribute('crossorigin'),
            String(Object.keys(photo).sort().join(',') === 'crossOrigin,currentSrc')
          ].join(':');

          delete photo.crossOrigin;
          delete photo.currentSrc;

          const restored = [
            photo.crossOrigin,
            photo.currentSrc,
            photo.getAttribute('crossorigin'),
            String(Object.hasOwn(photo, 'crossOrigin')),
            String(Object.hasOwn(photo, 'currentSrc'))
          ].join(':');

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

    let h = Harness::from_html_with_url("https://app.local/gallery/index.html", html)?;
    h.assert_text(
        "#result",
        "anonymous:https://app.local/img/hero.webp:anonymous|set-cors:set-current:anonymous:true|anonymous:https://app.local/img/hero.webp:anonymous:false:false",
    )?;
    Ok(())
}

#[test]
fn img_load_facing_state_and_picture_current_src_matrix_work() -> Result<()> {
    let html = r#"
        <picture>
          <source id='hero' srcset='/img/hero.webp 1x' media='(width >= 900px)' type='image/webp'>
          <img id='photo' src='/img/fallback.jpg' alt='Artwork'>
        </picture>
        <p id='result'></p>
        <script>
          const hero = document.getElementById('hero');
          const photo = document.getElementById('photo');

          const snapshot = () => [
            String(photo.complete),
            String(photo.naturalWidth),
            String(photo.naturalHeight),
            photo.currentSrc
          ].join(':');

          window.innerWidth = 500;
          const initial = snapshot();

          window.innerWidth = 1200;
          const matched = snapshot();

          hero.type = 'video/mp4';
          const unsupported = snapshot();

          hero.type = 'image/webp';
          hero.media = '(width >= 1500px)';
          const filtered = snapshot();

          photo.removeAttribute('src');
          hero.removeAttribute('srcset');
          const empty = snapshot();

          document.getElementById('result').textContent = [
            initial,
            matched,
            unsupported,
            filtered,
            empty
          ].join('|');
        </script>
        "#;

    let h = Harness::from_html_with_url("https://app.local/gallery/index.html", html)?;
    h.assert_text(
        "#result",
        "true:1:1:https://app.local/img/fallback.jpg|true:1:1:https://app.local/img/hero.webp|true:1:1:https://app.local/img/fallback.jpg|true:1:1:https://app.local/img/fallback.jpg|true:0:0:",
    )?;
    Ok(())
}