commit 6455b94447935dcd7cfb9f88fce66feb5d1b26c9
parent 01a75a80dab2e2154f744937f5832baa9abcba43
Author: Nazım Can Altınova <canaltinova@gmail.com>
Date: Tue, 21 Oct 2025 23:59:09 +0000
Bug 1916785 - Add tests for the new webchannel request r=mstange,profiler-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D259273
Diffstat:
3 files changed, 367 insertions(+), 0 deletions(-)
diff --git a/devtools/client/performance-new/test/browser/browser.toml b/devtools/client/performance-new/test/browser/browser.toml
@@ -9,6 +9,7 @@ support-files = [
"webchannel.html",
"webchannel-favicons.html",
"webchannel-open-script-in-debugger_assets/*",
+ "webchannel-js-sources.html",
]
skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && tsan", # Bug 1804081, timeouts and data races in various tests
@@ -65,6 +66,8 @@ skip-if = [
["browser_webchannel-enable-menu-button.js"]
+["browser_webchannel-get-js-sources.js"]
+
["browser_webchannel-open-script-in-debugger.js"]
["browser_webchannel-page-favicon-data.js"]
diff --git a/devtools/client/performance-new/test/browser/browser_webchannel-get-js-sources.js b/devtools/client/performance-new/test/browser/browser_webchannel-get-js-sources.js
@@ -0,0 +1,178 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const FRONTEND_URL =
+ "https://example.com/browser/devtools/client/performance-new/test/browser/webchannel-js-sources.html";
+
+add_task(async function test_webchannel_get_js_sources() {
+ info("Test the WebChannel GET_JS_SOURCES request functionality");
+
+ // Register some mock JS sources for testing. We already test the logic that
+ // retrieves the sources in other tests. This test focuses on the webchannel
+ // itself only.
+ const mockJSSources = {
+ "uuid-12345-1": "function testFunction() { return 42; }",
+ "uuid-12345-2": "console.log('Hello from source 2');",
+ "uuid-67890-5": "function anotherFunction() { return 'test'; }",
+ "uuid-67890-6": "alert('Hello world');",
+ };
+
+ const sourcesToRequest = ["uuid-12345-1", "uuid-12345-2", "uuid-67890-5"];
+
+ const expectedResponse = [
+ { sourceText: "function testFunction() { return 42; }" },
+ { sourceText: "console.log('Hello from source 2');" },
+ { sourceText: "function anotherFunction() { return 'test'; }" },
+ ];
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:blank",
+ },
+ async browser => {
+ // Register the mock sources for the current browser
+ BackgroundJSM.registerProfileCaptureForBrowser(
+ browser,
+ Promise.resolve(new ArrayBuffer(0)), // Mock profile result
+ null, // No symbolication service
+ mockJSSources
+ );
+
+ // Now navigate to the test HTML page
+ const url =
+ FRONTEND_URL +
+ "?testType=basic" +
+ "&sourceUuids=" +
+ encodeURIComponent(JSON.stringify(sourcesToRequest)) +
+ "&expectedResponse=" +
+ encodeURIComponent(JSON.stringify(expectedResponse));
+
+ const loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.startLoadingURIString(browser, url);
+ await loaded;
+
+ await waitForTabTitle("JS sources received");
+ ok(true, "The JS sources are successfully retrieved by the WebChannel.");
+ }
+ );
+});
+
+add_task(async function test_webchannel_get_js_sources_nonexistent() {
+ info("Test GET_JS_SOURCES WebChannel request with non-existent sources");
+
+ // Register some mock JS sources
+ const mockJSSources = {
+ "uuid-12345-1": "function testFunction() { return 42; }",
+ };
+
+ // Request non-existent sources (should return null)
+ const sourcesToRequest = [
+ "uuid-12345-999", // Non-existent UUID
+ "uuid-99999-1", // Non-existent UUID
+ ];
+
+ const expectedResponse = [
+ { error: "Source not found in the browser" },
+ { error: "Source not found in the browser" },
+ ];
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:blank",
+ },
+ async browser => {
+ // Register the mock sources for the current browser
+ BackgroundJSM.registerProfileCaptureForBrowser(
+ browser,
+ Promise.resolve(new ArrayBuffer(0)),
+ null,
+ mockJSSources
+ );
+
+ // Now navigate to the test HTML page
+ const url =
+ FRONTEND_URL +
+ "?testType=nonexistent" +
+ "&sourceUuids=" +
+ encodeURIComponent(JSON.stringify(sourcesToRequest)) +
+ "&expectedResponse=" +
+ encodeURIComponent(JSON.stringify(expectedResponse));
+
+ const loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.startLoadingURIString(browser, url);
+ await loaded;
+
+ await waitForTabTitle("JS sources received");
+ ok(
+ true,
+ "Successfully verified GET_JS_SOURCES with non-existent sources"
+ );
+ }
+ );
+});
+
+add_task(async function test_webchannel_get_js_sources_no_data() {
+ info("Test GET_JS_SOURCES WebChannel request when no data is registered");
+
+ const sourcesToRequest = ["uuid-12345-1"];
+
+ // Open a new tab without registering JS sources
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:blank",
+ },
+ async browser => {
+ // Don't register any JS sources - this should cause an error
+
+ // Now navigate to the test HTML page
+ const url =
+ FRONTEND_URL +
+ "?testType=no-data" +
+ "&sourceUuids=" +
+ encodeURIComponent(JSON.stringify(sourcesToRequest)) +
+ "&expectError=true";
+
+ const loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.startLoadingURIString(browser, url);
+ await loaded;
+
+ await waitForTabTitle("JS sources received");
+ ok(true, "Successfully verified GET_JS_SOURCES error handling");
+ }
+ );
+});
+
+add_task(async function test_webchannel_get_js_sources_invalid_request() {
+ info("Test GET_JS_SOURCES WebChannel request with invalid sources parameter");
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:blank",
+ },
+ async browser => {
+ // Don't need to register JS sources for this test
+
+ // For this test, we need to pass invalid sourceUuids directly in the HTML
+ // We'll use a special URL parameter that the HTML will use directly
+ const url =
+ FRONTEND_URL +
+ "?testType=invalid-request" +
+ "&sourceUuids=invalid_not_array" + // This is intentionally not JSON
+ "&expectError=true";
+
+ const loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.startLoadingURIString(browser, url);
+ await loaded;
+
+ await waitForTabTitle("JS sources received");
+ ok(true, "Successfully verified GET_JS_SOURCES invalid request handling");
+ }
+ );
+});
diff --git a/devtools/client/performance-new/test/browser/webchannel-js-sources.html b/devtools/client/performance-new/test/browser/webchannel-js-sources.html
@@ -0,0 +1,186 @@
+<!DOCTYPE html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title></title>
+ </head>
+ <body>
+ <script>
+ "use strict";
+ // This file is used to test the retrieval of JS sources from a front-end.
+ // Rather than using some kind of complicated message passing scheme to
+ // talk to the test harness, modify the title of the page. The tests can
+ // easily read the window title to see if things worked as expected.
+
+ // The following are the titles used to communicate the page's state to the tests.
+ // Keep these in sync with any tests that read them.
+ const initialTitle = "Waiting for the JS sources";
+ const successTitle = "JS sources received";
+ const errorTitle = "Error"
+
+ document.title = initialTitle;
+
+ // A function which requests the JS sources from the browser using the GET_JS_SOURCES
+ // WebChannel message.
+ function getJSSources(sourceUuids, expectError = false) {
+ return new Promise((resolve, reject) => {
+ const requestId = Date.now();
+
+ function listener(event) {
+ window.removeEventListener(
+ "WebChannelMessageToContent",
+ listener,
+ true
+ );
+
+ const { id, message } = event.detail;
+
+ dump(JSON.stringify(message));
+ dump("\n");
+ if (id !== "profiler.firefox.com" ||
+ !message ||
+ typeof message !== "object"
+ ) {
+ console.error(message);
+ reject(new Error("A malformed WebChannel event was received."));
+ return;
+ }
+
+ if (!message.type) {
+ console.error(message);
+ reject(new Error("The WebChannel event indicates an error."));
+ return;
+ }
+
+ if (message.requestId === requestId) {
+ if (message.type === "SUCCESS_RESPONSE") {
+ resolve(message.response);
+ } else if (message.type === "ERROR_RESPONSE") {
+ if (expectError) {
+ resolve({ error: message.error });
+ } else {
+ reject(new Error(message.error));
+ }
+ } else {
+ reject(new Error("Unknown response type: " + message.type));
+ }
+ }
+ }
+
+ window.addEventListener("WebChannelMessageToContent", listener, true);
+
+ window.dispatchEvent(
+ new CustomEvent("WebChannelMessageToChrome", {
+ detail: JSON.stringify({
+ id: "profiler.firefox.com",
+ message: { type: "GET_JS_SOURCES", requestId, sourceUuids },
+ }),
+ })
+ );
+ })
+ }
+
+ async function runTest() {
+ try {
+ // Get test configuration from URL parameters
+ const params = new URLSearchParams(document.location.search);
+ const testType = params.get("testType") || "basic";
+ const expectError = params.get("expectError") === "true";
+
+ let sourcesToRequest;
+ let expectedResponse;
+
+ // Handle invalid-request test case where sourceUuids is intentionally not JSON
+ if (testType === "invalid-request") {
+ sourcesToRequest = params.get("sourceUuids"); // Keep as string for invalid test
+ expectedResponse = null;
+ } else {
+ sourcesToRequest = JSON.parse(params.get("sourceUuids") || "[]");
+ expectedResponse = JSON.parse(params.get("expectedResponse") || "null");
+ }
+
+ let response;
+ if (expectError) {
+ response = await getJSSources(sourcesToRequest, true);
+ } else {
+ response = await getJSSources(sourcesToRequest, false);
+ }
+
+ dump("response\n")
+ dump(JSON.stringify(response));
+
+ let testPassed = false;
+
+ function deepEqual(obj1, obj2) {
+ return JSON.stringify(obj1) === JSON.stringify(obj2);
+ }
+
+ switch (testType) {
+ case "basic":
+ // Test case: Request existing JS sources
+ testPassed = response &&
+ Array.isArray(response) &&
+ response.length === expectedResponse.length &&
+ response.every((item, index) => deepEqual(item, expectedResponse[index]));
+ break;
+
+ case "nonexistent":
+ // Test case: Request non-existent sources (should return error objects)
+ testPassed = response &&
+ Array.isArray(response) &&
+ response.length === expectedResponse.length &&
+ response.every((item, index) => deepEqual(item, expectedResponse[index]));
+ break;
+
+ case "no-data":
+ // Test case: No JS source data registered (should return error)
+ testPassed = response &&
+ response.error &&
+ response.error.includes("No JS source data found for this tab");
+ break;
+
+ case "invalid-request":
+ // Test case: Invalid sources parameter (should return error)
+ testPassed = response &&
+ response.error &&
+ response.error.includes("sourceUuids must be an array");
+ break;
+
+ default:
+ throw new Error("Unknown test type: " + testType);
+ }
+
+ if (testPassed) {
+ document.title = successTitle;
+ } else {
+ dump('Test failed in webchannel-js-sources.html\n');
+ dump(`Test type: ${testType}\n`);
+ dump(`Expected: ${JSON.stringify(expectedResponse)}\n`);
+ dump(`Actual: ${JSON.stringify(response)}\n`);
+
+ console.error(
+ "Test failed in webchannel-js-sources.html",
+ { testType, expectedResponse, response }
+ );
+
+ document.title = errorTitle;
+ }
+ } catch (error) {
+ // Catch any error and notify the test.
+ document.title = errorTitle;
+ dump('An error was caught in webchannel-js-sources.html\n');
+ dump(`${error}\n`);
+ console.error(
+ "An error was caught in webchannel-js-sources.html",
+ error
+ );
+ }
+ }
+
+ runTest();
+ </script>
+ </body>
+</html>