Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write screenshot to file #292

Merged
merged 3 commits into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ jobs:
go run -mod=vendor build/make.go --install --prefix=/tmp/gauge --verbose
echo "::add-path::/tmp/gauge/bin"

- name: Install html report
run: |
git clone --depth=1 https://github.com/getgauge/html-report
cd html-report
go run build/make.go
go run build/make.go --install

- name: Install JS
run: |
npm install
Expand Down
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
102 changes: 90 additions & 12 deletions test/screenshot-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
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 path = require("path");
const sandbox = require("sinon").createSandbox();


function screenshotFunction() {
Expand All @@ -11,20 +15,94 @@ function asyncScreenshotFunction() {
}

describe("screentshot.capture", function () {
const screenshotsDir = path.join(".gauge", "screenshots");

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();
this.beforeEach( () => {
process.env.gauge_screenshots_dir = screenshotsDir;
});
afterEach(() => {
sandbox.restore();
});
describe("with default screenshot writer", () => {
it("should capture screentshot 5769768", function (done) {
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 filePath = path.join(screenshotsDir, screenShotFile);
const expectedArgs = ["gauge_screenshot", [filePath]];
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 filePath = path.join(screenshotsDir, screenShotFile);
const expectedArgs = [filePath, 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");

screenshot.capture().then(function (file) {
const filePath = path.join(screenshotsDir, screenShotFile);
const expectedArgs = [filePath, 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();
});
});
});
});