From cdec11ba05777b5a1944c58e9d3ba1afcdb2086a Mon Sep 17 00:00:00 2001 From: Tsvetelina Aleksandrova Date: Tue, 7 Jul 2020 12:43:05 +0300 Subject: [PATCH] feat: add saucelabs reporter (#213) --- docs/config/cloud.md | 26 +++++++++++ package-lock.json | 2 +- src/reporter/saucelabsReporter.js | 78 +++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/reporter/saucelabsReporter.js diff --git a/docs/config/cloud.md b/docs/config/cloud.md index 6c3237e9..fcad5ddd 100644 --- a/docs/config/cloud.md +++ b/docs/config/cloud.md @@ -35,3 +35,29 @@ Some additional [SauceLabs specific capabilities](https://wiki.saucelabs.com/dis * maxDuration - max duration of the whole test suite execution in seconds, default is 30min. Could be extended to 10800sec = 3hours for extremely long tests. * idleTimeout - max duration for a single interaction. This is the time that the application needs to process the longest interaction like a navigation after a click. Default is 90sec and could be extended to 600sec for extremely slow applications. + +## Test annotations +SauceLabs gives you the option to annotate tests and make their execution logs more comrehensive. +UIVeri5 has a SauceLabs reporter that adds a default set of annotations - spec names, actions, expectation results, suite result, etc. +To enable it, simply add it to the `reporters` configuration: +```js +exports.config = { + reporters: [ + {name: './reporter/saucelabsReporter'} + ] +} +``` + +If you want to set a name, tags or CI build number for your test, you can do so in the browser capabilities: +```js +exports.config = { + browsers: [{ + capabilities: { + // here you can add details for your saucelabs test execution + name: "my-test", + tags: ["UIVeri5"], + build: process.env.BUILD_NUMBER + } + }] +}; +``` diff --git a/package-lock.json b/package-lock.json index 6459ca57..11b9a0f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4082,4 +4082,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/reporter/saucelabsReporter.js b/src/reporter/saucelabsReporter.js new file mode 100644 index 00000000..8b3b71aa --- /dev/null +++ b/src/reporter/saucelabsReporter.js @@ -0,0 +1,78 @@ +function JasmineSaucelabsReporter(config, instanceConfig, logger, collector) { + this.config = config; + this.instanceConfig = instanceConfig; + this.logger = logger; + this.collector = collector; +} + +JasmineSaucelabsReporter.prototype.suiteStarted = function () { + browser.executeScript('sauce:context=Suite started: ' + this.collector.getCurrentSuite().name); +}; + +JasmineSaucelabsReporter.prototype.specStarted = function () { + browser.executeScript('sauce:context=Spec started: ' + this.collector.getCurrentSpec().name); +}; + +JasmineSaucelabsReporter.prototype.specDone = function () { + browser.executeScript('sauce:context=Spec done: ' + this.collector.getCurrentSpec().name); +}; + +JasmineSaucelabsReporter.prototype.suiteDone = function () { + browser.executeScript('sauce:context=Suite done: ' + this.collector.getCurrentSuite().name); +}; + +JasmineSaucelabsReporter.prototype.jasmineDone = function () { + var overview = this.collector.getOverview(); + browser.executeScript('sauce:job-result=' + overview.status); +}; + +JasmineSaucelabsReporter.prototype.register = function (jasmineEnv) { + jasmineEnv.addReporter(this); + + ['click', 'sendKeys'].forEach(this._registerOnAction.bind(this)); + this._registerOnExpectation(); +}; + +JasmineSaucelabsReporter.prototype._registerOnAction = function (action) { + var originalAction = protractorModule.parent.parent.exports.WebElement.prototype[action]; + + protractorModule.parent.parent.exports.WebElement.prototype[action] = function () { + var element = this; + var actionValue = arguments[0]; + + // TODO: save the locator which was used to find the element + return element.getAttribute('id').then(function (elementId) { + var onAction = function () { + browser.executeScript('sauce:context=Perform action: ' + action + ' with value "' + actionValue + '" on "' + elementId + '"'); + }; + return originalAction.call(element, actionValue).then(onAction, onAction); + }); + }; +}; + +// should be called after browser.getProcessedConfig() +JasmineSaucelabsReporter.prototype._registerOnExpectation = function () { + var originalAddExpectationResult = jasmine.Spec.prototype.addExpectationResult; + + jasmine.Spec.prototype.addExpectationResult = function (passed, expectation) { + var sExpectation = 'Expectation ' + (passed ? 'passed' : 'failed') + '.'; + + if (expectation.matcherName) { + sExpectation += ' Expected "' + expectation.actual + '" ' + expectation.matcherName + ' "' + expectation.expected + '".'; + } + if (expectation.message) { + sExpectation += ' Message: "' + expectation.message + '". '; + } + if (expectation.error) { + sExpectation += ' Error: "' + expectation.error + '". '; + } + + browser.executeScript('sauce:context=' + sExpectation); + + return originalAddExpectationResult.apply(this, arguments); + }; +}; + +module.exports = function (config, instanceConfig, logger, collector) { + return new JasmineSaucelabsReporter(config, instanceConfig, logger, collector); +};