/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ const TEST_URL = "https://example.com/document-builder.sjs?html=

Test serial port chooser

"; add_setup(async function () { await SpecialPowers.pushPrefEnv({ set: [["dom.webserial.gated", false]], }); registerCleanupFunction(() => { while (gBrowser.tabs.length > 1) { BrowserTestUtils.removeTab(gBrowser.selectedTab); } }); }); add_task(async function testChooserCancel() { info("Test that canceling the port chooser throws NotFoundError"); await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); let popupShown = BrowserTestUtils.waitForEvent( PopupNotifications.panel, "popupshown" ); await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { content.navigator.serial.autoselectPorts = false; SpecialPowers.wrap(content.document).notifyUserGestureActivation(); content.portRequestPromise = content.navigator.serial.requestPort(); }); await popupShown; is( PopupNotifications.panel.querySelector("popupnotification").id, "webSerial-choosePort-notification", "Port chooser notification was displayed" ); PopupNotifications.panel .querySelector(".popup-notification-secondary-button") .click(); let errorInfo = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], async () => { let errorName, errorMessage; try { await content.portRequestPromise; } catch (e) { errorName = e.name; errorMessage = e.message; } delete content.portRequestPromise; return { name: errorName, message: errorMessage }; } ); is(errorInfo.name, "NotFoundError", "Rejection is NotFoundError"); is(errorInfo.message, "No port selected", "Error message is correct"); }); add_task(async function testChooserNoPorts() { info("Test that the port chooser is shown even if there are no ports"); await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); let popupShown = BrowserTestUtils.waitForEvent( PopupNotifications.panel, "popupshown" ); await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { await content.navigator.serial.removeAllMockDevices(); content.navigator.serial.autoselectPorts = false; SpecialPowers.wrap(content.document).notifyUserGestureActivation(); content.portRequestPromise = content.navigator.serial.requestPort(); }); await popupShown; is( PopupNotifications.panel.querySelector("popupnotification").id, "webSerial-choosePort-notification", "Port chooser notification was displayed" ); let menulist = document.getElementById("webSerial-selectPort-menulist"); ok(menulist, "Found the port selection menulist"); is(menulist.hidden, true, "menulist should be hidden"); let noPortsMsg = document.getElementById("webSerial-no-ports-available"); ok(noPortsMsg, "Found the no ports message"); is(noPortsMsg.hidden, false, "no ports message should be visible"); PopupNotifications.panel .querySelector(".popup-notification-secondary-button") .click(); let errorInfo = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], async () => { let errorName, errorMessage; try { await content.portRequestPromise; } catch (e) { errorName = e.name; errorMessage = e.message; } delete content.portRequestPromise; return { name: errorName, message: errorMessage }; } ); is(errorInfo.name, "NotFoundError", "Rejection is NotFoundError"); is(errorInfo.message, "No port selected", "Error message is correct"); await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { await content.navigator.serial.resetToDefaultMockDevices(); }); }); add_task(async function testChooserSelection() { info("Test that selecting a port from the chooser resolves the promise"); await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); let popupShown = BrowserTestUtils.waitForEvent( PopupNotifications.panel, "popupshown" ); let originalPortsLength = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], async () => { return (await content.navigator.serial.getPorts()).length; } ); is( originalPortsLength, 0, "getPorts() returns empty list before requestPort() is called" ); await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { content.navigator.serial.autoselectPorts = false; SpecialPowers.wrap(content.document).notifyUserGestureActivation(); content.portRequestPromise = content.navigator.serial.requestPort(); }); await popupShown; PopupNotifications.panel .querySelector(".popup-notification-primary-button") .click(); let gotPort = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], async () => { try { const port = await content.portRequestPromise; delete content.portRequestPromise; return !!port; } catch (e) { return false; } } ); ok(gotPort, "requestPort resolved with a port"); let portsLength = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], async () => { let ports = await content.navigator.serial.getPorts(); return ports.length; } ); is( portsLength, 1, "getPorts() returns non-empty list after requestPort() is called" ); }); add_task(async function testChooserSelectSecondDevice() { info( "Test that selecting the second device from the chooser works correctly" ); await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); let popupShown = BrowserTestUtils.waitForEvent( PopupNotifications.panel, "popupshown" ); await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { content.navigator.serial.autoselectPorts = false; SpecialPowers.wrap(content.document).notifyUserGestureActivation(); content.portRequestPromise = content.navigator.serial.requestPort(); }); await popupShown; is( PopupNotifications.panel.querySelector("popupnotification").id, "webSerial-choosePort-notification", "Port chooser notification was displayed" ); let menulist = document.getElementById("webSerial-selectPort-menulist"); ok(menulist, "Found the port selection menulist"); is(menulist.hidden, false, "menulist should be visible"); let noPortsMsg = document.getElementById("webSerial-no-ports-available"); ok(noPortsMsg, "Found the no ports message"); is(noPortsMsg.hidden, true, "no ports message should be hidden"); let itemCount = menulist.itemCount; info(`Menulist has ${itemCount} items`); if (itemCount < 2) { ok( false, "Need at least 2 ports to test selecting the second device, this should be guaranteed by dom.webserial.testing.enabled." ); PopupNotifications.panel .querySelector(".popup-notification-secondary-button") .click(); return; } let firstPortLabel = menulist.getItemAtIndex(0).label; let secondPortLabel = menulist.getItemAtIndex(1).label; is(firstPortLabel, "test-device-1", "First port has correct label"); is(secondPortLabel, "test-device-2", "Second port has correct label"); menulist.selectedIndex = 1; is(menulist.selectedIndex, 1, "Selected second item in menulist"); is( menulist.selectedItem.label, secondPortLabel, "Selected item label matches second port" ); PopupNotifications.panel .querySelector(".popup-notification-primary-button") .click(); let portInfo = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], async () => { try { const port = await content.portRequestPromise; delete content.portRequestPromise; const info = port.getInfo(); return { success: true, usbVendorId: info.usbVendorId || 0, usbProductId: info.usbProductId || 0, }; } catch (e) { return { success: false, error: e.message }; } } ); ok(portInfo.success, "requestPort resolved with a port"); is(portInfo.usbVendorId, 0x0403, "Second port has correct vendor ID"); is(portInfo.usbProductId, 0x6002, "Second port has correct product ID"); });