browser_tester 1.5.0

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

#[test]
fn navigate_event_constructor_populates_fields_and_methods() -> Result<()> {
    let html = r#"
        <a id='source' href='/next'>go</a>
        <p id='result'></p>
        <script>
          const source = document.getElementById('source');
          const destination = { url: 'https://app.local/articles/1' };
          const formData = { kind: 'f' };
          const info = { from: 'back' };
          const signal = { aborted: false };
          const ev = new NavigateEvent('navigate', {
            bubbles: true,
            cancelable: true,
            canIntercept: true,
            destination,
            downloadRequest: 'book.pdf',
            formData,
            hashChange: true,
            hasUAVisualTransition: true,
            info,
            navigationType: 'replace',
            signal,
            sourceElement: source,
            userInitiated: true
          });
          document.getElementById('result').textContent = [
            ev.type,
            ev.bubbles,
            ev.cancelable,
            ev.canIntercept,
            ev.destination === destination,
            ev.downloadRequest,
            ev.formData === formData,
            ev.hashChange,
            ev.hasUAVisualTransition,
            ev.info === info,
            ev.navigationType,
            ev.signal === signal,
            ev.sourceElement === source,
            ev.userInitiated,
            typeof ev.intercept,
            typeof ev.scroll
          ].join(':');
        </script>
        "#;

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

#[test]
fn navigate_event_constructor_uses_expected_defaults() -> Result<()> {
    let html = r#"
        <p id='result'></p>
        <script>
          const ev = new NavigateEvent('navigate');
          document.getElementById('result').textContent = [
            ev.canIntercept,
            ev.destination === null,
            ev.downloadRequest === null,
            ev.formData === null,
            ev.hashChange,
            ev.hasUAVisualTransition,
            ev.info === undefined,
            ev.navigationType,
            typeof ev.signal,
            ev.signal && ev.signal.aborted === false,
            ev.sourceElement === null,
            ev.userInitiated
          ].join(':');
        </script>
        "#;

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

#[test]
fn navigate_event_intercept_runs_handler_and_throws_when_not_interceptable() -> Result<()> {
    let html = r#"
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            const ev = new NavigateEvent('navigate', { canIntercept: true });
            let handled = 0;
            ev.intercept({
              handler() {
                handled += 1;
              }
            });
            ev.scroll();

            let threw = false;
            try {
              new NavigateEvent('navigate').intercept();
            } catch (err) {
              threw = String(err).includes('InvalidStateError');
            }
            document.getElementById('result').textContent = [handled, threw].join(':');
          });
        </script>
        "#;

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

#[test]
fn dispatch_event_with_navigate_event_payload_preserves_navigate_fields() -> Result<()> {
    let html = r#"
        <a id='source' href='/next'>go</a>
        <div id='target'></div>
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          const target = document.getElementById('target');
          target.addEventListener('navigate', (event) => {
            let handled = 0;
            event.intercept({
              handler() {
                handled += 1;
              }
            });
            event.scroll();
            document.getElementById('result').textContent = [
              event.canIntercept,
              event.destination.url,
              event.navigationType,
              event.userInitiated,
              event.sourceElement ? event.sourceElement.id : 'none',
              typeof event.signal,
              event.signal && event.signal.aborted === false,
              handled
            ].join(':');
          });

          document.getElementById('run').addEventListener('click', () => {
            const source = document.getElementById('source');
            const ev = new NavigateEvent('navigate', {
              canIntercept: true,
              destination: { url: 'https://app.local/next' },
              navigationType: 'push',
              userInitiated: true,
              sourceElement: source
            });
            target.dispatchEvent(ev);
          });
        </script>
        "#;

    let mut h = Harness::from_html(html)?;
    h.click("#run")?;
    h.assert_text(
        "#result",
        "true:https://app.local/next:push:true:source:object:true:1",
    )?;
    Ok(())
}

#[test]
fn navigate_event_constructor_rejects_non_object_options() -> Result<()> {
    let html = r#"
        <button id='run'>run</button>
        <p id='result'></p>
        <script>
          document.getElementById('run').addEventListener('click', () => {
            let threw = false;
            try {
              new NavigateEvent('navigate', 123);
            } catch (error) {
              threw = String(error).includes('options argument must be an object');
            }
            document.getElementById('result').textContent = String(threw);
          });
        </script>
        "#;

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

#[test]
fn navigate_event_raw_getter_and_inherited_property_paths_work() -> Result<()> {
    let html = r#"
        <p id='result'></p>
        <script>
          const ev = new NavigateEvent('navigate', { canIntercept: true });
          const intercept = Object.create(ev).intercept;
          const scroll = ev['scroll'];
          let handled = 0;

          intercept.call(ev, {
            handler() {
              handled += 1;
            }
          });
          scroll.call(ev);

          let incompatible = false;
          try {
            scroll.call({});
          } catch (error) {
            incompatible = String(error).includes('NavigateEvent');
          }

          document.getElementById('result').textContent = [
            intercept.name,
            intercept.length,
            scroll.name,
            scroll.length,
            handled,
            incompatible
          ].join(':');
        </script>
        "#;

    let h = Harness::from_html(html)?;
    h.assert_text("#result", "intercept:1:scroll:0:1:true")?;
    Ok(())
}