browser_tester 1.5.0

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

#[test]
fn link_href_rel_and_metadata_properties_roundtrip_work() -> Result<()> {
    let html = r#"
        <head>
          <link
            id='sheet'
            href='/styles/main.css'
            rel='stylesheet preload'
            type='text/css'
            hreflang='en'
            crossorigin='use-credentials'
            referrerpolicy='origin'>
          <link id='nohref' rel='stylesheet'>
        </head>
        <body>
          <a id='anchor' href='/page'>page</a>
          <button id='run' type='button'>run</button>
          <p id='result'></p>
          <script>
            document.getElementById('run').addEventListener('click', () => {
              const sheet = document.getElementById('sheet');
              const nohref = document.getElementById('nohref');

              const initialRoles = sheet.role + ':' + nohref.role;
              const initial =
                sheet.href + ':' +
                sheet.rel + ':' +
                sheet.relList.length + ':' +
                sheet.type + ':' +
                sheet.hreflang + ':' +
                sheet.crossOrigin + ':' +
                sheet.referrerPolicy + ':' +
                sheet.disabled + ':' +
                document.links.length + ':' +
                document.links[0].id;

              sheet.href = '/styles/next.css';
              sheet.rel = 'alternate stylesheet';
              sheet.type = 'text/plain';
              sheet.hreflang = 'ja';
              sheet.crossOrigin = 'anonymous';
              sheet.referrerPolicy = 'no-referrer';
              sheet.disabled = true;

              const assigned =
                sheet.href + ':' +
                sheet.getAttribute('href') + ':' +
                sheet.rel + ':' +
                sheet.relList.length + ':' +
                sheet.type + ':' +
                sheet.hreflang + ':' +
                sheet.crossOrigin + ':' +
                sheet.referrerPolicy + ':' +
                sheet.disabled + ':' +
                sheet.getAttribute('disabled');

              sheet.disabled = false;
              const disabledReset =
                sheet.disabled + ':' + (sheet.getAttribute('disabled') === null);

              nohref.href = '/styles/missing.css';
              const roleAfterHref = nohref.role;

              sheet.role = 'none';
              const roleAssigned = sheet.role + ':' + sheet.getAttribute('role');
              sheet.removeAttribute('role');
              const roleRestored = sheet.role + ':' + (sheet.getAttribute('role') === null);

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

    let mut h = Harness::from_html_with_url("https://app.local/docs/index.html", html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "link:|https://app.local/styles/main.css:stylesheet preload:2:text/css:en:use-credentials:origin:false:1:anchor|https://app.local/styles/next.css:/styles/next.css:alternate stylesheet:2:text/plain:ja:anonymous:no-referrer:true:true|false:true|link|none:none|link:true",
    )?;
    Ok(())
}

#[test]
fn link_is_void_and_implicit_role_depends_on_href_presence() -> Result<()> {
    let html = r#"
        <p id='line'>
          before
          <link id='icon' rel='icon' href='/favicon.ico'>
          after
        </p>
        <button id='run' type='button'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const line = document.getElementById('line');
            const icon = document.getElementById('icon');

            const initial =
              icon.role + ':' +
              icon.tagName + ':' +
              line.childElementCount + ':' +
              line.textContent.replace(/\s+/g, ' ').trim();

            icon.href = '#favicon';
            const nextHref = icon.href;

            icon.removeAttribute('href');
            const roleWithoutHref = icon.role + ':' + (icon.getAttribute('href') === null);

            icon.setAttribute('sizes', '32x32');
            icon.setAttribute('as', 'image');
            const attrs = icon.getAttribute('sizes') + ':' + icon.getAttribute('as');

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

    let mut h = Harness::from_html_with_url("https://app.local/guide/page.html", html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "link:LINK:1:before after|https://app.local/guide/page.html#favicon|:true|32x32:image",
    )?;
    Ok(())
}

#[test]
fn link_null_and_invalid_url_properties_match_anchor_semantics_and_click_is_noop() -> Result<()> {
    let html = r#"
        <head>
          <link id='missing' rel='stylesheet'>
          <link id='bad' rel='stylesheet' href='http://'>
          <link id='hostless' rel='stylesheet' href='https:Example.COM:080/styles/main.css'>
        </head>
        <body>
          <button id='run' type='button'>run</button>
          <p id='result'></p>
          <script>
            let clicks = 0;
            document.getElementById('bad').addEventListener('click', () => {
              clicks += 1;
            });
            document.getElementById('run').addEventListener('click', () => {
              const missing = document.getElementById('missing');
              const bad = document.getElementById('bad');
              const hostless = document.getElementById('hostless');

              const missingState = [
                'href=' + missing.href,
                'protocol=' + missing.protocol,
                'host=' + missing.host,
                'pathname=' + missing.pathname,
                'origin=' + missing.origin
              ].join(',');

              const badState = [
                'href=' + bad.href,
                'protocol=' + bad.protocol,
                'host=' + bad.host,
                'pathname=' + bad.pathname,
                'origin=' + bad.origin
              ].join(',');

              bad.protocol = 'https:';
              bad.host = 'example.com:443';

              document.getElementById('result').textContent = [
                missingState,
                badState,
                'attr=' + bad.getAttribute('href'),
                'hostless=' + hostless.href,
                'clicks=' + clicks
              ].join('|');
            });
          </script>
        </body>
        "#;

    let mut h = Harness::from_html_with_url("https://app.local/base/index.html", html)?;
    h.click("#bad")?;
    h.click("#hostless")?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "href=,protocol=:,host=,pathname=,origin=|href=http://,protocol=:,host=,pathname=,origin=|attr=http://|hostless=https://example.com:80/styles/main.css|clicks=1",
    )?;
    assert!(h.take_location_navigations().is_empty());
    Ok(())
}