From 6076babe7459e47281a8f883369659eec6254cd7 Mon Sep 17 00:00:00 2001 From: Dharmendra Singh Date: Fri, 10 Jan 2020 20:18:35 +0530 Subject: [PATCH 1/3] Write screenshots to file. getgauge/gauge#1476 - Change default and custome screenshot api to write screenshots to file. - Add a new API customScreenshotWriter --- src/executor.js | 10 +- src/gauge-global.js | 1 + src/message-processor.js | 4 +- src/response-factory.js | 7 +- src/screenshot.js | 90 +++++++++++++----- ....js => custom-screenshot-registry-test.js} | 12 +-- test/executor-test.js | 3 + test/screenshot-test.js | 94 ++++++++++++++++--- 8 files changed, 167 insertions(+), 54 deletions(-) rename test/{custom-screenshot-registry.js => custom-screenshot-registry-test.js} (71%) diff --git a/src/executor.js b/src/executor.js index 9b053538..2d575081 100644 --- a/src/executor.js +++ b/src/executor.js @@ -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); @@ -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); diff --git a/src/gauge-global.js b/src/gauge-global.js index f030e95e..6da32cff 100644 --- a/src/gauge-global.js +++ b/src/gauge-global.js @@ -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."); diff --git a/src/message-processor.js b/src/message-processor.js index 1b75f59f..cbde98f9 100644 --- a/src/message-processor.js +++ b/src/message-processor.js @@ -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(); }); }; diff --git a/src/response-factory.js b/src/response-factory.js index f981debd..e060c139 100644 --- a/src/response-factory.js +++ b/src/response-factory.js @@ -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, @@ -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 || [] } }; diff --git a/src/screenshot.js b/src/screenshot.js index 2f19cd4f..b3a9f797 100644 --- a/src/screenshot.js +++ b/src/screenshot.js @@ -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 }; diff --git a/test/custom-screenshot-registry.js b/test/custom-screenshot-registry-test.js similarity index 71% rename from test/custom-screenshot-registry.js rename to test/custom-screenshot-registry-test.js index 9805485d..670c6cc2 100644 --- a/test/custom-screenshot-registry.js +++ b/test/custom-screenshot-registry-test.js @@ -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(); }); }); diff --git a/test/executor-test.js b/test/executor-test.js index 89904870..b2d26398 100644 --- a/test/executor-test.js +++ b/test/executor-test.js @@ -8,6 +8,7 @@ describe("Executing steps", function () { var executeStepRequest = null; var executeStepFailingRequest = null; this.timeout(10000); + var originalGlobalGauge; before(function () { var opts = { continueOnFailure: false }; @@ -15,6 +16,7 @@ describe("Executing steps", function () { stepRegistry.add("Say to ", 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"); @@ -37,6 +39,7 @@ describe("Executing steps", function () { after(function (done) { stepRegistry.get.restore(); + global.gauge = originalGlobalGauge; done(); }); diff --git a/test/screenshot-test.js b/test/screenshot-test.js index 2499b2f3..abbdb07c 100644 --- a/test/screenshot-test.js +++ b/test/screenshot-test.js @@ -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() { @@ -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(); + }); }); }); }); From e6cb9557e21a7eee37c3c3c59f8f16f69915d132 Mon Sep 17 00:00:00 2001 From: Dharmendra Singh Date: Mon, 13 Jan 2020 13:57:07 +0530 Subject: [PATCH 2/3] Fixed screenshot-test on windows getgauge/gauge#1476 --- test/screenshot-test.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/screenshot-test.js b/test/screenshot-test.js index abbdb07c..48af1cb6 100644 --- a/test/screenshot-test.js +++ b/test/screenshot-test.js @@ -2,6 +2,7 @@ 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(); @@ -14,17 +15,22 @@ function asyncScreenshotFunction() { } describe("screentshot.capture", function () { + const screenshotsDir = path.join(".gauge", "screenshots"); + + this.beforeEach( () => { + process.env.gauge_screenshots_dir = screenshotsDir; + }); 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 filePath = path.join(screenshotsDir, screenShotFile); + const expectedArgs = ["gauge_screenshot", [filePath]]; const actualArgs = spawnSyncStub.getCall(0).args; expect(file).to.be.equal(screenShotFile); @@ -45,7 +51,8 @@ describe("screentshot.capture", function () { const writeFileSyncStub = sandbox.stub(fs, "writeFileSync"); screenshot.capture().then(function (file) { - const expectedArgs = [".gauge/screenshots/" + screenShotFile, Buffer.from("screentshot").toString("base64")]; + 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); @@ -60,7 +67,8 @@ describe("screentshot.capture", function () { const writeFileSyncStub = sandbox.stub(fs, "writeFileSync"); screenshot.capture().then(function (file) { - const expectedArgs = [".gauge/screenshots/" + screenShotFile, Buffer.from("screentshot").toString("base64")]; + 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); From 43b2e529732c08d48e1ed10281dee772a75e5884 Mon Sep 17 00:00:00 2001 From: Dharmendra Singh Date: Mon, 13 Jan 2020 14:05:56 +0530 Subject: [PATCH 3/3] Install html-report from source --- .github/workflows/build.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3cc798f9..a3466c61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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