tor-browser

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

browser_net_sse-basic.js (9045B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * Test basic SSE connection.
      8 */
      9 
     10 function setupTestServer() {
     11  const httpServer = createTestHTTPServer();
     12  httpServer.registerContentType("html", "text/html");
     13  httpServer.registerPathHandler("/index.html", function (request, response) {
     14    response.setStatusLine(request.httpVersion, 200, "OK");
     15    response.write(
     16      `<!DOCTYPE html>
     17      <html>
     18        <head>
     19          <title>SSE Inspection Test Page</title>
     20        </head>
     21        <body>
     22            <h1>SSE Inspection Test Page</h1>
     23            <script type="text/javascript">
     24            /* exported openConnection, closeConnection */
     25            "use strict";
     26 
     27            let es;
     28            function openConnection(endpoint) {
     29              return new Promise(resolve => {
     30                es = new EventSource("http://localhost:${httpServer.identity.primaryPort}/" + endpoint);
     31                es.onmessage = function () {
     32                  resolve();
     33                };
     34              });
     35            }
     36 
     37            function closeConnection() {
     38              es.close();
     39            }
     40            </script>
     41        </body>
     42      </html>
     43    `
     44    );
     45  });
     46 
     47  let sseResponse;
     48  httpServer.registerPathHandler("/sse", function (request, response) {
     49    response.processAsync();
     50    response.setHeader("Content-Type", "text/event-stream");
     51    response.write("data: Why so serious?\n\n");
     52    response.write("data: Why so serious?\n\n");
     53    response.write("data: Why so serious?\n\n");
     54    response.finish();
     55  });
     56 
     57  httpServer.registerPathHandler("/sse-delay", function (request, response) {
     58    response.processAsync();
     59    response.setHeader("Content-Type", "text/event-stream");
     60    response.write("data: Why so serious?\n\n");
     61    sseResponse = response;
     62  });
     63 
     64  const sendResponseMessages = () => {
     65    sseResponse.write("data: Another why so serious?\n\n");
     66    sseResponse.write("data: Another why so serious?\n\n");
     67    sseResponse.write("data: Another why so serious?\n\n");
     68  };
     69 
     70  const completeResponse = () => {
     71    sseResponse.finish();
     72  };
     73 
     74  return { httpServer, sendResponseMessages, completeResponse };
     75 }
     76 
     77 add_task(async function testBasicServerSentEvents() {
     78  const { httpServer } = setupTestServer();
     79  const port = httpServer.identity.primaryPort;
     80 
     81  const { tab, monitor } = await initNetMonitor(
     82    `http://localhost:${port}/index.html`,
     83    { requestCount: 1 }
     84  );
     85  info("Starting test... ");
     86 
     87  const { document, store, windowRequire } = monitor.panelWin;
     88  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
     89 
     90  store.dispatch(Actions.batchEnable(false));
     91 
     92  const onNetworkEvents = waitForNetworkEvents(monitor, 1);
     93  await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
     94    await content.wrappedJSObject.openConnection("sse");
     95  });
     96  await onNetworkEvents;
     97 
     98  const requests = document.querySelectorAll(".request-list-item");
     99  is(requests.length, 1, "There should be one request");
    100 
    101  const requestItem = document.querySelectorAll(".request-list-item")[0];
    102 
    103  const type = requestItem.querySelector(".requests-list-type").textContent;
    104  is(type, "eventsource", "Type should be rendered correctly.");
    105 
    106  // Select the request to open the side panel.
    107  EventUtils.sendMouseEvent({ type: "mousedown" }, requests[0]);
    108 
    109  // Wait for messages to be displayed in DevTools
    110  const waitForMessages = waitForDOM(
    111    document,
    112    "#messages-view .message-list-table .message-list-item",
    113    3
    114  );
    115 
    116  // Test that 'Save Response As' is not in the context menu
    117  EventUtils.sendMouseEvent({ type: "contextmenu" }, requests[0]);
    118 
    119  ok(
    120    !getContextMenuItem(monitor, "request-list-context-save-response-as"),
    121    "The 'Save Response As' context menu item should be hidden"
    122  );
    123 
    124  // Close context menu.
    125  const contextMenu = monitor.toolbox.topDoc.querySelector(
    126    'popupset menupopup[menu-api="true"]'
    127  );
    128  const popupHiddenPromise = BrowserTestUtils.waitForEvent(
    129    contextMenu,
    130    "popuphidden"
    131  );
    132  contextMenu.hidePopup();
    133  await popupHiddenPromise;
    134 
    135  // Click on the "Response" panel
    136  clickOnSidebarTab(document, "response");
    137  await waitForMessages;
    138 
    139  // Get all messages present in the "Response" panel
    140  const frames = document.querySelectorAll(
    141    "#messages-view .message-list-table .message-list-item"
    142  );
    143 
    144  // Check expected results
    145  is(frames.length, 3, "There should be three messages");
    146 
    147  is(
    148    frames[0].querySelector(".message-list-payload").textContent,
    149    // Initial whitespace comes from ColumnData.
    150    " Why so serious?",
    151    "Data column shows correct payload"
    152  );
    153 
    154  await waitForDOMIfNeeded(
    155    document,
    156    "#messages-view .msg-connection-closed-message",
    157    1
    158  );
    159 
    160  is(
    161    !!document.querySelector("#messages-view .msg-connection-closed-message"),
    162    true,
    163    "Connection closed message should be displayed"
    164  );
    165 
    166  is(
    167    document.querySelector(".message-network-summary-count").textContent,
    168    "3 messages",
    169    "Correct message count is displayed"
    170  );
    171 
    172  is(
    173    document.querySelector(".message-network-summary-total-size").textContent,
    174    "45 B total",
    175    "Correct total size should be displayed"
    176  );
    177 
    178  is(
    179    !!document.querySelector(".message-network-summary-total-millis"),
    180    true,
    181    "Total time is displayed"
    182  );
    183 
    184  is(
    185    document.getElementById("frame-filter-menu"),
    186    null,
    187    "Toolbar filter menu is hidden"
    188  );
    189 
    190  await waitForTick();
    191 
    192  EventUtils.sendMouseEvent(
    193    { type: "contextmenu" },
    194    document.querySelector(".message-list-headers")
    195  );
    196 
    197  const columns = ["data", "time", "retry", "size", "eventName", "lastEventId"];
    198  for (const column of columns) {
    199    is(
    200      !!getContextMenuItem(monitor, `message-list-header-${column}-toggle`),
    201      true,
    202      `Context menu item "${column}" is displayed`
    203    );
    204  }
    205 
    206  return teardown(monitor);
    207 });
    208 
    209 /**
    210 * Test various scenarios around SSE requests,
    211 * 1) Assert that the SSE requests messages are displayed before the connection is closed.
    212 * 2) Assert that subsequent messages after the response panel is open are visible.
    213 * 3) Assert that the close connection message is displayed when the connection is closed.
    214 */
    215 add_task(async function testServerSentEventsDetails() {
    216  const { httpServer, sendResponseMessages, completeResponse } =
    217    setupTestServer();
    218  const port = httpServer.identity.primaryPort;
    219 
    220  const { tab, monitor } = await initNetMonitor(
    221    `http://localhost:${port}/index.html`,
    222    {
    223      requestCount: 1,
    224    }
    225  );
    226  info("Starting test... ");
    227 
    228  const { document, store, windowRequire } = monitor.panelWin;
    229  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
    230 
    231  store.dispatch(Actions.batchEnable(false));
    232 
    233  // We are expecting an SSE request whose response will remain pending on the server,
    234  const waitForRequest = waitUntil(() =>
    235    document.querySelector(".request-list-item")
    236  );
    237  await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
    238    await content.wrappedJSObject.openConnection("sse-delay");
    239  });
    240  await waitForRequest;
    241 
    242  const requests = document.querySelectorAll(".request-list-item");
    243  is(requests.length, 1, "There should be one request");
    244 
    245  // Wait for messages to be displayed in DevTools
    246  const waitForMessages = waitForDOM(
    247    document,
    248    "#messages-view .message-list-table .message-list-item",
    249    1
    250  );
    251 
    252  // Select the request to open the side panel.
    253  EventUtils.sendMouseEvent({ type: "mousedown" }, requests[0]);
    254 
    255  // Click on the "Response" panel
    256  clickOnSidebarTab(document, "response");
    257  await waitForMessages;
    258 
    259  // Get all messages present in the "Response" panel
    260  const frames = document.querySelectorAll(
    261    "#messages-view .message-list-table .message-list-item"
    262  );
    263 
    264  // Check expected results
    265  is(frames.length, 1, "There should be one message");
    266 
    267  is(
    268    frames[0].querySelector(".message-list-payload").textContent,
    269    // Initial whitespace comes from ColumnData.
    270    " Why so serious?",
    271    "Data column shows correct payload"
    272  );
    273 
    274  const waitForMoreMessages = waitForDOM(
    275    document,
    276    "#messages-view .message-list-table .message-list-item",
    277    4
    278  );
    279  info("Send a couple of more messages");
    280  sendResponseMessages();
    281  await waitForMoreMessages;
    282 
    283  ok(
    284    !document.querySelector("#messages-view .msg-connection-closed-message"),
    285    "Connection closed message not be should not be displayed"
    286  );
    287 
    288  // Lets finish the request
    289  completeResponse();
    290 
    291  const waitForClose = waitForDOM(
    292    document,
    293    "#messages-view .msg-connection-closed-message",
    294    1
    295  );
    296 
    297  await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
    298    await content.wrappedJSObject.closeConnection();
    299  });
    300 
    301  await waitForClose;
    302 
    303  is(
    304    !!document.querySelector("#messages-view .msg-connection-closed-message"),
    305    true,
    306    "Connection closed message should be displayed"
    307  );
    308 
    309  // Wait for the connection closed event to complete
    310  await waitForTime(1000);
    311 
    312  return teardown(monitor);
    313 });