Bug 1464461 - add back mochitests for screenshot command; r=ochameau, nchevobbe draft
authoryulia <ystartsev@mozilla.com>
Fri, 08 Jun 2018 20:14:36 +0200
changeset 806349 68bc4920c52b
parent 806348 af3e2f8f8d28
child 806350 4e3f11464569
push id112872
push userbmo:ystartsev@mozilla.com
push dateSat, 09 Jun 2018 11:25:30 +0000
reviewersochameau, nchevobbe
bugs1464461
milestone62.0a1
Bug 1464461 - add back mochitests for screenshot command; r=ochameau, nchevobbe MozReview-Commit-ID: 4yGM74AXwoP
devtools/client/webconsole/test/mochitest/browser.ini
devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command.html
devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command.js
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -147,16 +147,17 @@ support-files =
   test-subresource-security-error.html
   test-subresource-security-error.js
   test-subresource-security-error.js^headers^
   test-time-methods.html
   test-trackingprotection-securityerrors.html
   test-webconsole-error-observer.html
   test-websocket.html
   test-websocket.js
+  browser_jsterm_screenshot_command.html
   testscript.js
   !/devtools/client/netmonitor/test/sjs_cors-test-server.sjs
   !/image/test/mochitest/blue.png
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
@@ -219,16 +220,17 @@ skip-if = os != 'mac' # The tested ctrl+
 [browser_jsterm_instance_of.js]
 [browser_jsterm_multiline.js]
 [browser_jsterm_no_autocompletion_on_defined_variables.js]
 [browser_jsterm_no_input_and_tab_key_pressed.js]
 [browser_jsterm_no_input_change_and_tab_key_pressed.js]
 [browser_jsterm_null_undefined.js]
 [browser_jsterm_popup_close_on_tab_switch.js]
 [browser_jsterm_popup.js]
