summaryrefslogtreecommitdiff
path: root/devtools/client/webconsole/net/test/mochitest/head.js
blob: 32aba2faf1b46155cde2bc103eb48e0febce9bfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
/* import-globals-from ../../../test/head.js */

"use strict";

// Load Web Console head.js, it implements helper console test API
Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/devtools/client/webconsole/test/head.js", this);

const FRAME_SCRIPT_UTILS_URL =
  "chrome://devtools/content/shared/frame-script-utils.js";

const NET_INFO_PREF = "devtools.webconsole.filter.networkinfo";
const NET_XHR_PREF = "devtools.webconsole.filter.netxhr";

// Enable XHR logging for the test
Services.prefs.setBoolPref(NET_INFO_PREF, true);
Services.prefs.setBoolPref(NET_XHR_PREF, true);

registerCleanupFunction(() => {
  Services.prefs.clearUserPref(NET_INFO_PREF, true);
  Services.prefs.clearUserPref(NET_XHR_PREF, true);
});

// Use the old webconsole since the new one doesn't yet support
// XHR spy. See Bug 1304794.
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
registerCleanupFunction(function* () {
  Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
});

/**
 * Add a new test tab in the browser and load the given url.
 * @param {String} url The url to be loaded in the new tab
 * @return a promise that resolves to the tab object when the url is loaded
 */
function addTestTab(url) {
  info("Adding a new JSON tab with URL: '" + url + "'");

  return Task.spawn(function* () {
    let tab = yield addTab(url);

    // Load devtools/shared/frame-script-utils.js
    loadCommonFrameScript(tab);

    // Open the Console panel
    let hud = yield openConsole();

    return {
      tab: tab,
      browser: tab.linkedBrowser,
      hud: hud
    };
  });
}

/**
 *
 * @param hud
 * @param options
 */
function executeAndInspectXhr(hud, options) {
  hud.jsterm.clearOutput();

  options.queryString = options.queryString || "";

  // Execute XHR in the content scope.
  performRequestsInContent({
    method: options.method,
    url: options.url + options.queryString,
    body: options.body,
    nocache: options.nocache,
    requestHeaders: options.requestHeaders
  });

  return Task.spawn(function* () {
    // Wait till the appropriate Net log appears in the Console panel.
    let rules = yield waitForMessages({
      webconsole: hud,
      messages: [{
        text: options.url,
        category: CATEGORY_NETWORK,
        severity: SEVERITY_INFO,
        isXhr: true,
      }]
    });

    // The log is here, get its parent element (className: 'message').
    let msg = [...rules[0].matched][0];
    let body = msg.querySelector(".message-body");

    // Open XHR HTTP details body and wait till the UI fetches
    // all necessary data from the backend. All RPD requests
    // needs to be finished before we can continue testing.
    yield synthesizeMouseClickSoon(hud, body);
    yield waitForBackend(msg);
    let netInfoBody = body.querySelector(".netInfoBody");
    ok(netInfoBody, "Net info body must exist");
    return netInfoBody;
  });
}

/**
 * Wait till XHR data are fetched from the backend (i.e. there are
 * no pending RDP requests.
 */
function waitForBackend(element) {
  if (!element.hasAttribute("loading")) {
    return;
  }
  return once(element, "netlog-no-pending-requests", true);
}

/**
 * Select specific tab in XHR info body.
 *
 * @param netInfoBody The main XHR info body
 * @param tabId Tab ID (possible values: 'headers', 'cookies', 'params',
 *   'post', 'response');
 *
 * @returns Tab body element.
 */
function selectNetInfoTab(hud, netInfoBody, tabId) {
  let tab = netInfoBody.querySelector(".tabs-menu-item." + tabId);
  ok(tab, "Tab must exist " + tabId);

  // Click to select specified tab and wait till its
  // UI is populated with data from the backend.
  // There must be no pending RDP requests before we can
  // continue testing the UI.
  return Task.spawn(function* () {
    yield synthesizeMouseClickSoon(hud, tab);
    let msg = getAncestorByClass(netInfoBody, "message");
    yield waitForBackend(msg);
    let tabBody = netInfoBody.querySelector("." + tabId + "TabBox");
    ok(tabBody, "Tab body must exist");
    return tabBody;
  });
}

/**
 * Return parent node with specified class.
 *
 * @param node A child element
 * @param className Specified class name.
 *
 * @returns A parent element.
 */
function getAncestorByClass(node, className) {
  for (let parent = node; parent; parent = parent.parentNode) {
    if (parent.classList && parent.classList.contains(className)) {
      return parent;
    }
  }
  return null;
}

/**
 * Synthesize asynchronous click event (with clean stack trace).
 */
function synthesizeMouseClickSoon(hud, element) {
  return new Promise((resolve) => {
    executeSoon(() => {
      EventUtils.synthesizeMouse(element, 2, 2, {}, hud.iframeWindow);
      resolve();
    });
  });
}

/**
 * Execute XHR in the content scope.
 */
function performRequestsInContent(requests) {
  info("Performing requests in the context of the content.");
  return executeInContent("devtools:test:xhr", requests);
}

function executeInContent(name, data = {}, objects = {},
  expectResponse = true) {
  let mm = gBrowser.selectedBrowser.messageManager;

  mm.sendAsyncMessage(name, data, objects);
  if (expectResponse) {
    return waitForContentMessage(name);
  }

  return Promise.resolve();
}

function waitForContentMessage(name) {
  info("Expecting message " + name + " from content");

  let mm = gBrowser.selectedBrowser.messageManager;

  return new Promise((resolve) => {
    mm.addMessageListener(name, function onMessage(msg) {
      mm.removeMessageListener(name, onMessage);
      resolve(msg.data);
    });
  });
}

function loadCommonFrameScript(tab) {
  let browser = tab ? tab.linkedBrowser : gBrowser.selectedBrowser;
  browser.messageManager.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
}