browser_tester 1.5.0

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

#[test]
fn data_transfer_item_string_kind_exposes_kind_type_and_string_methods() -> Result<()> {
    let html = r#"
      <div id='source' draggable='true'></div>
      <p id='out'></p>
      <script>
        document.getElementById('source').addEventListener('dragstart', (event) => {
          const dt = event.dataTransfer;
          dt.setData('text/plain', 'alpha');
          const item = dt.items[0];
          let callbackValue = '';
          const ret = item.getAsString((value) => {
            callbackValue = value;
          });
          item.getAsFileSystemHandle().then((handle) => {
            document.getElementById('out').textContent = [
              dt.items.length,
              item.kind,
              item.type,
              ret === undefined,
              item.getAsFile() === null,
              callbackValue,
              handle === null,
              item.webkitGetAsEntry() === null
            ].join('|');
          });
        });
      </script>
    "#;

    let mut h = Harness::from_html(html)?;
    h.dispatch("#source", "dragstart")?;
    h.assert_text("#out", "1|string|text/plain|true|true|alpha|true|true")?;
    Ok(())
}

#[test]
fn data_transfer_item_order_preserved_when_set_data_replaces_existing_type() -> Result<()> {
    let html = r#"
      <div id='source' draggable='true'></div>
      <p id='out'></p>
      <script>
        document.getElementById('source').addEventListener('dragstart', (event) => {
          const dt = event.dataTransfer;
          dt.setData('text/plain', 'one');
          dt.setData('text/uri-list', 'https://example.com');
          dt.setData('text/plain', 'two');

          const firstItem = dt.items[0];
          const secondItem = dt.items[1];
          let firstValue = '';
          firstItem.getAsString((value) => {
            firstValue = value;
          });
          document.getElementById('out').textContent = [
            dt.items.length,
            firstItem.type,
            firstValue,
            secondItem.type
          ].join('|');
        });
      </script>
    "#;

    let mut h = Harness::from_html(html)?;
    h.dispatch("#source", "dragstart")?;
    h.assert_text("#out", "2|text/plain|two|text/uri-list")?;
    Ok(())
}

#[test]
fn data_transfer_item_list_tracks_clear_data_removals() -> Result<()> {
    let html = r#"
      <div id='source' draggable='true'></div>
      <p id='out'></p>
      <script>
        document.getElementById('source').addEventListener('dragstart', (event) => {
          const dt = event.dataTransfer;
          dt.setData('text/plain', 'alpha');
          dt.setData('text/uri-list', 'https://example.com');
          dt.clearData('text/plain');

          const firstItem = dt.items[0];
          let value = '';
          firstItem.getAsString((text) => {
            value = text;
          });
          document.getElementById('out').textContent = [
            dt.items.length,
            firstItem.type,
            value
          ].join('|');
        });
      </script>
    "#;

    let mut h = Harness::from_html(html)?;
    h.dispatch("#source", "dragstart")?;
    h.assert_text("#out", "1|text/uri-list|https://example.com")?;
    Ok(())
}

#[test]
fn data_transfer_item_get_as_string_requires_callback() {
    let html = r#"
      <div id='source' draggable='true'></div>
      <script>
        document.getElementById('source').addEventListener('dragstart', (event) => {
          const dt = event.dataTransfer;
          dt.setData('text/plain', 'alpha');
          dt.items[0].getAsString();
        });
      </script>
    "#;

    let mut h = Harness::from_html(html).expect("harness should initialize");
    let err = h
        .dispatch("#source", "dragstart")
        .expect_err("getAsString should require a callback argument");
    match err {
        Error::ScriptRuntime(msg) => assert_eq!(
            msg,
            "DataTransferItem.getAsString requires exactly one callback argument"
        ),
        other => panic!("unexpected error: {other:?}"),
    }
}

#[test]
fn data_transfer_item_get_as_string_requires_callable_callback() {
    let html = r#"
      <div id='source' draggable='true'></div>
      <script>
        document.getElementById('source').addEventListener('dragstart', (event) => {
          const dt = event.dataTransfer;
          dt.setData('text/plain', 'alpha');
          dt.items[0].getAsString('not-a-function');
        });
      </script>
    "#;

    let mut h = Harness::from_html(html).expect("harness should initialize");
    let err = h
        .dispatch("#source", "dragstart")
        .expect_err("getAsString should require a callable callback");
    match err {
        Error::ScriptRuntime(msg) => assert_eq!(
            msg,
            "DataTransferItem.getAsString callback must be callable"
        ),
        other => panic!("unexpected error: {other:?}"),
    }
}

#[test]
fn data_transfer_item_get_as_string_does_not_invoke_callback_for_file_item() -> Result<()> {
    let html = r#"
      <input id='upload' type='file'>
      <div id='source' draggable='true'></div>
      <p id='out'></p>
      <script>
        const source = document.getElementById('source');
        const upload = document.getElementById('upload');
        source.addEventListener('dragstart', (event) => {
          const dt = event.dataTransfer;
          dt.files.push(upload.files[0]);
          dt.setData('text/plain', 'alpha');

          const fileItem = dt.items[1];
          let called = false;
          const ret = fileItem.getAsString(() => {
            called = true;
          });
          document.getElementById('out').textContent = [
            fileItem.kind,
            ret === undefined,
            called
          ].join('|');
        });
      </script>
    "#;

    let mut h = Harness::from_html(html)?;
    h.set_input_files("#upload", &[MockFile::new("drop.txt").with_text("hello")])?;
    h.dispatch("#source", "dragstart")?;
    h.assert_text("#out", "file|true|false")?;
    Ok(())
}

#[test]
fn data_transfer_item_get_as_file_returns_mock_file_for_file_item() -> Result<()> {
    let html = r#"
      <input id='upload' type='file'>
      <div id='source' draggable='true'></div>
      <p id='out'></p>
      <script>
        const source = document.getElementById('source');
        const upload = document.getElementById('upload');
        source.addEventListener('dragstart', (event) => {
          const dt = event.dataTransfer;
          dt.files.push(upload.files[0]);
          dt.setData('text/plain', 'alpha');

          const fileItem = dt.items[1];
          const file = fileItem.getAsFile();
          document.getElementById('out').textContent = [
            dt.items.length,
            fileItem.kind,
            fileItem.type,
            file && file.name,
            file && file.size
          ].join('|');
        });
      </script>
    "#;

    let mut h = Harness::from_html(html)?;
    h.set_input_files("#upload", &[MockFile::new("drop.txt").with_text("hello")])?;
    h.dispatch("#source", "dragstart")?;
    h.assert_text("#out", "2|file|text/plain|drop.txt|5")?;
    Ok(())
}

#[test]
fn data_transfer_item_get_as_file_rejects_extra_arguments() {
    let html = r#"
      <div id='source' draggable='true'></div>
      <script>
        document.getElementById('source').addEventListener('dragstart', (event) => {
          const dt = event.dataTransfer;
          dt.setData('text/plain', 'alpha');
          dt.items[0].getAsFile('unexpected');
        });
      </script>
    "#;

    let mut h = Harness::from_html(html).expect("harness should initialize");
    let err = h
        .dispatch("#source", "dragstart")
        .expect_err("getAsFile should reject extra arguments");
    match err {
        Error::ScriptRuntime(msg) => {
            assert_eq!(msg, "DataTransferItem.getAsFile does not take arguments")
        }
        other => panic!("unexpected error: {other:?}"),
    }
}