browser_tester 1.5.0

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

#[test]
fn html_area_element_global_and_instanceof_work() -> Result<()> {
    let html = r#"
        <map name='zones'>
          <area id='hot' href='/go' alt='hot'>
        </map>
        <a id='link' href='/other'>other</a>
        <p id='result'></p>
        <script>
          const hot = document.getElementById('hot');
          const link = document.getElementById('link');
          document.getElementById('result').textContent = [
            typeof HTMLAreaElement,
            window.HTMLAreaElement === HTMLAreaElement,
            hot instanceof HTMLAreaElement,
            hot instanceof HTMLElement,
            link instanceof HTMLAreaElement
          ].join(':');
        </script>
        "#;

    let h = Harness::from_html(html)?;
    h.assert_text("#result", "function:true:true:true:false")?;
    Ok(())
}

#[test]
fn area_properties_and_document_links_include_href_areas() -> Result<()> {
    let html = r#"
        <map name='primary'>
          <area id='left' shape='circle' coords='75,75,75' href='/left' alt='Click left'>
          <area id='noop' shape='default' alt='No link'>
        </map>
        <a id='other' href='/other'>other</a>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const left = document.getElementById('left');
            left.referrerPolicy = 'origin';
            left.download = 'left.txt';
            left.ping = 'https://ping.example';

            document.getElementById('result').textContent =
              left.href + '|' +
              left.shape + '|' +
              left.coords + '|' +
              left.getAttribute('alt') + '|' +
              left.referrerPolicy + '|' +
              left.download + '|' +
              left.ping + '|' +
              document.links.length + '|' +
              document.links[0].id + ':' + document.links[1].id;
          });
        </script>
        "#;

    let mut h = Harness::from_html_with_url("https://example.com/base/index.html", html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "https://example.com/left|circle|75,75,75|Click left|origin|left.txt|https://ping.example|2|left:other",
    )?;
    Ok(())
}

#[test]
fn area_click_follows_href_and_skips_blank_target_or_missing_href() -> Result<()> {
    let html = r#"
        <map name='routes'>
          <area id='go' href='/go' alt='go'>
          <area id='mail' href='mailto:m.bluth@example.com' alt='mail'>
          <area id='inactive' href='/inactive' nohref alt='inactive'>
          <area id='blank' href='/blank' target='_blank' alt='blank'>
          <area id='nohref' alt='no href'>
        </map>
        "#;

    let mut h = Harness::from_html_with_url("https://app.local/start", html)?;
    h.click("#go")?;
    h.click("#mail")?;
    h.click("#inactive")?;
    h.click("#blank")?;
    h.click("#nohref")?;

    assert_eq!(
        h.take_location_navigations(),
        vec![
            LocationNavigation {
                kind: LocationNavigationKind::Assign,
                from: "https://app.local/start".to_string(),
                to: "https://app.local/go".to_string(),
            },
            LocationNavigation {
                kind: LocationNavigationKind::Assign,
                from: "https://app.local/go".to_string(),
                to: "mailto:m.bluth@example.com".to_string(),
            },
        ]
    );
    Ok(())
}

#[test]
fn area_invalid_and_special_host_click_matrix_work() -> Result<()> {
    let html = r#"
        <map name='routes'>
          <area id='bad' href='http://' alt='bad'>
          <area id='bad-query' href='http:?x' alt='bad query'>
          <area id='blank' href='https://example.com:abc/report' target='_blank' alt='blank'>
          <area id='download' href='https://example.com:abc/report' download='report.txt' alt='download'>
          <area id='inactive' href='https://example.com:abc/report' nohref alt='inactive'>
          <area id='hostless' href='http:Example.COM:080/docs' alt='hostless'>
        </map>
        "#;

    let mut h = Harness::from_html_with_url("https://app.local/start", html)?;
    h.click("#bad")?;
    h.click("#bad-query")?;
    h.click("#blank")?;
    h.click("#download")?;
    h.click("#inactive")?;
    h.click("#hostless")?;

    assert_eq!(
        h.take_location_navigations(),
        vec![LocationNavigation {
            kind: LocationNavigationKind::Assign,
            from: "https://app.local/start".to_string(),
            to: "http://example.com/docs".to_string(),
        }]
    );
    Ok(())
}

#[test]
fn area_alt_nohref_interest_and_rel_list_properties_reflect_work() -> Result<()> {
    let html = r#"
        <map name='meta'>
          <area id='spot' href='/x' alt='old alt' rel='noopener noreferrer'>
        </map>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const spot = document.getElementById('spot');
            const before = [
              spot.alt,
              spot.noHref,
              spot.relList.length
            ].join(':');
            spot.alt = 'new alt';
            spot.interestForElement = 'panel';
            spot.noHref = true;
            const withNoHref = [
              spot.noHref,
              spot.getAttribute('nohref') !== null
            ].join(':');
            spot.noHref = false;
            document.getElementById('result').textContent = [
              before,
              spot.alt,
              spot.getAttribute('alt'),
              spot.interestForElement,
              withNoHref,
              spot.noHref,
              spot.getAttribute('nohref') === null,
              spot.relList.length
            ].join('|');
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "old alt:false:2|new alt|new alt|panel|true:true|false|true|2",
    )?;
    Ok(())
}

