tor-browser

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

browser_net_simple-request-details.js (10261B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * Tests if requests render correct information in the details UI.
      8 */
      9 
     10 add_task(async function () {
     11  const {
     12    L10N,
     13  } = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
     14 
     15  const { monitor } = await initNetMonitor(SIMPLE_SJS, {
     16    requestCount: 1,
     17  });
     18  info("Starting test... ");
     19 
     20  const { document, store, windowRequire } = monitor.panelWin;
     21  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
     22  const { PANELS } = windowRequire("devtools/client/netmonitor/src/constants");
     23  const { getSelectedRequest, getSortedRequests } = windowRequire(
     24    "devtools/client/netmonitor/src/selectors/index"
     25  );
     26 
     27  store.dispatch(Actions.batchEnable(false));
     28 
     29  const wait = waitForNetworkEvents(monitor, 1);
     30  await reloadBrowser();
     31  await wait;
     32 
     33  is(
     34    getSelectedRequest(store.getState()),
     35    undefined,
     36    "There shouldn't be any selected item in the requests menu."
     37  );
     38  is(
     39    store.getState().requests.requests.length,
     40    1,
     41    "The requests menu should not be empty after the first request."
     42  );
     43  is(
     44    !!document.querySelector(".network-details-bar"),
     45    false,
     46    "The network details panel should still be hidden after first request."
     47  );
     48 
     49  const waitForHeaders = waitForDOM(document, ".headers-overview");
     50 
     51  store.dispatch(Actions.toggleNetworkDetails());
     52 
     53  isnot(
     54    getSelectedRequest(store.getState()),
     55    undefined,
     56    "There should be a selected item in the requests menu."
     57  );
     58  is(
     59    getSelectedIndex(store.getState()),
     60    0,
     61    "The first item should be selected in the requests menu."
     62  );
     63  is(
     64    !!document.querySelector(".network-details-bar"),
     65    true,
     66    "The network details panel should not be hidden after toggle button was pressed."
     67  );
     68 
     69  await waitForHeaders;
     70 
     71  await testHeadersTab();
     72  await testCookiesTab();
     73  await testParamsTab();
     74  await testResponseTab();
     75  await testTimingsTab();
     76  await closePanelOnEsc();
     77  return teardown(monitor);
     78 
     79  function getSelectedIndex(state) {
     80    if (!state.requests.selectedId) {
     81      return -1;
     82    }
     83    return getSortedRequests(state).findIndex(
     84      r => r.id === state.requests.selectedId
     85    );
     86  }
     87 
     88  async function testHeadersTab() {
     89    const tabEl = document.querySelectorAll(
     90      ".network-details-bar .tabs-menu a"
     91    )[0];
     92    const tabpanel = document.querySelector("#headers-panel");
     93 
     94    is(
     95      tabEl.getAttribute("aria-selected"),
     96      "true",
     97      "The headers tab in the network details pane should be selected."
     98    );
     99    // Request URL
    100    is(
    101      tabpanel.querySelector(".url-preview .url").innerText,
    102      SIMPLE_SJS,
    103      "The url summary value is incorrect."
    104    );
    105 
    106    // Request method
    107    is(
    108      tabpanel.querySelectorAll(".treeLabel")[0].innerText,
    109      "GET",
    110      "The method summary value is incorrect."
    111    );
    112    // Status code
    113    is(
    114      tabpanel.querySelector(".requests-list-status-code").innerText,
    115      "200",
    116      "The status summary code is incorrect."
    117    );
    118    is(
    119      tabpanel.querySelector(".status").childNodes[1].textContent,
    120      "Och Aye",
    121      "The status summary value is incorrect."
    122    );
    123    // Version
    124    is(
    125      tabpanel.querySelectorAll(".tabpanel-summary-value")[1].innerText,
    126      "HTTP/1.1",
    127      "The HTTP version is incorrect."
    128    );
    129 
    130    await waitForRequestData(store, ["requestHeaders", "responseHeaders"]);
    131 
    132    is(
    133      tabpanel.querySelectorAll(".accordion-item").length,
    134      2,
    135      "There should be 2 header scopes displayed in this tabpanel."
    136    );
    137 
    138    is(
    139      tabpanel.querySelectorAll(".accordion .treeLabelCell").length,
    140      24,
    141      "There should be 24 header values displayed in this tabpanel."
    142    );
    143 
    144    const headersTable = tabpanel.querySelector(".accordion");
    145    const responseScope = headersTable.querySelectorAll(
    146      "tr[id^='/responseHeaders']"
    147    );
    148    const requestScope = headersTable.querySelectorAll(
    149      "tr[id^='/requestHeaders']"
    150    );
    151 
    152    const headerLabels = headersTable.querySelectorAll(
    153      ".accordion-item .accordion-header-label"
    154    );
    155 
    156    ok(
    157      headerLabels[0].innerHTML.match(
    158        new RegExp(L10N.getStr("responseHeaders") + " \\([0-9]+ .+\\)")
    159      ),
    160      "The response headers scope doesn't have the correct title."
    161    );
    162 
    163    ok(
    164      headerLabels[1].innerHTML.includes(L10N.getStr("requestHeaders") + " ("),
    165      "The request headers scope doesn't have the correct title."
    166    );
    167 
    168    const responseHeaders = [
    169      {
    170        name: "cache-control",
    171        value: "no-cache, no-store, must-revalidate",
    172        pos: "first",
    173        index: 1,
    174      },
    175      {
    176        name: "connection",
    177        value: "close",
    178        pos: "second",
    179        index: 2,
    180      },
    181      {
    182        name: "content-length",
    183        value: "12",
    184        pos: "third",
    185        index: 3,
    186      },
    187      {
    188        name: "content-type",
    189        value: "text/plain; charset=utf-8",
    190        pos: "fourth",
    191        index: 4,
    192      },
    193      {
    194        name: "foo-bar",
    195        value: "baz",
    196        pos: "seventh",
    197        index: 7,
    198      },
    199    ];
    200    responseHeaders.forEach(header => {
    201      is(
    202        responseScope[header.index - 1].querySelector(".treeLabel").innerHTML,
    203        header.name,
    204        `The ${header.pos} response header name was incorrect.`
    205      );
    206      is(
    207        responseScope[header.index - 1].querySelector(".objectBox").innerHTML,
    208        `${header.value}`,
    209        `The ${header.pos} response header value was incorrect.`
    210      );
    211    });
    212 
    213    const requestHeaders = [
    214      {
    215        name: "Cache-Control",
    216        value: "no-cache",
    217        pos: "fourth",
    218        index: 4,
    219      },
    220      {
    221        name: "Connection",
    222        value: "keep-alive",
    223        pos: "fifth",
    224        index: 5,
    225      },
    226      {
    227        name: "Host",
    228        value: "example.com",
    229        pos: "seventh",
    230        index: 7,
    231      },
    232      {
    233        name: "Pragma",
    234        value: "no-cache",
    235        pos: "eighth",
    236        index: 8,
    237      },
    238    ];
    239    requestHeaders.forEach(header => {
    240      is(
    241        requestScope[header.index - 1].querySelector(".treeLabel").innerHTML,
    242        header.name,
    243        `The ${header.pos} request header name was incorrect.`
    244      );
    245      is(
    246        requestScope[header.index - 1].querySelector(".objectBox").innerHTML,
    247        `${header.value}`,
    248        `The ${header.pos} request header value was incorrect.`
    249      );
    250    });
    251  }
    252 
    253  async function testCookiesTab() {
    254    const tabpanel = await selectTab(PANELS.COOKIES, 1);
    255 
    256    const cookieAccordion = tabpanel.querySelector(".accordion");
    257 
    258    is(
    259      cookieAccordion.querySelectorAll(".accordion-item").length,
    260      2,
    261      "There should be 2 cookie scopes displayed in this tabpanel."
    262    );
    263    // 2 Cookies in response - 1 httpOnly and 1 value for each cookie - total 6
    264 
    265    const resCookiesTable = cookieAccordion.querySelector(
    266      "li[id='responseCookies'] .accordion-content .treeTable"
    267    );
    268    is(
    269      resCookiesTable.querySelectorAll("tr.treeRow").length,
    270      6,
    271      "There should be 6 rows displayed in response cookies table"
    272    );
    273 
    274    const reqCookiesTable = cookieAccordion.querySelector(
    275      "li[id='requestCookies'] .accordion-content .treeTable"
    276    );
    277    is(
    278      reqCookiesTable.querySelectorAll("tr.treeRow").length,
    279      2,
    280      "There should be 2 cookie values displayed in request cookies table."
    281    );
    282  }
    283 
    284  async function testParamsTab() {
    285    const tabpanel = await selectTab(PANELS.REQUEST, 2);
    286 
    287    is(
    288      tabpanel.querySelectorAll(".panel-container").length,
    289      0,
    290      "There should be no param scopes displayed in this tabpanel."
    291    );
    292    is(
    293      tabpanel.querySelectorAll(".empty-notice").length,
    294      1,
    295      "The empty notice should be displayed in this tabpanel."
    296    );
    297  }
    298 
    299  async function testResponseTab() {
    300    const tabpanel = await selectTab(PANELS.RESPONSE, 3);
    301    await waitForDOM(document, "#response-panel .source-editor-mount");
    302 
    303    is(
    304      tabpanel.querySelectorAll(
    305        "#response-panel .raw-data-toggle-input .devtools-checkbox-toggle"
    306      ).length,
    307      0,
    308      "The raw data toggle should not be shown in this tabpanel."
    309    );
    310    is(
    311      tabpanel.querySelectorAll(".source-editor-mount").length,
    312      1,
    313      "The response payload should be shown initially."
    314    );
    315  }
    316 
    317  async function testTimingsTab() {
    318    const tabpanel = await selectTab(PANELS.TIMINGS, 4);
    319 
    320    const displayFormat = new RegExp(/[0-9]+ ms$/);
    321    const propsToVerify = [
    322      "blocked",
    323      "dns",
    324      "connect",
    325      "ssl",
    326      "send",
    327      "wait",
    328      "receive",
    329    ];
    330 
    331    // To ensure that test case for a new property is written, otherwise this
    332    // test will fail
    333    is(
    334      tabpanel.querySelectorAll(".tabpanel-summary-container").length,
    335      propsToVerify.length,
    336      `There should be exactly ${propsToVerify.length} values
    337      displayed in this tabpanel`
    338    );
    339 
    340    propsToVerify.forEach(propName => {
    341      ok(
    342        tabpanel
    343          .querySelector(
    344            `#timings-summary-${propName}
    345      .requests-list-timings-total`
    346          )
    347          .innerHTML.match(displayFormat),
    348        `The ${propName} timing info does not appear to be correct.`
    349      );
    350    });
    351  }
    352 
    353  async function selectTab(tabName, pos) {
    354    const tabEl = document.querySelectorAll(
    355      ".network-details-bar .tabs-menu a"
    356    )[pos];
    357 
    358    const onPanelOpen = waitForDOM(document, `#${tabName}-panel`);
    359    clickOnSidebarTab(
    360      document,
    361      tabEl.id.substring(0, tabEl.id.indexOf("-tab"))
    362    );
    363    await onPanelOpen;
    364 
    365    is(
    366      tabEl.getAttribute("aria-selected"),
    367      "true",
    368      `The ${tabName} tab in the network details pane should be selected.`
    369    );
    370 
    371    return document.querySelector(".network-details-bar .tab-panel");
    372  }
    373 
    374  // This test will timeout on failure
    375  async function closePanelOnEsc() {
    376    EventUtils.sendKey("ESCAPE", window);
    377 
    378    await waitUntil(() => {
    379      return document.querySelector(".network-details-bar") == null;
    380    });
    381 
    382    is(
    383      document.querySelectorAll(".network-details-bar").length,
    384      0,
    385      "Network details panel should close on ESC key"
    386    );
    387  }
    388 });