tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_aboutdebugging_telemetry_connection_attempt.js (7946B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /* import-globals-from helper-telemetry.js */
      7 Services.scriptloader.loadSubScript(
      8  CHROME_URL_ROOT + "helper-telemetry.js",
      9  this
     10 );
     11 
     12 const USB_RUNTIME = {
     13  id: "runtime-id-1",
     14  deviceName: "Device A",
     15  name: "Runtime 1",
     16  shortName: "R1",
     17 };
     18 
     19 /**
     20 * Check that telemetry events for connection attempts are correctly recorded in various
     21 * scenarios:
     22 * - successful connection
     23 * - successful connection after showing the timeout warning
     24 * - failed connection
     25 * - connection timeout
     26 */
     27 add_task(async function testSuccessfulConnectionAttempt() {
     28  const { doc, mocks, runtimeId, sessionId, tab } =
     29    await setupConnectionAttemptTest();
     30 
     31  await connectToRuntime(USB_RUNTIME.deviceName, doc);
     32 
     33  const connectionEvents = checkTelemetryEvents(
     34    [
     35      { method: "runtime_connected", extras: { runtime_id: runtimeId } },
     36      {
     37        method: "connection_attempt",
     38        extras: getEventExtras("start", runtimeId),
     39      },
     40      {
     41        method: "connection_attempt",
     42        extras: getEventExtras("success", runtimeId),
     43      },
     44      { method: "select_page", extras: { page_type: "runtime" } },
     45    ],
     46    sessionId
     47  ).filter(({ method }) => method === "connection_attempt");
     48 
     49  checkConnectionId(connectionEvents);
     50 
     51  await removeUsbRuntime(USB_RUNTIME, mocks, doc);
     52  await removeTab(tab);
     53 });
     54 
     55 add_task(async function testFailedConnectionAttempt() {
     56  const { doc, mocks, runtimeId, sessionId, tab } =
     57    await setupConnectionAttemptTest();
     58  mocks.runtimeClientFactoryMock.createClientForRuntime = async () => {
     59    throw new Error("failed");
     60  };
     61 
     62  info(
     63    "Try to connect to the runtime and wait for the connection error message"
     64  );
     65  const usbRuntimeSidebarItem = findSidebarItemByText(
     66    USB_RUNTIME.deviceName,
     67    doc
     68  );
     69  const connectButton =
     70    usbRuntimeSidebarItem.querySelector(".qa-connect-button");
     71  connectButton.click();
     72  await waitUntil(() =>
     73    usbRuntimeSidebarItem.querySelector(".qa-connection-error")
     74  );
     75 
     76  const connectionEvents = checkTelemetryEvents(
     77    [
     78      {
     79        method: "connection_attempt",
     80        extras: getEventExtras("start", runtimeId),
     81      },
     82      {
     83        method: "connection_attempt",
     84        extras: getEventExtras("failed", runtimeId),
     85      },
     86    ],
     87    sessionId
     88  ).filter(({ method }) => method === "connection_attempt");
     89 
     90  checkConnectionId(connectionEvents);
     91 
     92  await removeUsbRuntime(USB_RUNTIME, mocks, doc);
     93  await removeTab(tab);
     94 });
     95 
     96 add_task(async function testPendingConnectionAttempt() {
     97  info("Set timeout preferences to avoid cancelling the connection");
     98  await pushPref(
     99    "devtools.aboutdebugging.test-connection-timing-out-delay",
    100    100
    101  );
    102  await pushPref(
    103    "devtools.aboutdebugging.test-connection-cancel-delay",
    104    100000
    105  );
    106 
    107  const { doc, mocks, runtimeId, sessionId, tab } =
    108    await setupConnectionAttemptTest();
    109 
    110  info("Simulate a pending connection");
    111  let resumeConnection;
    112  const resumeConnectionPromise = new Promise(r => {
    113    resumeConnection = r;
    114  });
    115  mocks.runtimeClientFactoryMock.createClientForRuntime = async runtime => {
    116    await resumeConnectionPromise;
    117    return mocks._clients[runtime.type][runtime.id];
    118  };
    119 
    120  info("Click on the connect button and wait for the warning message");
    121  const usbRuntimeSidebarItem = findSidebarItemByText(
    122    USB_RUNTIME.deviceName,
    123    doc
    124  );
    125  const connectButton =
    126    usbRuntimeSidebarItem.querySelector(".qa-connect-button");
    127  connectButton.click();
    128  await waitUntil(() => doc.querySelector(".qa-connection-not-responding"));
    129 
    130  info("Resume the connection and wait for the connection to succeed");
    131  resumeConnection();
    132  await waitUntil(
    133    () => !usbRuntimeSidebarItem.querySelector(".qa-connect-button")
    134  );
    135 
    136  const connectionEvents = checkTelemetryEvents(
    137    [
    138      { method: "runtime_connected", extras: { runtime_id: runtimeId } },
    139      {
    140        method: "connection_attempt",
    141        extras: getEventExtras("start", runtimeId),
    142      },
    143      {
    144        method: "connection_attempt",
    145        extras: getEventExtras("not responding", runtimeId),
    146      },
    147      {
    148        method: "connection_attempt",
    149        extras: getEventExtras("success", runtimeId),
    150      },
    151      { method: "select_page", extras: { page_type: "runtime" } },
    152    ],
    153    sessionId
    154  ).filter(({ method }) => method === "connection_attempt");
    155  checkConnectionId(connectionEvents);
    156 
    157  await removeUsbRuntime(USB_RUNTIME, mocks, doc);
    158  await removeTab(tab);
    159 });
    160 
    161 add_task(async function testCancelledConnectionAttempt() {
    162  info("Set timeout preferences to quickly cancel the connection");
    163  await pushPref(
    164    "devtools.aboutdebugging.test-connection-timing-out-delay",
    165    100
    166  );
    167  await pushPref("devtools.aboutdebugging.test-connection-cancel-delay", 1000);
    168 
    169  const { doc, mocks, runtimeId, sessionId, tab } =
    170    await setupConnectionAttemptTest();
    171 
    172  info("Simulate a connection timeout");
    173  mocks.runtimeClientFactoryMock.createClientForRuntime = async () => {
    174    await new Promise(() => {});
    175  };
    176 
    177  info("Click on the connect button and wait for the error message");
    178  const usbRuntimeSidebarItem = findSidebarItemByText(
    179    USB_RUNTIME.deviceName,
    180    doc
    181  );
    182  const connectButton =
    183    usbRuntimeSidebarItem.querySelector(".qa-connect-button");
    184  connectButton.click();
    185  await waitUntil(() =>
    186    usbRuntimeSidebarItem.querySelector(".qa-connection-timeout")
    187  );
    188 
    189  const connectionEvents = checkTelemetryEvents(
    190    [
    191      {
    192        method: "connection_attempt",
    193        extras: getEventExtras("start", runtimeId),
    194      },
    195      {
    196        method: "connection_attempt",
    197        extras: getEventExtras("not responding", runtimeId),
    198      },
    199      {
    200        method: "connection_attempt",
    201        extras: getEventExtras("cancelled", runtimeId),
    202      },
    203    ],
    204    sessionId
    205  ).filter(({ method }) => method === "connection_attempt");
    206  checkConnectionId(connectionEvents);
    207 
    208  await removeUsbRuntime(USB_RUNTIME, mocks, doc);
    209  await removeTab(tab);
    210 });
    211 
    212 function checkConnectionId(connectionEvents) {
    213  const connectionId = connectionEvents[0].extras.connection_id;
    214  ok(
    215    !!connectionId,
    216    "Found a valid connection id in the first connection_attempt event"
    217  );
    218  for (const evt of connectionEvents) {
    219    is(
    220      evt.extras.connection_id,
    221      connectionId,
    222      "All connection_attempt events share the same connection id"
    223    );
    224  }
    225 }
    226 
    227 // Small helper to create the expected event extras object for connection_attempt events
    228 function getEventExtras(status, runtimeId) {
    229  return {
    230    connection_type: "usb",
    231    runtime_id: runtimeId,
    232    status,
    233  };
    234 }
    235 
    236 // Open about:debugging, setup telemetry, mocks and create a mocked USB runtime.
    237 async function setupConnectionAttemptTest() {
    238  const mocks = new Mocks();
    239  setupTelemetryTest();
    240 
    241  const { tab, document } = await openAboutDebugging();
    242 
    243  const sessionId = getOpenEventSessionId();
    244  ok(!isNaN(sessionId), "Open event has a valid session id");
    245 
    246  mocks.createUSBRuntime(USB_RUNTIME.id, {
    247    deviceName: USB_RUNTIME.deviceName,
    248    name: USB_RUNTIME.name,
    249    shortName: USB_RUNTIME.shortName,
    250  });
    251  mocks.emitUSBUpdate();
    252 
    253  info("Wait for the runtime to appear in the sidebar");
    254  await waitUntil(() => findSidebarItemByText(USB_RUNTIME.shortName, document));
    255  const evts = checkTelemetryEvents(
    256    [
    257      { method: "device_added", extras: {} },
    258      { method: "runtime_added", extras: {} },
    259    ],
    260    sessionId
    261  );
    262 
    263  const runtimeId = evts.filter(e => e.method === "runtime_added")[0].extras
    264    .runtime_id;
    265  return { doc: document, mocks, runtimeId, sessionId, tab };
    266 }
    267 
    268 async function removeUsbRuntime(runtime, mocks, doc) {
    269  mocks.removeRuntime(runtime.id);
    270  mocks.emitUSBUpdate();
    271  await waitUntil(
    272    () =>
    273      !findSidebarItemByText(runtime.name, doc) &&
    274      !findSidebarItemByText(runtime.shortName, doc)
    275  );
    276 }