Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / devtools / client / netmonitor / test / browser_net_copy_as_curl.js
blobdf3abd1305cdb1019952b798d7bc2bae22701591
1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
4 "use strict";
6 /**
7 * Tests if Copy as cURL works.
8 */
10 const POST_PAYLOAD = "Plaintext value as a payload";
12 add_task(async function () {
13 const { tab, monitor } = await initNetMonitor(HTTPS_CURL_URL, {
14 requestCount: 1,
15 });
16 // disable sending idempotency header for POST request
17 Services.prefs.setBoolPref("network.http.idempotencyKey.enabled", false);
18 info("Starting test... ");
20 // Different quote chars are used for Windows and POSIX
21 const QUOTE_WIN = '"';
22 const QUOTE_POSIX = "'";
24 const isWin = Services.appinfo.OS === "WINNT";
25 const testData = isWin
26 ? [
28 menuItemId: "request-list-context-copy-as-curl-win",
29 data: buildTestData(QUOTE_WIN),
32 menuItemId: "request-list-context-copy-as-curl-posix",
33 data: buildTestData(QUOTE_POSIX),
36 : [
38 menuItemId: "request-list-context-copy-as-curl",
39 data: buildTestData(QUOTE_POSIX),
43 await testForPlatform(tab, monitor, testData);
45 await teardown(monitor);
46 });
48 function buildTestData(QUOTE) {
49 // Quote a string, escape the quotes inside the string
50 function quote(str) {
51 return QUOTE + str.replace(new RegExp(QUOTE, "g"), `\\${QUOTE}`) + QUOTE;
54 // Header param is formatted as -H "Header: value" or -H 'Header: value'
55 function header(h) {
56 return "-H " + quote(h);
59 // Construct the expected command
60 const SIMPLE_BASE = ["curl " + quote(HTTPS_SIMPLE_SJS)];
61 const SLOW_BASE = ["curl " + quote(HTTPS_SLOW_SJS)];
62 const BASE_RESULT = [
63 "--compressed",
64 header("User-Agent: " + navigator.userAgent),
65 header("Accept: */*"),
66 header("Accept-Language: " + navigator.language),
67 header("X-Custom-Header-1: Custom value"),
68 header("X-Custom-Header-2: 8.8.8.8"),
69 header("X-Custom-Header-3: Mon, 3 Mar 2014 11:11:11 GMT"),
70 header("Referer: " + HTTPS_CURL_URL),
71 header("Connection: keep-alive"),
72 header("Pragma: no-cache"),
73 header("Cache-Control: no-cache"),
74 header("Sec-Fetch-Dest: empty"),
75 header("Sec-Fetch-Mode: cors"),
76 header("Sec-Fetch-Site: same-origin"),
79 const COOKIE_PARTIAL_RESULT = [header("Cookie: bob=true; tom=cool")];
81 const POST_PARTIAL_RESULT = [
82 "-X",
83 "POST",
84 "--data-raw " + quote(POST_PAYLOAD),
85 header("Content-Type: text/plain;charset=UTF-8"),
87 const ORIGIN_RESULT = [header("Origin: https://example.com")];
89 const HEAD_PARTIAL_RESULT = ["-I"];
91 return {
92 SIMPLE_BASE,
93 SLOW_BASE,
94 BASE_RESULT,
95 COOKIE_PARTIAL_RESULT,
96 POST_PAYLOAD,
97 POST_PARTIAL_RESULT,
98 ORIGIN_RESULT,
99 HEAD_PARTIAL_RESULT,
103 async function testForPlatform(tab, monitor, testData) {
104 // GET request, no cookies (first request)
105 await performRequest("GET");
106 for (const test of testData) {
107 await testClipboardContent(test.menuItemId, [
108 ...test.data.SIMPLE_BASE,
109 ...test.data.BASE_RESULT,
112 // Check to make sure it is still OK after we view the response (bug#1452442)
113 await selectIndexAndWaitForSourceEditor(monitor, 0);
114 for (const test of testData) {
115 await testClipboardContent(test.menuItemId, [
116 ...test.data.SIMPLE_BASE,
117 ...test.data.BASE_RESULT,
121 // GET request, cookies set by previous response
122 await performRequest("GET");
123 for (const test of testData) {
124 await testClipboardContent(test.menuItemId, [
125 ...test.data.SIMPLE_BASE,
126 ...test.data.BASE_RESULT,
127 ...test.data.COOKIE_PARTIAL_RESULT,
131 // Unfinished request (bug#1378464, bug#1420513)
132 const waitSlow = waitForNetworkEvents(monitor, 0);
133 await SpecialPowers.spawn(
134 tab.linkedBrowser,
135 [HTTPS_SLOW_SJS],
136 async function (url) {
137 content.wrappedJSObject.performRequest(url, "GET", null);
140 await waitSlow;
141 for (const test of testData) {
142 await testClipboardContent(test.menuItemId, [
143 ...test.data.SLOW_BASE,
144 ...test.data.BASE_RESULT,
145 ...test.data.COOKIE_PARTIAL_RESULT,
149 // POST request
150 await performRequest("POST", POST_PAYLOAD);
151 for (const test of testData) {
152 await testClipboardContent(test.menuItemId, [
153 ...test.data.SIMPLE_BASE,
154 ...test.data.BASE_RESULT,
155 ...test.data.COOKIE_PARTIAL_RESULT,
156 ...test.data.POST_PARTIAL_RESULT,
157 ...test.data.ORIGIN_RESULT,
161 // HEAD request
162 await performRequest("HEAD");
163 for (const test of testData) {
164 await testClipboardContent(test.menuItemId, [
165 ...test.data.SIMPLE_BASE,
166 ...test.data.BASE_RESULT,
167 ...test.data.COOKIE_PARTIAL_RESULT,
168 ...test.data.HEAD_PARTIAL_RESULT,
172 async function performRequest(method, payload) {
173 const waitRequest = waitForNetworkEvents(monitor, 1);
174 await SpecialPowers.spawn(
175 tab.linkedBrowser,
178 url: HTTPS_SIMPLE_SJS,
179 method_: method,
180 payload_: payload,
183 async function ({ url, method_, payload_ }) {
184 content.wrappedJSObject.performRequest(url, method_, payload_);
187 await waitRequest;
190 async function testClipboardContent(menuItemId, expectedResult) {
191 const { document } = monitor.panelWin;
193 const items = document.querySelectorAll(".request-list-item");
194 const itemIndex = items.length - 1;
195 EventUtils.sendMouseEvent({ type: "mousedown" }, items[itemIndex]);
196 EventUtils.sendMouseEvent(
197 { type: "contextmenu" },
198 document.querySelectorAll(".request-list-item")[0]
201 /* Ensure that the copy as cURL option is always visible */
203 !!getContextMenuItem(monitor, menuItemId),
204 true,
205 `The "Copy as cURL" context menu item "${menuItemId}" should not be hidden.`
208 await waitForClipboardPromise(
209 async function setup() {
210 await selectContextMenuItem(monitor, menuItemId);
212 function validate(result) {
213 if (typeof result !== "string") {
214 return false;
217 // Different setups may produce the same command, but with the
218 // parameters in a different order in the commandline (which is fine).
219 // Here we confirm that the commands are the same even in that case.
221 // This monster regexp parses the command line into an array of arguments,
222 // recognizing quoted args with matching quotes and escaped quotes inside:
223 // [ "curl 'url'", "--standalone-arg", "-arg-with-quoted-string 'value\'s'" ]
224 const matchRe = /[-A-Za-z1-9]+(?: ([\"'])(?:\\\1|.)*?\1)?/g;
226 const actual = result.match(matchRe);
227 // Must begin with the same "curl 'URL'" segment
228 if (!actual || expectedResult[0] != actual[0]) {
229 return false;
232 // Must match each of the params in the middle (headers)
233 return (
234 expectedResult.length === actual.length &&
235 expectedResult.some(param => actual.includes(param))
240 info(
241 `Clipboard contains a cURL command for item ${itemIndex} by "${menuItemId}"`