Skip to content

Commit

Permalink
Screenshot file patterns (closes DevExpress#1602, DevExpress#1651, De…
Browse files Browse the repository at this point in the history
  • Loading branch information
thireven committed Jan 30, 2018
1 parent ea29623 commit 7e1e919
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 23 deletions.
7 changes: 7 additions & 0 deletions src/cli/argument-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export default class CLIArgumentParser {
.option('-b, --list-browsers [provider]', 'output the aliases for local browsers or browsers available through the specified browser provider')
.option('-r, --reporter <name[:outputFile][,...]>', 'specify the reporters and optionally files where reports are saved')
.option('-s, --screenshots <path>', 'enable screenshot capturing and specify the path to save the screenshots to')
.option('-p, --pattern <pattern>', 'screenshot files are named using specific patterns: %BROWSER%, %FIXTURE_NAME%, %TEST_NAME%, %NUMBERED%, %FILENAME%')
.option('-S, --screenshots-on-fails', 'take a screenshot whenever a test fails')
.option('-q, --quarantine-mode', 'enable the quarantine mode')
.option('-d, --debug-mode', 'execute test steps one by one pausing the test after each step')
Expand Down Expand Up @@ -186,6 +187,11 @@ export default class CLIArgumentParser {
this.opts.speed = parseFloat(this.opts.speed);
}

_parseScreenshotsPattern () {
if (!this.opts.pattern)
this.opts.pattern = '%DATE%_%TIME%/test-%TESTNUMBER%/%USERAGENT%/%TESTNUMBER%';
}

_parseConcurrency () {
if (this.opts.concurrency)
this.concurrency = parseInt(this.opts.concurrency, 10);
Expand Down Expand Up @@ -311,6 +317,7 @@ export default class CLIArgumentParser {
this._parsePorts();
this._parseBrowserList();
this._parseConcurrency();
this._parseScreenshotsPattern();

await Promise.all([
this._parseScreenshotsPath(),
Expand Down
2 changes: 1 addition & 1 deletion src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ async function runTests (argParser) {
.browsers(browsers)
.concurrency(concurrency)
.filter(argParser.filter)
.screenshots(opts.screenshots, opts.screenshotsOnFails)
.screenshots(opts.screenshots, opts.screenshotsOnFails, opts.pattern)
.startApp(opts.app, opts.appInitDelay);

runner.once('done-bootstrapping', () => log.hideSpinner());
Expand Down
4 changes: 3 additions & 1 deletion src/runner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default class Runner extends EventEmitter {
externalProxyHost: null,
screenshotPath: null,
takeScreenshotsOnFails: false,
screenshotsPattern: null,
skipJsErrors: false,
quarantineMode: false,
debugMode: false,
Expand Down Expand Up @@ -171,9 +172,10 @@ export default class Runner extends EventEmitter {
return this;
}

screenshots (path, takeOnFails = false) {
screenshots (path, takeOnFails = false, pattern) {
this.opts.takeScreenshotsOnFails = takeOnFails;
this.opts.screenshotPath = path;
this.opts.screenshotsPattern = pattern;

return this;
}
Expand Down
2 changes: 1 addition & 1 deletion src/runner/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class Task extends EventEmitter {
this.running = false;
this.browserConnectionGroups = browserConnectionGroups;
this.tests = tests;
this.screenshots = new Screenshots(opts.screenshotPath);
this.screenshots = new Screenshots(opts.screenshotPath, opts.screenshotsPattern);
this.warningLog = new WarningLog();

this.fixtureHookController = new FixtureHookController(tests, browserConnectionGroups.length);
Expand Down
36 changes: 27 additions & 9 deletions src/screenshots/capturer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ export default class Capturer {
this.testEntry = testEntry;
this.provider = connection.provider;
this.browserId = connection.id;
this.baseDirName = namingOptions.baseDirName;
this.userAgentName = namingOptions.userAgentName;
this.quarantineAttemptNum = namingOptions.quarantineAttemptNum;
this.testIndex = namingOptions.testIndex;
this.screenshotIndex = 1;
this.errorScreenshotIndex = 1;

var testDirName = `test-${this.testIndex}`;
var screenshotsPath = this.enabled ? joinPath(this.baseScreenshotsPath, this.baseDirName, testDirName) : '';
var screenshotsPath = this.enabled ? this.baseScreenshotsPath : '';

this.screenshotsPath = screenshotsPath;
this.screenshotPathForReport = screenshotsPath;
this.screenshotsPatternName = namingOptions.patternName;

this.patternMap = namingOptions.patternMap;
}

static _correctFilePath (path) {
Expand All @@ -38,8 +38,20 @@ export default class Capturer {
return PNG_EXTENSION_RE.test(correctedPath) ? correctedPath : `${correctedPath}.png`;
}

_parseFileNumber (fileName) {
if (fileName.indexOf('%FILENUMBER%') !== -1)
return fileName.replace(new RegExp('%FILENUMBER%', 'g'), (this.screenshotIndex - 1).toString().padStart(3, 0));

return fileName;
}

_getFileName (forError) {
var fileName = `${forError ? this.errorScreenshotIndex : this.screenshotIndex}.png`;
let fileName = '';

if (this.screenshotsPatternName)
fileName = `${this.screenshotsPatternName}.png`;
else
fileName = `${forError ? this.errorScreenshotIndex : this.screenshotIndex}.png`;

if (forError)
this.errorScreenshotIndex++;
Expand All @@ -49,15 +61,22 @@ export default class Capturer {
return fileName;
}

_parsePattern (namePattern) {
for (const pattern in this.patternMap)
namePattern = namePattern.replace(new RegExp(`%${pattern}%`, 'g'), this.patternMap[pattern]);

return namePattern;
}

_getSreenshotPath (fileName, customPath) {
if (customPath)
return joinPath(this.baseScreenshotsPath, Capturer._correctFilePath(customPath));
return joinPath(this.baseScreenshotsPath, Capturer._correctFilePath(this._parsePattern(customPath)));

var screenshotPath = this.quarantineAttemptNum !== null ?
joinPath(this.screenshotsPath, `run-${this.quarantineAttemptNum}`) :
this.screenshotsPath;

return joinPath(screenshotPath, this.userAgentName, fileName);
return joinPath(screenshotPath, fileName);
}

async _takeScreenshot (filePath, pageWidth, pageHeight) {
Expand All @@ -69,14 +88,13 @@ export default class Capturer {
if (!this.enabled)
return null;

var fileName = this._getFileName(forError);
var fileName = this._parseFileNumber(this._getFileName(forError));

fileName = forError ? joinPath('errors', fileName) : fileName;

var screenshotPath = this._getSreenshotPath(fileName, customScreenshotPath);

await this._takeScreenshot(screenshotPath, pageWidth, pageHeight);

await generateThumbnail(screenshotPath);

// NOTE: if test contains takeScreenshot action with custom path
Expand Down
74 changes: 63 additions & 11 deletions src/screenshots/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,29 @@ import moment from 'moment';
import Capturer from './capturer';

export default class Screenshots {
constructor (path) {
constructor (path, pattern) {
const now = moment(Date.now());

this.enabled = !!path;
this.screenshotsPath = path;
this.screenshotsPattern = pattern;
this.testEntries = [];
this.screenshotBaseDirName = Screenshots._getScreenshotBaseDirName();
this.userAgentNames = [];
}

static _getScreenshotBaseDirName () {
var now = Date.now();

return moment(now).format('YYYY-MM-DD_hh-mm-ss');
this.currentDate = now.format('YYYY-MM-DD');
this.currentTime = now.format('HH-mm-ss');
// FILENUMBER is handled in the Capturer class
this.patternMap = {
FIXTURE: null,
TEST: null,
TESTNUMBER: null,
DATE: this.currentDate,
TIME: this.currentTime,
USERAGENT: null,
BROWSER: null,
BROWSERVERSION: null,
OS: null,
OSVERSION: null
};
}

static _escapeUserAgent (userAgent) {
Expand Down Expand Up @@ -46,7 +57,19 @@ export default class Screenshots {
}

this.userAgentNames.push({ name: userAgentName, index: 0, testIndex, quarantineAttemptNum });
return userAgentName;

const userAgentMatches = userAgentName.match(new RegExp('^(.+?)_([0-9.]+)_(.+?)_([0-9.]+)$'));

userAgentMatches.shift();
const [browser, browserVersion, os, osVersion] = userAgentMatches;

return {
full: userAgentName,
browser,
browserVersion,
os,
osVersion
};
}

_addTestEntry (test) {
Expand All @@ -73,17 +96,46 @@ export default class Screenshots {
return this._getTestEntry(test).path;
}

_parsePattern (namePattern, options) {
if (!namePattern) return '';

this.patternMap = Object.assign(this.patternMap, {
FIXTURE: options.fixture.replace(' ', '-'),
TEST: options.test.replace(' ', '-'),
TESTNUMBER: options.testNumber,
DATE: this.currentDate,
TIME: this.currentTime,
USERAGENT: options.userAgent.full,
BROWSER: options.userAgent.browser,
BROWSERVERSION: options.userAgent.browserVersion,
OS: options.userAgent.os,
OSVERSION: options.userAgent.osVersion
});

for (const pattern in this.patternMap)
namePattern = namePattern.replace(new RegExp(`%${pattern}%`, 'g'), this.patternMap[pattern]);

return namePattern;
}

createCapturerFor (test, testIndex, quarantineAttemptNum, connection) {
var testEntry = this._getTestEntry(test);

if (!testEntry)
testEntry = this._addTestEntry(test);

const patternOptions = {
testNumber: testIndex,
fixture: test.fixture.name,
test: test.name,
userAgent: this._getUserAgentName(connection.userAgent, testIndex, quarantineAttemptNum)
};

var namingOptions = {
testIndex,
quarantineAttemptNum,
baseDirName: this.screenshotBaseDirName,
userAgentName: this._getUserAgentName(connection.userAgent, testIndex, quarantineAttemptNum)
patternMap: this.patternMap,
patternName: this._parsePattern(this.screenshotsPattern, patternOptions)
};

return new Capturer(this.screenshotsPath, testEntry, connection, namingOptions);
Expand Down
2 changes: 2 additions & 0 deletions test/server/cli-argument-parser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ describe('CLI argument parser', function () {

return parse('chrome', workingDir)
.then(function (parser) {
console.log('*********************************************', parser.src); // eslint-disable-line
expect(parser.src).eql(expected);
});
});
Expand Down Expand Up @@ -358,6 +359,7 @@ describe('CLI argument parser', function () {
{ long: '--list-browsers', short: '-b' },
{ long: '--reporter', short: '-r' },
{ long: '--screenshots', short: '-s' },
{ long: '--pattern', short: '-p' },
{ long: '--screenshots-on-fails', short: '-S' },
{ long: '--quarantine-mode', short: '-q' },
{ long: '--debug-mode', short: '-d' },
Expand Down

0 comments on commit 7e1e919

Please sign in to comment.