+[browser_jsterm_screenshot_command.js]
 [browser_jsterm_selfxss.js]
 subsuite = clipboard
 [browser_webconsole_allow_mixedcontent_securityerrors.js]
 tags = mcb
 [browser_webconsole_batching.js]
 [browser_webconsole_block_mixedcontent_securityerrors.js]
 tags = mcb
 [browser_webconsole_cached_messages.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command.html
@@ -0,0 +1,18 @@
+<html>
+  <head>
+    <style>
+      img {
+        height: 100px;
+        width: 100px;
+      }
+      .overflow {
+        overflow: scroll;
+        height: 200%;
+        width: 200%;
+      }
+    </style>
+  </head>
+  <body>
+    <img id="testImage" ></img>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command.js
@@ -0,0 +1,219 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* global helpers, btoa, whenDelayedStartupFinished, OpenBrowserWindow */
+
+// Test that screenshot command works properly
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+                 "test/mochitest/browser_cmd_screenshot.html";
+
+const FileUtils = (ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {})).FileUtils;
+const browser = "?";
+
+add_task(async function() {
+  await addTab(TEST_URI);
+
+  const opened = waitForBrowserConsole();
+
+  let hud = HUDService.getBrowserConsole();
+  ok(!hud, "browser console is not open");
+  info("wait for the browser console to open with ctrl-shift-j");
+  EventUtils.synthesizeKey("j", { accelKey: true, shiftKey: true }, window);
+
+  hud = await opened;
+  ok(hud, "browser console opened");
+
+  await testFile(hud);
+  await testClipboard(hud);
+  await testFullpageClipboard(hud);
+  await testSelectorClipboard(hud);
+
+  const scrollbarSize = await createScrollbarOverflow();
+  await testClipboardScrollbar(hud, scrollbarSize);
+  await testFullpageClipboardScrollbar(hud, scrollbarSize);
+});
+
+async function testFile(hud) {
+  // Test capture to file
+  const file = FileUtils.getFile("TmpD", [ "TestScreenshotFile.png" ]);
+  const command = `:screenshot ${file.path}`;
+  hud.jsterm.execute(command);
+  await waitForMessage("Saved to TmpD/TestScreenshotFile.png");
+  // Bug 849168: screenshot command tests fail in try but not locally
+  ok(file.exists(), "Screenshot file exists");
+
+  if (file.exists()) {
+    file.remove(false);
+  }
+}
+
+async function testClipboard(hud) {
+  const command = `:screenshot --clipboard`;
+  hud.jsterm.execute(command);
+  await waitForMessage("Copied to clipboard.");
+  const imgSize1 = await getImageSizeFromClipboard();
+  await ContentTask.spawn(browser, imgSize1, function* (imgSize) {
+    Assert.equal(imgSize.width, content.innerWidth,
+                 "Image width matches window size");
+    Assert.equal(imgSize.height, content.innerHeight,
+                 "Image height matches window size");
+  });
+}
+
+async function testFullpageClipboard(hud) {
+  const command = `:screenshot --fullpage --clipboard`;
+  hud.jsterm.execute(command);
+  await waitForMessage("Copied to clipboard.");
+  const imgSize1 = await getImageSizeFromClipboard();
+  await ContentTask.spawn(browser, imgSize1, function* (imgSize) {
+    Assert.equal(imgSize.width,
+      content.innerWidth + content.scrollMaxX - content.scrollMinX,
+      "Image width matches page size");
+    Assert.equal(imgSize.height,
+      content.innerHeight + content.scrollMaxY - content.scrollMinY,
+      "Image height matches page size");
+  });
+}
+
+async function testSelectorClipboard(hud) {
+  const command = `:screenshot --selector img#testImage --clipboard`;
+  hud.jsterm.execute(command);
+  await waitForMessage("Copied to clipboard.");
+  const imgSize1 = await getImageSizeFromClipboard();
+  await ContentTask.spawn(browser, imgSize1, function* (imgSize) {
+    const img = content.document.querySelector("img#testImage");
+    Assert.equal(imgSize.width, img.clientWidth,
+       "Image width matches element size");
+    Assert.equal(imgSize.height, img.clientHeight,
+       "Image height matches element size");
+  });
+}
+
+async function createScrollbarOverflow() {
+  // Trigger scrollbars by forcing document to overflow
+  // This only affects results on OSes with scrollbars that reduce document size
+  // (non-floating scrollbars).  With default OS settings, this means Windows
+  // and Linux are affected, but Mac is not.  For Mac to exhibit this behavior,
+  // change System Preferences -> General -> Show scroll bars to Always.
+  await ContentTask.spawn(browser, {}, function* () {
+    content.document.body.classList.add("overflow");
+  });
+
+  const scrollbarSize = await ContentTask.spawn(browser, {}, function* () {
+    const winUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+    const scrollbarHeight = {};
+    const scrollbarWidth = {};
+    winUtils.getScrollbarSize(true, scrollbarWidth, scrollbarHeight);
+    return {
+      width: scrollbarWidth.value,
+      height: scrollbarHeight.value,
+    };
+  });
+
+  info(`Scrollbar size: ${scrollbarSize.width}x${scrollbarSize.height}`);
+  return scrollbarSize;
+}
+
+async function testClipboardScrollbar(hud, scrollbarSize) {
+  const command = `:screenshot --clipboard`;
+  hud.jsterm.execute(command);
+  await waitForMessage("Copied to clipboard.");
+  const imgSize1 = await getImageSizeFromClipboard();
+  imgSize1.scrollbarWidth = scrollbarSize.width;
+  imgSize1.scrollbarHeight = scrollbarSize.height;
+  await ContentTask.spawn(browser, imgSize1, function* (imgSize) {
+    Assert.equal(imgSize.width, content.innerWidth - imgSize.scrollbarWidth,
+       "Image width matches window size minus scrollbar size");
+    Assert.equal(imgSize.height, content.innerHeight - imgSize.scrollbarHeight,
+       "Image height matches window size minus scrollbar size");
+  });
+}
+
+async function testFullpageClipboardScrollbar(hud, scrollbarSize) {
+  const command = `:screenshot --fullpage --clipboard`;
+  hud.jsterm.execute(command);
+  await waitForMessage("Copied to clipboard.");
+  const imgSize1 = await getImageSizeFromClipboard();
+  imgSize1.scrollbarWidth = scrollbarSize.width;
+  imgSize1.scrollbarHeight = scrollbarSize.height;
+  await ContentTask.spawn(browser, imgSize1, function* (imgSize) {
+    Assert.equal(imgSize.width,
+      (content.innerWidth + content.scrollMaxX -
+       content.scrollMinX) - imgSize.scrollbarWidth,
+      "Image width matches page size minus scrollbar size");
+    Assert.equal(imgSize.height,
+      (content.innerHeight + content.scrollMaxY -
+       content.scrollMinY) - imgSize.scrollbarHeight,
+      "Image height matches page size minus scrollbar size");
+  });
+}
+
+async function getImageSizeFromClipboard() {
+  const clipid = Ci.nsIClipboard;
+  const clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
+  const trans = Cc["@mozilla.org/widget/transferable;1"]
+                .createInstance(Ci.nsITransferable);
+  const flavor = "image/png";
+  trans.init(null);
+  trans.addDataFlavor(flavor);
+
+  clip.getData(trans, clipid.kGlobalClipboard);
+  const data = new Object();
+  const dataLength = new Object();
+  trans.getTransferData(flavor, data, dataLength);
+
+  ok(data.value, "screenshot exists");
+  ok(dataLength.value > 0, "screenshot has length");
+
+  let image = data.value;
+  let dataURI = `data:${flavor};base64,`;
+
+  // Due to the differences in how images could be stored in the clipboard the
+  // checks below are needed. The clipboard could already provide the image as
+  // byte streams, but also as pointer, or as image container. If it's not
+  // possible obtain a byte stream, the function returns `null`.
+  if (image instanceof Ci.nsISupportsInterfacePointer) {
+    image = image.data;
+  }
+
+  if (image instanceof Ci.imgIContainer) {
+    image = Cc["@mozilla.org/image/tools;1"]
+              .getService(Ci.imgITools)
+              .encodeImage(image, flavor);
+  }
+
+  if (image instanceof Ci.nsIInputStream) {
+    const binaryStream = Cc["@mozilla.org/binaryinputstream;1"]
+                         .createInstance(Ci.nsIBinaryInputStream);
+    binaryStream.setInputStream(image);
+    const rawData = binaryStream.readBytes(binaryStream.available());
+    const charCodes = Array.from(rawData, c => c.charCodeAt(0) & 0xff);
+    let encodedData = String.fromCharCode(...charCodes);
+    encodedData = btoa(encodedData);
+    dataURI = dataURI + encodedData;
+  } else {
+    throw new Error("Unable to read image data");
+  }
+
+  const img = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
+
+  const loaded = new Promise(resolve => {
+    img.addEventListener("load", function() {
+      resolve();
+    }, {once: true});
+  });
+
+  img.src = dataURI;
+  document.documentElement.appendChild(img);
+  await loaded;
+  img.remove();
+
+  return {
+    width: img.width,
+    height: img.height,
+  };
+}