"use strict";
/**
* Steps for video recording.
*
* @mixin VideoSteps
* @prop {VideoRecorder} video - Video recorder instance.
*/
var fs = require("fs");
var path = require("path");
var _ = require("lodash");
var expect = require("chai").expect;
var resolution = require("screen-resolution");
var uuid = require("uuid/v4");
var U = require("glace-utils");
var LOG = U.logger;
require("./fixtures");
var VideoRecorder = require("./video");
var VideoSteps = {
/* modules */
__fs: fs,
__resolution: resolution,
__VideoRecorder: VideoRecorder,
startVideo: async function (opts) {
/**
* Step to start video recording. Step recall will be skipped if video
* recording wasn't stopped before.
*
* @async
* @memberOf VideoSteps
* @method startVideo
* @instance
* @arg {object} [opts] - Step options.
* @arg {string} [opts.name] - File name. Extension `.avi` will be
* added automatically. Default name will be generated with `uuid`
* algorithm.
* @arg {string} [opts.dir] - Folder to save video.
* @arg {boolean} [opts.check=true] - Flag to check that video recording
* was launched.
* @return {Promise<boolean>} `true` if step was executed, `false` if
* was skipped.
* @throws {AssertionError} If video recording wasn't launched.
*/
if (this._isVideoStarted) {
LOG.warn("Step to start video recording was passed already");
return false;
};
opts = U.defVal(opts, {});
var check = U.defVal(opts.check, true);
LOG.info("Starting video recording...");
this.video = this.video || new this.__VideoRecorder();
var videoOpts = await this._videoLocation();
videoOpts.path = this._makeVideoPath(opts);
allure.step(`Record video to ${videoOpts.path}`);
this.video.configure(videoOpts);
this.video.start();
await this.pause(1, "it needs a time to start recording");
if (check) {
expect(this.video.isRunning,
"Video recording wasn't launched")
.to.be.true;
};
this._isVideoStarted = true;
LOG.info("Video recording is started");
allure.pass();
return true;
},
stopVideo: async function (opts) {
/**
* Step to stop video recording. Step call will be skipped if video
* recording wasn't launched before.
*
* @async
* @memberOf VideoSteps
* @method stopVideo
* @instance
* @arg {object} [opts] - Step options.
* @arg {boolean} [opts.check=true] - Flag to check that video recording
* was stopped.
* @return {Promise<string>} Path to recorded video.
* @return {Promise<boolean>} `false` if step was skipped.
* @throws {AssertionError} If video recording wasn't stopped.
*/
if (!this._isVideoStarted) {
LOG.warn("Step to start video recording wasn't passed yet");
return false;
};
opts = U.defVal(opts, {});
var check = U.defVal(opts.check, true);
allure.step("Stop video recording");
LOG.info("Stopping video recording...");
await this.pause(1, "it needs a time to gather latest frames");
await this.video.stop();
if (check) {
expect(this.video.isRunning,
"Video recording was still running")
.to.be.false;
};
this._isVideoStarted = false;
LOG.info("Video recording is stopped");
allure.pass();
return this.video.filePath;
},
getVideo: function (opts) {
/**
* Step to get path to recorded video.
*
* @memberOf VideoSteps
* @method getVideo
* @instance
* @arg {object} [opts] - Step options.
* @arg {boolean} [opts.check=true] - Flag to check that video is recorded
* and path exists.
* @return {string} Path to recorded video.
*/
allure.step("Get video file");
expect(this.video,
"Video recorder isn't initialized").to.exist;
opts = U.defVal(opts, {});
var check = U.defVal(opts.check, true);
LOG.info("Getting recorded video path...");
if (check) {
expect(this.video.isRunning,
"Can't get recorded video file path, " +
"because video is still recording").to.be.false;
expect(this.video.filePath,
"Can't get recorded video file path, " +
"because it's empty").to.not.be.empty;
};
LOG.info("Recorded video path is got");
allure.pass();
return this.video.filePath;
},
removeVideo: function (opts) {
/**
* Step to remove recorded video.
*
* @memberOf VideoSteps
* @method removeVideo
* @instance
* @arg {object} [opts] - Step options.
* @arg {boolean} [opts.check=true] - Flag to check step result.
* @return {boolean} `true` if step was executed, `false` if was skipped.
* @throws {AssertionError} If video file wasn't removed.
*/
if (!this.video.filePath || !this.__fs.existsSync(this.video.filePath)) {
LOG.warn("Video file doesn't exist");
return false;
};
opts = U.defVal(opts, {});
var check = U.defVal(opts.check, true);
allure.step("Remove recorded video");
LOG.info("Removing recorded video file...");
this.__fs.unlinkSync(this.video.filePath);
if (check) {
expect(this.__fs.existsSync(this.video.filePath),
`Video file '${this.video.filePath}' wasn't removed`)
.to.be.false;
};
LOG.info("Recorded video file is removed");
allure.pass();
return true;
},
/**
* Helper to get video location.
*
* @async
* @method
* @instance
* @protected
* @return {Promise<object>} Dict of location.
*/
_videoLocation: async function () {
var loc = {};
var screen = await this.__resolution.get();
var screenLoc = { x: 0, y: 0,
width: screen.width,
height: screen.height };
if (this.webdriver && await this.webdriver.session()) {
_.assign(loc,
(await this.webdriver.windowHandlePosition()).value);
_.assign(loc,
(await this.webdriver.windowHandleSize()).value);
expect(U.isInScreen(loc, screenLoc),
"Browser is outside of screen").to.be.true;
if (loc.x < 0) loc.x = 0;
if (loc.y < 0) loc.y = 0;
if (loc.x + loc.width > screen.width) {
loc.width = screen.width - loc.x;
};
if (loc.y + loc.height > screen.height) {
loc.height = screen.height - loc.y;
};
} else {
loc = screenLoc;
};
expect(loc.width, "Invalid video width").to.be.above(0);
expect(loc.height, "Invalid video height").to.be.above(0);
return loc;
},
/**
* Helper to make video path.
*
* @method
* @instance
* @protected
* @arg {object} [opts] - Step options.
* @arg {string} [opts.name] - File name. Extension `.avi` will be
* added automatically. Default name will be generated with `uuid`
* algorithm.
* @arg {string} [opts.dir] - Folder to save video.
* @return {string} Full video path.
*/
_makeVideoPath: function (opts) {
opts = U.defVal(opts, {});
var fileName = U.toKebab(U.defVal(opts.name, uuid()));
if (!fileName.endsWith(".avi")) fileName += ".avi";
return U.mkpath(
U.defVal(opts.dir,
path.resolve(CONF.report.testDir || CONF.report.dir, "videos")),
fileName);
},
};
module.exports = VideoSteps;