/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test basic SSE connection.
*/
function setupTestServer() {
const httpServer = createTestHTTPServer();
httpServer.registerContentType("html", "text/html");
httpServer.registerPathHandler("/index.html", function (request, response) {
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(
`
SSE Inspection Test Page
SSE Inspection Test Page
`
);
});
let sseResponse;
httpServer.registerPathHandler("/sse", function (request, response) {
response.processAsync();
response.setHeader("Content-Type", "text/event-stream");
response.write("data: Why so serious?\n\n");
response.write("data: Why so serious?\n\n");
response.write("data: Why so serious?\n\n");
response.finish();
});
httpServer.registerPathHandler("/sse-delay", function (request, response) {
response.processAsync();
response.setHeader("Content-Type", "text/event-stream");
response.write("data: Why so serious?\n\n");
sseResponse = response;
});
const sendResponseMessages = () => {
sseResponse.write("data: Another why so serious?\n\n");
sseResponse.write("data: Another why so serious?\n\n");
sseResponse.write("data: Another why so serious?\n\n");
};
const completeResponse = () => {
sseResponse.finish();
};
return { httpServer, sendResponseMessages, completeResponse };
}
add_task(async function testBasicServerSentEvents() {
const { httpServer } = setupTestServer();
const port = httpServer.identity.primaryPort;
const { tab, monitor } = await initNetMonitor(
`http://localhost:${port}/index.html`,
{ requestCount: 1 }
);
info("Starting test... ");
const { document, store, windowRequire } = monitor.panelWin;
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
const onNetworkEvents = waitForNetworkEvents(monitor, 1);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
await content.wrappedJSObject.openConnection("sse");
});
await onNetworkEvents;
const requests = document.querySelectorAll(".request-list-item");
is(requests.length, 1, "There should be one request");
const requestItem = document.querySelectorAll(".request-list-item")[0];
const type = requestItem.querySelector(".requests-list-type").textContent;
is(type, "eventsource", "Type should be rendered correctly.");
// Select the request to open the side panel.
EventUtils.sendMouseEvent({ type: "mousedown" }, requests[0]);
// Wait for messages to be displayed in DevTools
const waitForMessages = waitForDOM(
document,
"#messages-view .message-list-table .message-list-item",
3
);
// Test that 'Save Response As' is not in the context menu
EventUtils.sendMouseEvent({ type: "contextmenu" }, requests[0]);
ok(
!getContextMenuItem(monitor, "request-list-context-save-response-as"),
"The 'Save Response As' context menu item should be hidden"
);
// Close context menu.
const contextMenu = monitor.toolbox.topDoc.querySelector(
'popupset menupopup[menu-api="true"]'
);
const popupHiddenPromise = BrowserTestUtils.waitForEvent(
contextMenu,
"popuphidden"
);
contextMenu.hidePopup();
await popupHiddenPromise;
// Click on the "Response" panel
clickOnSidebarTab(document, "response");
await waitForMessages;
// Get all messages present in the "Response" panel
const frames = document.querySelectorAll(
"#messages-view .message-list-table .message-list-item"
);
// Check expected results
is(frames.length, 3, "There should be three messages");
is(
frames[0].querySelector(".message-list-payload").textContent,
// Initial whitespace comes from ColumnData.
" Why so serious?",
"Data column shows correct payload"
);
await waitForDOMIfNeeded(
document,
"#messages-view .msg-connection-closed-message",
1
);
is(
!!document.querySelector("#messages-view .msg-connection-closed-message"),
true,
"Connection closed message should be displayed"
);
is(
document.querySelector(".message-network-summary-count").textContent,
"3 messages",
"Correct message count is displayed"
);
is(
document.querySelector(".message-network-summary-total-size").textContent,
"45 B total",
"Correct total size should be displayed"
);
is(
!!document.querySelector(".message-network-summary-total-millis"),
true,
"Total time is displayed"
);
is(
document.getElementById("frame-filter-menu"),
null,
"Toolbar filter menu is hidden"
);
await waitForTick();
EventUtils.sendMouseEvent(
{ type: "contextmenu" },
document.querySelector(".message-list-headers")
);
const columns = ["data", "time", "retry", "size", "eventName", "lastEventId"];
for (const column of columns) {
is(
!!getContextMenuItem(monitor, `message-list-header-${column}-toggle`),
true,
`Context menu item "${column}" is displayed`
);
}
return teardown(monitor);
});
/**
* Test various scenarios around SSE requests,
* 1) Assert that the SSE requests messages are displayed before the connection is closed.
* 2) Assert that subsequent messages after the response panel is open are visible.
* 3) Assert that the close connection message is displayed when the connection is closed.
*/
add_task(async function testServerSentEventsDetails() {
// TODO: Should enable this test when Bug 1557795 gets fixed.
// eslint-disable-next-line no-constant-condition
if (true) {
return null;
}
const { httpServer, sendResponseMessages, completeResponse } =
setupTestServer();
const port = httpServer.identity.primaryPort;
const { tab, monitor } = await initNetMonitor(
`http://localhost:${port}/index.html`,
{
requestCount: 1,
}
);
info("Starting test... ");
const { document, store, windowRequire } = monitor.panelWin;
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
// We are expecting an SSE request whose response will remain pending on the server,
const waitForRequest = waitUntil(() =>
document.querySelector(".request-list-item")
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
await content.wrappedJSObject.openConnection("sse-delay");
});
await waitForRequest;
const requests = document.querySelectorAll(".request-list-item");
is(requests.length, 1, "There should be one request");
// Wait for messages to be displayed in DevTools
const waitForMessages = waitForDOM(
document,
"#messages-view .message-list-table .message-list-item",
1
);
// Select the request to open the side panel.
EventUtils.sendMouseEvent({ type: "mousedown" }, requests[0]);
// Click on the "Response" panel
clickOnSidebarTab(document, "response");
await waitForMessages;
// Get all messages present in the "Response" panel
const frames = document.querySelectorAll(
"#messages-view .message-list-table .message-list-item"
);
// Check expected results
is(frames.length, 1, "There should be one message");
is(
frames[0].querySelector(".message-list-payload").textContent,
// Initial whitespace comes from ColumnData.
" Why so serious?",
"Data column shows correct payload"
);
const waitForMoreMessages = waitForDOM(
document,
"#messages-view .message-list-table .message-list-item",
3
);
info("Send a couple of more messages");
sendResponseMessages();
await waitForMoreMessages;
ok(
!document.querySelector("#messages-view .msg-connection-closed-message"),
"Connection closed message not be should not be displayed"
);
// Lets finish the request
completeResponse();
const waitForClose = waitForDOM(
document,
"#messages-view .msg-connection-closed-message",
1
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
await content.wrappedJSObject.closeConnection();
});
await waitForClose;
is(
!!document.querySelector("#messages-view .msg-connection-closed-message"),
true,
"Connection closed message should be displayed"
);
// Wait for the connection closed event to complete
await waitForTime(1000);
return teardown(monitor);
});