protractor-smartrunner
Advanced tools
Comparing version 2.0.0-beta1 to 2.0.0-beta10
export declare function isCliGrepped(): boolean; | ||
export declare function getExclusions(filePath: string | null): string[]; | ||
export declare function getExclusionGrep(filePath: string | null): string; | ||
export declare function getResultsOutputPath(outputDirectory: string, repoHash: string): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isCliGrepped = void 0; | ||
exports.getResultsOutputPath = exports.getExclusionGrep = exports.getExclusions = exports.isCliGrepped = void 0; | ||
const path_1 = require("path"); | ||
function isCliGrepped() { | ||
@@ -8,1 +9,19 @@ return process.argv.some((argument) => /^-g=/.test(argument) || /^--grep=/.test(argument)); | ||
exports.isCliGrepped = isCliGrepped; | ||
function getExclusions(filePath) { | ||
return (filePath === null || filePath === void 0 ? void 0 : filePath.length) ? Object.keys(require(filePath)) : ['']; | ||
} | ||
exports.getExclusions = getExclusions; | ||
function getExclusionGrep(filePath) { | ||
return getExclusions(filePath).join('|'); | ||
} | ||
exports.getExclusionGrep = getExclusionGrep; | ||
function getResultsOutputPath(outputDirectory, repoHash) { | ||
if (!(outputDirectory === null || outputDirectory === void 0 ? void 0 : outputDirectory.length)) { | ||
throw new Error('đ ERROR: outputDirectory is not defined!'); | ||
} | ||
if (!(repoHash === null || repoHash === void 0 ? void 0 : repoHash.length)) { | ||
throw new Error('đ ERROR: repoHash is not defined!'); | ||
} | ||
return path_1.resolve(outputDirectory, repoHash); | ||
} | ||
exports.getResultsOutputPath = getResultsOutputPath; |
@@ -1,2 +0,4 @@ | ||
import { SmartRunnerFactory } from './smartrunner.factory'; | ||
export = SmartRunnerFactory; | ||
export { SmartRunnerFactory } from './smartrunner.factory'; | ||
export * from './common.interfaces'; | ||
export * from './helpers'; | ||
export * from './fs-helpers'; |
"use strict"; | ||
const smartrunner_factory_1 = require("./smartrunner.factory"); | ||
module.exports = smartrunner_factory_1.SmartRunnerFactory; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.SmartRunnerFactory = void 0; | ||
var smartrunner_factory_1 = require("./smartrunner.factory"); | ||
Object.defineProperty(exports, "SmartRunnerFactory", { enumerable: true, get: function () { return smartrunner_factory_1.SmartRunnerFactory; } }); | ||
__exportStar(require("./common.interfaces"), exports); | ||
__exportStar(require("./helpers"), exports); | ||
__exportStar(require("./fs-helpers"), exports); |
@@ -21,4 +21,4 @@ "use strict"; | ||
const suiteName = result.fullName.replace(specName, '').trim(); | ||
if (result.status !== 'disabled') { | ||
this.results.set(suiteName, specName, result.status === 'passed' || result.status === 'pending', duration); | ||
if (result.status !== 'disabled' && result.status !== 'pending') { | ||
this.results.set(suiteName, specName, result.status === 'passed', duration); | ||
} | ||
@@ -25,0 +25,0 @@ } |
import { Logger } from 'protractor/built/logger'; | ||
export interface TestResult { | ||
retries: number; | ||
passed: boolean; | ||
duration: number; | ||
} | ||
export interface TestResults { | ||
[testName: string]: TestResult; | ||
} | ||
export interface SuiteResults { | ||
[suiteName: string]: TestResults; | ||
} | ||
import { TestResult } from './common.interfaces'; | ||
export interface SuiteUpdateFlags { | ||
@@ -14,0 +4,0 @@ [suiteName: string]: boolean; |
@@ -5,2 +5,4 @@ "use strict"; | ||
const path_1 = require("path"); | ||
const fs_helpers_1 = require("./fs-helpers"); | ||
const helpers_1 = require("./helpers"); | ||
const fs = require('fs-extra'); | ||
@@ -11,14 +13,14 @@ const filenamify = require('filenamify'); | ||
this.logger = logger; | ||
this.affectedSuites = {}; | ||
if (!(repoHash === null || repoHash === void 0 ? void 0 : repoHash.length)) { | ||
this.logger.error('đ ERROR: repoHash is not defined, terminating...'); | ||
try { | ||
this.affectedSuites = {}; | ||
this.smartRunDir = helpers_1.getResultsOutputPath(outputDirectory, repoHash); | ||
fs.ensureDirSync(this.smartRunDir); | ||
} | ||
catch (error) { | ||
this.logger.error(error.message); | ||
process.exit(1); | ||
} | ||
this.smartRunDir = path_1.resolve(outputDirectory, repoHash); | ||
fs.ensureDirSync(this.smartRunDir); | ||
} | ||
load() { | ||
this.results = fs.readdirSync(this.smartRunDir) | ||
.map((jsonFile) => fs.readJsonSync(path_1.resolve(this.smartRunDir, `./${jsonFile}`))) | ||
.reduce((accumulator, currentValue) => (Object.assign(Object.assign({}, accumulator), currentValue)), {}); | ||
this.results = fs_helpers_1.loadResults(this.smartRunDir); | ||
} | ||
@@ -46,3 +48,4 @@ set(suiteName, testName, passed, duration) { | ||
this.logger.info(`âšī¸ Suite (${suite}) was affected by this thread, writing to filesystem.`); | ||
const fileName = path_1.resolve(this.smartRunDir, filenamify(`./${suite}.json`)); | ||
const suiteFileName = filenamify(suite.replace(/\s/g, '').toLowerCase(), { maxLength: 250 }) + '.json'; | ||
const fileName = path_1.resolve(this.smartRunDir, suiteFileName); | ||
fs.outputJsonSync(fileName, { [suite]: this.results[suite] }, { spaces: 4 }); | ||
@@ -49,0 +52,0 @@ } |
@@ -5,2 +5,3 @@ import { Logger } from 'protractor/built/logger'; | ||
passedMessagePrefix?: string; | ||
excludedMessagePrefix?: string; | ||
repoHash: string; | ||
@@ -7,0 +8,0 @@ exclusionPath?: string | null; |
import { SmartRunnerOptions, SmartRunner } from './smartrunner'; | ||
export declare class SmartRunnerFactory { | ||
private options; | ||
private providedOptions; | ||
private logger; | ||
constructor(options: SmartRunnerOptions); | ||
constructor(providedOptions: SmartRunnerOptions); | ||
getInstance(): SmartRunner; | ||
@@ -14,2 +14,10 @@ applyExclusionFilter(): { | ||
}; | ||
getResultsOutputPath(): string; | ||
get options(): { | ||
outputDirectory: string; | ||
passedMessagePrefix: string; | ||
excludedMessagePrefix: string; | ||
repoHash: string; | ||
exclusionPath: string | null; | ||
}; | ||
} |
@@ -12,11 +12,12 @@ "use strict"; | ||
passedMessagePrefix: 'đĸ previously passed:', | ||
excludedMessagePrefix: 'đ excluded:', | ||
exclusionPath: null | ||
}; | ||
class SmartRunnerFactory { | ||
constructor(options) { | ||
this.options = options; | ||
constructor(providedOptions) { | ||
this.providedOptions = providedOptions; | ||
this.logger = new logger_1.Logger(LOGGER_ID); | ||
} | ||
getInstance() { | ||
return new smartrunner_1.SmartRunner(Object.assign(Object.assign({}, DEFAULT_OPTIONS), this.options), this.logger); | ||
return new smartrunner_1.SmartRunner(this.options, this.logger); | ||
} | ||
@@ -31,9 +32,7 @@ applyExclusionFilter() { | ||
} | ||
const exclusions = Object.keys(require(this.options.exclusionPath)); | ||
const exclusions = helpers_1.getExclusions(this.options.exclusionPath); | ||
const grep = helpers_1.getExclusionGrep(this.options.exclusionPath); | ||
if (exclusions.length) { | ||
this.logger.info('đĢ Exclusion patterns: ', exclusions.join(', ')); | ||
return { | ||
grep: exclusions.join('|'), | ||
invertGrep: true | ||
}; | ||
return { grep, invertGrep: true }; | ||
} | ||
@@ -46,3 +45,9 @@ } | ||
} | ||
getResultsOutputPath() { | ||
return helpers_1.getResultsOutputPath(this.options.outputDirectory, this.options.repoHash); | ||
} | ||
get options() { | ||
return Object.assign(Object.assign({}, DEFAULT_OPTIONS), this.providedOptions); | ||
} | ||
} | ||
exports.SmartRunnerFactory = SmartRunnerFactory; |
@@ -22,2 +22,3 @@ "use strict"; | ||
const oldSpecFilter = jasmine.getEnv().specFilter; | ||
const exclusions = helpers_1.getExclusionGrep(this.options.exclusionPath); | ||
jasmine.getEnv().specFilter = (spec) => { | ||
@@ -30,2 +31,5 @@ const testName = spec.getResult().description; | ||
} | ||
if (exclusions.length && spec.getFullName().match(new RegExp(exclusions)) != null) { | ||
this.logger.info(`${this.options.excludedMessagePrefix} ${suiteName} ${testName}`); | ||
} | ||
return !testPassedInPreviousRun.passed && oldSpecFilter(spec); | ||
@@ -32,0 +36,0 @@ }; |
{ | ||
"name": "protractor-smartrunner", | ||
"version": "2.0.0-beta1", | ||
"version": "2.0.0-beta10", | ||
"description": "Protractor utility for keeping track of passed/failed tests between runs. Works together with protractor-retry.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
# Protractor Smartrunner | ||
Protractor utility features for having more configuration over spec filtering, like: | ||
1. [Keeping track of passed/failed tests between runs](#1-record-status-of-test-runs) (works together with [protractor-retry](https://www.npmjs.com/package/protractor-retry)). | ||
1. [Keeping track of passed/failed tests between runs](#1-record-status-of-test-runs) | ||
(works together with [protractor-retry](https://www.npmjs.com/package/protractor-retry) and [protractor-retry-angular-cli](https://www.npmjs.com/package/protractor-retry-angular-cli)). | ||
2. [Having an exclusion file containing the name of temporary disabled specs.](#2-spec-exclusions) | ||
## Warning | ||
> ___ | ||
> This library only works for protractor configurations using the [jasmine framework](#usage)!!! | ||
Mocha framework support is NOT planned at the moment. If it is needed by someone, then Pull Requests are more than welcome. :P | ||
> ___ | ||
## Installation | ||
@@ -19,3 +28,3 @@ | ||
Every test has the following status object stored in json files: | ||
Every `passed` or `failed` test **(no pending or disabled!)** has the following status object stored in json files: | ||
```json | ||
@@ -37,3 +46,3 @@ { | ||
``` | ||
After the first run, during every subsequent protractor execution, this feature only lets the failed tests to run, every previously passed tests will be skipped (and displayed with the `â previously passed:` prefix, which is configurable, see the [options](#options)). | ||
After the first run, during every subsequent protractor execution, this feature only lets the failed tests to run, every previously passed tests will be skipped (and displayed with the `đĸ previously passed:` prefix, which is configurable, see the [options](#options)). | ||
@@ -55,7 +64,12 @@ This can be particularly handy and performant in CI environments, if you happen to have flaky tests, or you know that some of your tests might have failed, not because of your changeset, but e.g.: shortage of BE service or bug in the related BE service. This way, fixing the BE, you can rerun only those tests which failed. | ||
Add the following snippets to your protractor configuration file: | ||
Add the following snippets to your `protractor.conf.js` configuration file: | ||
```js | ||
const SmartRunner = require('protractor-smartrunner'); | ||
const SmartRunnerFactory = require('protractor-smartrunner').SmartRunnerFactory; | ||
const smartRunnerFactory = new SmartRunnerFactory({ | ||
outputDirectory: '/absolute/path/to/where/you/want/to/store/the/results', | ||
repoHash: process.env.GIT_HASH | ||
}); | ||
exports.config = { | ||
@@ -68,3 +82,3 @@ ... | ||
onPrepare() { | ||
SmartRunner.apply({ repoHash: process.env.GIT_HASH }); | ||
smartRunnerFactory.getInstance().onPrepare(); | ||
} | ||
@@ -78,3 +92,3 @@ | ||
Smartrunner accepts the following configuration options: | ||
SmartRunnerFactory accepts the following configuration options: | ||
@@ -84,3 +98,5 @@ ```ts | ||
outputDirectory?: string; // default: './.protractor-smartrunner' | ||
passedMessagePrefix?: string; // default: 'â previously passed:' | ||
passedMessagePrefix?: string; // default: 'đĸ previously passed:' | ||
excludedMessagePrefix?: string; // default: 'đ excluded:' | ||
exclusionPath?: string | null; // default: null | ||
repoHash: string; | ||
@@ -94,4 +110,9 @@ } | ||
const retry = require('protractor-retry').retry; | ||
const SmartRunner = require('protractor-smartrunner'); | ||
const SmartRunnerFactory = require('protractor-smartrunner').SmartRunnerFactory; | ||
const smartRunnerFactory = new SmartRunnerFactory({ | ||
outputDirectory: '/absolute/path/to/where/you/want/to/store/the/results', | ||
repoHash: process.env.GIT_HASH | ||
}); | ||
exports.config = { | ||
@@ -106,4 +127,3 @@ | ||
retry.onPrepare(); | ||
SmartRunner.apply({ repoHash: process.env.GIT_HASH }); | ||
smartRunnerFactory.getInstance().onPrepare(); | ||
}, | ||
@@ -125,14 +145,7 @@ | ||
The test results are stored in the following directory by default: `.protractor-smartrunner` (can be configured, see [options](#options)). To be able to store the results between test runs, you may need to cache this directory in your CI pipeline. | ||
The test results are stored in the following directory by default: `./.protractor-smartrunner` (can be configured, see [options](#options)). To be able to store the results between test runs, you may need to cache this directory in your CI pipeline. | ||
##### Travis | ||
With Tavis, you can do this with the cache option in your `.travis.yml` file: | ||
With Tavis, you can do this with Travis Workspace features. See the [.travis.yml]('https://github.com/popovicsandras/protractor-smartrunner/blob/master/.travis.yml') file for the details. | ||
```yml | ||
cache: | ||
directories: | ||
- node_modules | ||
- .protractor-smartrunner | ||
``` | ||
### 2. Spec exclusions | ||
@@ -146,3 +159,3 @@ | ||
The spec exclusion file is a one level depth dictionary json, where the keys are the grep pattern to exclude, like this: | ||
The spec exclusion file is a one level deep json object, where the keys are the **grep** pattern to exclude, like this: | ||
@@ -164,5 +177,11 @@ #### Exclusion file | ||
```js | ||
const SmartRunner = require('protractor-smartrunner'); | ||
const resolve = require('path').resolve; | ||
const SmartRunnerFactory = require('protractor-smartrunner').SmartRunnerFactory; | ||
const smartRunnerFactory = new SmartRunnerFactory({ | ||
outputDirectory: '/absolute/path/to/where/you/want/to/store/the/results', | ||
repoHash: process.env.GIT_HASH, | ||
exclusionPath: resolve(__dirname, 'protractor.excludes.json') | ||
}); | ||
exports.config = { | ||
@@ -175,5 +194,3 @@ ... | ||
print: () => {}, | ||
...SmartRunner.withOptionalExclusions( | ||
resolve(__dirname, 'protractor.excludes.json') | ||
) | ||
...smartRunnerFactory.applyExclusionFilter() | ||
}, | ||
@@ -180,0 +197,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
23242
18
380
1
188
4