Skip to content

Commit

Permalink
Write screenshots to file. getgauge/gauge#1476
Browse files Browse the repository at this point in the history
	- Change default and custome screenshot api <screenshotFn> to
          write screenshots to file.

	- Add a new API customScreenshotWriter
  • Loading branch information
negiDharmendra committed Jan 13, 2020
1 parent e3da412 commit 6076bab
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 54 deletions.
10 changes: 4 additions & 6 deletions src/executor.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ var executeStep = function (executeStepRequest) {
screenshotPromises.then(function (screenshots) {
var errorResponse = factory.createExecutionStatusResponse(true, result.duration, result.exception, msgs, "", step.options.continueOnFailure, screenshots);
if (process.env.screenshot_on_failure !== "false") {
screenshot.capture().then(function (bytes) {
errorResponse.executionResult.screenShot = bytes;
errorResponse.executionResult.failureScreenshot = bytes;
screenshot.capture().then(function (screenshotFile) {
errorResponse.executionResult.failureScreenshotFile = screenshotFile;
deferred.reject(errorResponse);
}).catch(function(error){
logger.error("\nFailed to capture screenshot on failure.\n" + error);
Expand Down Expand Up @@ -116,9 +115,8 @@ var executeHook = function (hookLevel, currentExecutionInfo) {
var onError = function (result) {
var errorResponse = factory.createExecutionStatusResponse(true, result.duration, result.exception);
if (process.env.screenshot_on_failure !== "false") {
screenshot.capture().then(function (bytes) {
errorResponse.executionResult.screenShot = bytes;
errorResponse.executionResult.failureScreenshot = bytes;
screenshot.capture().then(function (screenshotFile) {
errorResponse.executionResult.failureScreenshotFile = screenshotFile;
deferred.reject(errorResponse);
}).catch(function(error){
logger.error("\nFailed to capture screenshot on failure.\n" + error);
Expand Down
1 change: 1 addition & 0 deletions src/gauge-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ gauge.message = function (msg) {
};

gauge.screenshotFn = null;
gauge.customScreenshotWriter = null;

gauge.step = function (stepName, options, stepFunction) {
logger.error("[DEPRECATED] gauge.step() will be removed soon, use step() instead.");
Expand Down
4 changes: 2 additions & 2 deletions src/message-processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ var processCustomMessages = function (response) {

var processScreenshots = function (response) {
var screenshotPromises = customScreenshotRegistry.get();
return screenshotPromises.then(function (screenshots) {
response.executionResult.screenshots = response.executionResult.screenshots.concat(screenshots);
return screenshotPromises.then(function (screenshotFiles) {
response.executionResult.screenshotFiles = response.executionResult.screenshotFiles.concat(screenshotFiles);
customScreenshotRegistry.clear();
});
};
Expand Down
7 changes: 3 additions & 4 deletions src/response-factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ exports.createStepValidateResponse = function (errorType, validated, suggestion)
};
};

exports.createExecutionStatusResponse = function (isFailed, executionTime, err, msg, screenShot, recoverable, screenshots) {
exports.createExecutionStatusResponse = function (isFailed, executionTime, err, msg, failureScreenshotFile, recoverable, screenshotFiles) {
return {
executionResult: {
failed: isFailed,
Expand All @@ -79,9 +79,8 @@ exports.createExecutionStatusResponse = function (isFailed, executionTime, err,
stackTrace: err && err.stack ? err.stack : "",
errorMessage: err ? (err instanceof Error ? err.toString() : JSON.stringify(err)) : "",
message: msg || [],
screenShot: screenShot || "",
failureScreenshot: screenShot || "",
screenshots: screenshots || []
failureScreenshotFile: failureScreenshotFile || "",
screenshotFiles: screenshotFiles || []
}
};

Expand Down
90 changes: 66 additions & 24 deletions src/screenshot.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,77 @@
var os = require("os"),
path = require("path"),
const path = require("path"),
fs = require("fs"),
child_process = require("child_process");
var logger= require("./logger");
var logger = require("./logger");
const SCREENSHOTS_DIR_ENV = "gauge_screenshots_dir";

var screenshot = function (tmpfile) {
tmpfile = tmpfile || path.join(os.tmpdir(), "screenshot-gauge-js-" + Date.now() + ".png");
var proc = child_process.spawnSync("gauge_screenshot", [tmpfile]);
if (proc.error) {
logger.error(proc.error.toString());
return "";
}
try {
return Buffer.from(fs.readFileSync(tmpfile)).toString("base64");
} catch (e) {
logger.info(e.toString());
return "";
}
var defaultScreenshotWriter = function () {
return new Promise((resolve) => {
const filePath = getScreenshotFileName();
var proc = child_process.spawnSync("gauge_screenshot", [filePath]);
if (proc.error) {
logger.error(proc.error.toString());
}
resolve(path.basename(filePath));
});
};

function hasCustumScreenGrabber() {
return global.gauge && global.gauge.screenshotFn && typeof global.gauge.screenshotFn === "function";
function getScreenshotFileName() {
return path.join(process.env[SCREENSHOTS_DIR_ENV], "screenshot-" + process.hrtime.bigint() + ".png");
}

function capture() {
var screenshotFn = hasCustumScreenGrabber() ? global.gauge.screenshotFn : screenshot;
var res = screenshotFn();
if (res instanceof Promise) {
return res;
function isCustumScreenshotFun(funcName) {
return global.gauge && global.gauge[funcName] && typeof global.gauge[funcName] === "function";
}

function getScreenshotFunc() {
if (isCustumScreenshotFun("customScreenshotWriter")) {
return () => {
return new Promise((resolve) => {
const screenshotFile = global.gauge.customScreenshotWriter();
if (screenshotFile.constructor.name === "Promise") {
screenshotFile.then((file) => {
resolve(path.basename(file));
});
} else {
resolve(path.basename(screenshotFile));
}
});
};
} else if (isCustumScreenshotFun("screenshotFn")) {
return () => {
return new Promise((resolve) => {
logger.error("[DEPRECATED] gauge.screenshotFn will be removed soon, use gauge.customScreenshotWriter instead.");
const res = global.gauge.screenshotFn();
const screenshotFile = getScreenshotFileName();
if (res.constructor.name == "Promise") {
res.then((data) => {
errorHandling( () => {
fs.writeFileSync(screenshotFile, data);
resolve(path.basename(screenshotFile));
});
});
} else {
errorHandling( () => {
fs.writeFileSync(screenshotFile, res);
resolve(path.basename(screenshotFile));
});
}
});
};
}
return Promise.resolve(res);
return defaultScreenshotWriter;
}

function errorHandling(action) {
try {
action();
} catch(e) {
logger.error(e);
}
}
function capture() {
var screenshotFn = getScreenshotFunc();
return screenshotFn();
}

module.exports = { capture: capture };
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
var assert = require("chai").assert;
var gauge = require("../src/gauge-global").gauge;
var sandbox = require("sinon").createSandbox();
var screenshot = require("../src/screenshot");
var customScreenshotRegistry = require("../src/custom-screenshot-registry");

describe("Custom Screenshot Registry", () => {
beforeEach(() => {
gauge.screenshotFn = function () {
return "foo";
};
global.gauge = gauge;
sandbox.stub(screenshot, "capture")
.returns(Promise.resolve("screenshot-file.png"));
});

afterEach(() => {
sandbox.restore();
customScreenshotRegistry.clear();
});

it("should add a screenshot", (done) => {
customScreenshotRegistry.add();
customScreenshotRegistry.get().then((screenshots) => {
assert.deepEqual(screenshots, ["foo"]);
assert.deepEqual(screenshots, ["screenshot-file.png"]);
done();
});
});
Expand Down
3 changes: 3 additions & 0 deletions test/executor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ describe("Executing steps", function () {
var executeStepRequest = null;
var executeStepFailingRequest = null;
this.timeout(10000);
var originalGlobalGauge;

before(function () {
var opts = { continueOnFailure: false };
stepRegistry.clear();
stepRegistry.add("Say <hi> to <me>", function () { }, "executor-test.js", 3, opts);
stepRegistry.add("failing test", function () { throw new Error("error message"); }, "executor-test.js", 6, opts);
sinon.spy(stepRegistry, "get");
originalGlobalGauge = global.gauge;
global.gauge = {
screenshotFn: function() {
return Promise.resolve("screenshot");
Expand All @@ -37,6 +39,7 @@ describe("Executing steps", function () {

after(function (done) {
stepRegistry.get.restore();
global.gauge = originalGlobalGauge;
done();
});

Expand Down
94 changes: 82 additions & 12 deletions test/screenshot-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
var expect = require("chai").expect;
var screenshot = require("../src/screenshot");
const expect = require("chai").expect;
const screenshot = require("../src/screenshot");
const child_process = require("child_process");
const fs = require("fs");
const sandbox = require("sinon").createSandbox();


function screenshotFunction() {
Expand All @@ -11,20 +14,87 @@ function asyncScreenshotFunction() {
}

describe("screentshot.capture", function () {
afterEach(() => {
sandbox.restore();
});
describe("with default screenshot writer", () => {
it("should capture screentshot 5769768", function (done) {
process.env.gauge_screenshots_dir = ".gauge/screenshots";
let screenShotFile = "screenshot-21432453.png";
const spawnSyncStub = sandbox.stub(child_process, "spawnSync").returns({});
sandbox.stub(process.hrtime, "bigint").returns(21432453);
screenshot.capture().then(function (file) {
const expectedArgs = ["gauge_screenshot", [".gauge/screenshots/" + screenShotFile]];
const actualArgs = spawnSyncStub.getCall(0).args;

expect(file).to.be.equal(screenShotFile);
expect(actualArgs).to.be.deep.equal(expectedArgs);
done();
});
});
});

describe("with custom screenshot grabber function", () => {
afterEach(function () {
global.gauge = { screenshotFn: null };
});
it("Should capture screentshot with async function", function (done) {
sandbox.stub(process.hrtime, "bigint").returns(6767787989089);
const screenShotFile = "screenshot-6767787989089.png";
global.gauge = { screenshotFn: asyncScreenshotFunction };
const writeFileSyncStub = sandbox.stub(fs, "writeFileSync");

screenshot.capture().then(function (file) {
const expectedArgs = [".gauge/screenshots/" + screenShotFile, Buffer.from("screentshot").toString("base64")];
const actualArgs = writeFileSyncStub.getCall(0).args;
expect(file).to.be.equal(screenShotFile);
expect(actualArgs).to.be.deep.equal(expectedArgs);
done();
});
});

it("Should capture screentshot with sync function", function (done) {
sandbox.stub(process.hrtime, "bigint").returns(6767787989089);
const screenShotFile = "screenshot-6767787989089.png";
global.gauge = { screenshotFn: screenshotFunction };
const writeFileSyncStub = sandbox.stub(fs, "writeFileSync");

it("Should capture screentshot and return base64 string", function (done) {
global.gauge = { screenshotFn: screenshotFunction };
screenshot.capture().then(function (bytes) {
expect(Buffer.from(bytes).toString(), "screentshot");
done();
screenshot.capture().then(function (file) {
const expectedArgs = [".gauge/screenshots/" + screenShotFile, Buffer.from("screentshot").toString("base64")];
const actualArgs = writeFileSyncStub.getCall(0).args;
expect(file).to.be.equal(screenShotFile);
expect(actualArgs).to.be.deep.equal(expectedArgs);
done();
});
});
});

it("Should capture screentshot with async custom screenshot function", function (done) {
global.gauge = { screenshotFn: asyncScreenshotFunction };
screenshot.capture().then(function (bytes) {
expect(Buffer.from(bytes).toString(), "screentshot");
done();
describe("with custom screenshot writer function", () => {
afterEach(() => {
global.gauge = { customScreenshotWriter: null };
});
it("Should capture screentshot with async function", function (done) {
const screenShotFile = "screenshot-file-1.png";
global.gauge = { customScreenshotWriter: function() {
return Promise.resolve(screenShotFile);
} };

screenshot.capture().then(function (file) {
expect(file).to.be.equal(screenShotFile);
done();
});
});

it("Should capture screentshot with sync function", function (done) {
const screenShotFile = "screenshot-file-2.png";
global.gauge = { customScreenshotWriter: function() {
return screenShotFile;
} };

screenshot.capture().then(function (file) {
expect(file).to.be.equal(screenShotFile);
done();
});
});
});
});

0 comments on commit 6076bab

Please sign in to comment.