#[test]
fn area_download_blob_click_captures_download_without_navigation() -> Result<()> {
    let html = r#"
        <map name='save'>
          <area id='save-area' alt='save'>
        </map>
        <button id='prep'>prep</button>
        <script>
          document.getElementById('prep').addEventListener('click', () => {
            const blob = new Blob(['abc'], { type: 'text/plain' });
            const url = URL.createObjectURL(blob);
            const area = document.getElementById('save-area');
            area.href = url;
            area.download = 'map.txt';
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#prep")?;
    h.click("#save-area")?;

    assert!(h.take_location_navigations().is_empty());
    assert_eq!(
        h.take_downloads(),
        vec![DownloadArtifact {
            filename: Some("map.txt".to_string()),
            mime_type: Some("text/plain".to_string()),
            bytes: b"abc".to_vec(),
        }]
    );
    Ok(())
}

#[test]
fn area_download_default_action_uses_post_click_attribute_state_and_keeps_network_downloads_uncaptured()
-> Result<()> {
    let html = r#"
        <map name='save'>
          <area id='save-area' href='/initial' download='initial.txt' alt='save'>
        </map>
        <script>
          let step = 0;
          const live = URL.createObjectURL(new Blob(['area'], { type: 'text/plain' }));
          const area = document.getElementById('save-area');
          area.addEventListener('click', () => {
            if (step === 0) {
              area.href = live;
              area.download = 'area.txt';
            } else if (step === 1) {
              area.href = '/report.csv';
              area.download = 'network.txt';
            } else {
              area.href = '/done';
              area.removeAttribute('download');
            }
            step += 1;
          });
        </script>
        "#;

    let mut h = Harness::from_html_with_url("https://app.local/start", html)?;
    h.click("#save-area")?;
    h.click("#save-area")?;
    h.click("#save-area")?;

    assert_eq!(
        h.take_downloads(),
        vec![DownloadArtifact {
            filename: Some("area.txt".to_string()),
            mime_type: Some("text/plain".to_string()),
            bytes: b"area".to_vec(),
        }]
    );
    assert_eq!(
        h.take_location_navigations(),
        vec![LocationNavigation {
            kind: LocationNavigationKind::Assign,
            from: "https://app.local/start".to_string(),
            to: "https://app.local/done".to_string(),
        }]
    );
    Ok(())
}

#[test]
fn area_role_is_link_with_href_and_empty_without_href() -> Result<()> {
    let html = r#"
        <map name='roles'>
          <area id='hot' href='/x' alt='x'>
          <area id='plain' alt='plain'>
        </map>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const hot = document.getElementById('hot');
            const plain = document.getElementById('plain');
            const initial = hot.role + ':' + plain.role;
            plain.role = 'note';
            const assigned = plain.role + ':' + plain.getAttribute('role');
            plain.removeAttribute('role');
            const restored = plain.role + ':' + (plain.getAttribute('role') === null);
            document.getElementById('result').textContent =
              initial + '|' + assigned + '|' + restored;
          });
        </script>
        "#;

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

#[test]
fn area_url_and_hyperlink_properties_reflect_and_to_string_work() -> Result<()> {
    let html = r#"
        <map name='primary'>
          <area id='left' href='/docs/p?x=1#h' alt='left'>
        </map>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            location.href = 'https://example.com/base/index.html';

            document.getElementById('left').download = 'left.txt';
            document.getElementById('left').referrerPolicy = 'strict-origin';
            document.getElementById('left').rel = 'noopener';
            document.getElementById('left').target = '_blank';
            document.getElementById('left').type = 'text/plain';
            document.getElementById('left').ping = 'https://ping.example';
            document.getElementById('left').shape = 'rect';
            document.getElementById('left').coords = '0,0,10,10';

            document.getElementById('result').textContent =
              document.getElementById('left').href + '|' +
              document.getElementById('left').protocol + '|' +
              document.getElementById('left').host + '|' +
              document.getElementById('left').pathname + '|' +
              document.getElementById('left').search + '|' +
              document.getElementById('left').hash + '|' +
              document.getElementById('left').origin + '|' +
              document.getElementById('left').download + '|' +
              document.getElementById('left').referrerPolicy + '|' +
              document.getElementById('left').rel + '|' +
              document.getElementById('left').target + '|' +
              document.getElementById('left').type + '|' +
              document.getElementById('left').ping + '|' +
              document.getElementById('left').shape + '|' +
              document.getElementById('left').coords + '|' +
              document.getElementById('left').toString();
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "https://example.com/docs/p?x=1#h|https:|example.com|/docs/p|?x=1|#h|https://example.com|left.txt|strict-origin|noopener|_blank|text/plain|https://ping.example|rect|0,0,10,10|https://example.com/docs/p?x=1#h",
    )?;
    Ok(())
}