wdio-reportportal-reporter
Advanced tools
Comparing version 0.0.7 to 0.0.8
@@ -7,4 +7,13 @@ 'use strict'; | ||
var _stringify = require('babel-runtime/core-js/json/stringify'); | ||
var _stringify2 = _interopRequireDefault(_stringify); | ||
var _assign = require('babel-runtime/core-js/object/assign'); | ||
var _assign2 = _interopRequireDefault(_assign); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/* eslint-disable object-curly-newline */ | ||
const { EventEmitter } = require('events'); | ||
@@ -14,3 +23,3 @@ const ReportPortalClient = require('reportportal-client'); | ||
const { testItemStatuses, events, entityType } = require('./constants'); | ||
const { promiseErrorHandler, logger } = require('./utils'); | ||
const { promiseErrorHandler, logger, isEmpty, limit, sendToReporter } = require('./utils'); | ||
@@ -26,4 +35,11 @@ const { PASSED, FAILED, SKIPPED } = testItemStatuses; | ||
this.config = config; | ||
this.client = new ReportPortalClient(options); | ||
const { tempId, promise } = this.client.startLaunch({ mode: options.mode || 'DEFAULT' }); | ||
this.options = (0, _assign2.default)({ | ||
enableSeleniumCommandReporting: false, | ||
seleniumCommandsLogLevel: 'debug', | ||
enableScreenshotsReporting: false, | ||
screenshotsLogLevel: 'info' | ||
}, options); | ||
this.client = new ReportPortalClient(options.rpConfig); | ||
const { tempId, promise } = this.client.startLaunch({ mode: options.rpConfig.mode || 'DEFAULT' }); | ||
promiseErrorHandler(promise); | ||
@@ -40,2 +56,6 @@ this.tempLaunchId = tempId; | ||
this.on('start', this.start.bind(this)); | ||
this.on('runner:command', this.runnerCommand.bind(this)); | ||
this.on('runner:result', this.runnerResult.bind(this)); | ||
// Rp events | ||
@@ -152,2 +172,60 @@ this.on(events.RP_LOG, this.sendLog.bind(this)); | ||
runnerCommand(command) { | ||
if (!this.options.enableSeleniumCommandReporting || this.isMultiremote) { | ||
return; | ||
} | ||
const parentId = this.getParentId(command.cid); | ||
if (!parentId) { | ||
return; | ||
} | ||
const method = `${command.method} ${command.uri.path}`; | ||
if (!isEmpty(command.data)) { | ||
const data = (0, _stringify2.default)(limit(command.data)); | ||
this.sendLog({ cid: command.cid, level: this.options.seleniumCommandsLogLevel, message: `${method}\n${data}` }); | ||
} else { | ||
this.sendLog({ cid: command.cid, level: this.options.seleniumCommandsLogLevel, message: `${method}` }); | ||
} | ||
} | ||
start(event) { | ||
this.isMultiremote = event.isMultiremote; | ||
} | ||
runnerResult(command) { | ||
if (this.isMultiremote) { | ||
return; | ||
} | ||
const parentId = this.getParentId(command.cid); | ||
if (!parentId) { | ||
return; | ||
} | ||
const isScreenshot = command.requestOptions.uri.path.match(/\/session\/[^/]*\/screenshot/) && command.body.value; | ||
if (isScreenshot) { | ||
if (this.options.enableScreenshotsReporting) { | ||
const obj = { | ||
cid: command.cid, | ||
level: this.options.screenshotsLogLevel, | ||
name: 'screenshot.png', | ||
content: command.body.value | ||
}; | ||
this.sendFile(obj); | ||
} | ||
} | ||
if (this.options.enableSeleniumCommandReporting) { | ||
if (command.body && !isEmpty(command.body.value)) { | ||
const method = `${command.requestOptions.uri.path}`; | ||
// eslint-disable-next-line no-param-reassign | ||
delete command.body.sessionId; | ||
const data = (0, _stringify2.default)(limit(command.body)); | ||
this.sendLog({ cid: command.cid, level: this.options.seleniumCommandsLogLevel, message: `${method}\n${data}` }); | ||
} | ||
} | ||
} | ||
async sendLogToLastFailedItem({ cid, level, message }) { | ||
@@ -234,4 +312,24 @@ if (!(await this.waitForFailedTest(cid, 2000, 10))) { | ||
} | ||
static sendLog(level, message) { | ||
sendToReporter(events.RP_LOG, { level, message }); | ||
} | ||
static sendFile(level, name, content, type = 'image/png') { | ||
// eslint-disable-next-line object-curly-newline | ||
sendToReporter(events.RP_FILE, { level, name, content, type }); | ||
} | ||
static sendLogToLastFailedTest(level, message) { | ||
sendToReporter(events.RP_FAILED_LOG, { level, message }); | ||
} | ||
static sendFileToLastFailedTest(level, name, content, type = 'image/png') { | ||
// eslint-disable-next-line object-curly-newline | ||
sendToReporter(events.RP_FAILED_FILE, { level, name, content, type }); | ||
} | ||
} | ||
ReportPortalReporter.reporterName = 'reportportal'; | ||
module.exports = ReportPortalReporter; |
@@ -1,5 +0,26 @@ | ||
"use strict"; | ||
'use strict'; | ||
/* eslint-disable no-console */ | ||
var _assign = require('babel-runtime/core-js/object/assign'); | ||
var _assign2 = _interopRequireDefault(_assign); | ||
var _stringify = require('babel-runtime/core-js/json/stringify'); | ||
var _stringify2 = _interopRequireDefault(_stringify); | ||
var _keys = require('babel-runtime/core-js/object/keys'); | ||
var _keys2 = _interopRequireDefault(_keys); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/* eslint-disable no-console,no-param-reassign */ | ||
const stringify = require('json-stringify-safe'); | ||
const OBJLENGTH = 10; | ||
const ARRLENGTH = 10; | ||
const STRINGLIMIT = 1000; | ||
const STRINGTRUNCATE = 200; | ||
const notBase64 = /[^A-Z0-9+/=]/i; | ||
const logger = { | ||
@@ -23,2 +44,76 @@ info(msg) { | ||
module.exports = { promiseErrorHandler, logger }; | ||
const isEmpty = object => !object || (0, _keys2.default)(object).length === 0; | ||
const isBase64 = str => { | ||
if (typeof str !== 'string') { | ||
return false; | ||
} | ||
const len = str.length; | ||
if (!len || len % 4 !== 0 || notBase64.test(str)) { | ||
return false; | ||
} | ||
const firstPaddingChar = str.indexOf('='); | ||
return firstPaddingChar === -1 || firstPaddingChar === len - 1 || firstPaddingChar === len - 2 && str[len - 1] === '='; | ||
}; | ||
/** | ||
* Limit the length of an arbitrary variable of any type, suitable for being logged or displayed | ||
* @param {Any} val Any variable | ||
* @return {Any} Limited var of same type | ||
*/ | ||
const limit = val => { | ||
if (!val) return val; | ||
// Ensure we're working with a copy | ||
let value = JSON.parse(stringify(val)); | ||
switch (Object.prototype.toString.call(value)) { | ||
case '[object String]': | ||
if (value.length > 100 && isBase64(value)) { | ||
return `[base64] ${value.length} bytes`; | ||
} | ||
if (value.length > STRINGLIMIT) { | ||
return `${value.substr(0, STRINGTRUNCATE)} ... (${value.length - STRINGTRUNCATE} more bytes)`; | ||
} | ||
return value; | ||
case '[object Array]': | ||
{ | ||
const { length } = value; | ||
if (length > ARRLENGTH) { | ||
value = value.slice(0, ARRLENGTH); | ||
value.push(`(${length - ARRLENGTH} more items)`); | ||
} | ||
return value.map(limit); | ||
} | ||
case '[object Object]': | ||
{ | ||
const keys = (0, _keys2.default)(value); | ||
const removed = []; | ||
for (let i = 0, l = keys.length; i < l; i += 1) { | ||
if (i < OBJLENGTH) { | ||
value[keys[i]] = limit(value[keys[i]]); | ||
} else { | ||
delete value[keys[i]]; | ||
removed.push(keys[i]); | ||
} | ||
} | ||
if (removed.length) { | ||
value._ = `${keys.length - OBJLENGTH} more keys: ${(0, _stringify2.default)(removed)}`; | ||
} | ||
return value; | ||
} | ||
default: | ||
{ | ||
return value; | ||
} | ||
} | ||
}; | ||
const sendToReporter = (event, msg = {}) => { | ||
process.send((0, _assign2.default)({ event }, msg)); | ||
}; | ||
module.exports = { promiseErrorHandler, logger, isEmpty, limit, sendToReporter }; |
@@ -0,1 +1,2 @@ | ||
/* eslint-disable object-curly-newline */ | ||
const { EventEmitter } = require('events'); | ||
@@ -5,3 +6,3 @@ const ReportPortalClient = require('reportportal-client'); | ||
const { testItemStatuses, events, entityType } = require('./constants'); | ||
const { promiseErrorHandler, logger } = require('./utils'); | ||
const { promiseErrorHandler, logger, isEmpty, limit, sendToReporter } = require('./utils'); | ||
@@ -17,4 +18,11 @@ const { PASSED, FAILED, SKIPPED } = testItemStatuses; | ||
this.config = config; | ||
this.client = new ReportPortalClient(options); | ||
const { tempId, promise } = this.client.startLaunch({ mode: options.mode || 'DEFAULT' }); | ||
this.options = Object.assign({ | ||
enableSeleniumCommandReporting: false, | ||
seleniumCommandsLogLevel: 'debug', | ||
enableScreenshotsReporting: false, | ||
screenshotsLogLevel: 'info', | ||
}, options); | ||
this.client = new ReportPortalClient(options.rpConfig); | ||
const { tempId, promise } = this.client.startLaunch({ mode: options.rpConfig.mode || 'DEFAULT' }); | ||
promiseErrorHandler(promise); | ||
@@ -31,2 +39,6 @@ this.tempLaunchId = tempId; | ||
this.on('start', ::this.start); | ||
this.on('runner:command', ::this.runnerCommand); | ||
this.on('runner:result', ::this.runnerResult); | ||
// Rp events | ||
@@ -151,2 +163,60 @@ this.on(events.RP_LOG, ::this.sendLog); | ||
runnerCommand(command) { | ||
if (!this.options.enableSeleniumCommandReporting || this.isMultiremote) { | ||
return; | ||
} | ||
const parentId = this.getParentId(command.cid); | ||
if (!parentId) { | ||
return; | ||
} | ||
const method = `${command.method} ${command.uri.path}`; | ||
if (!isEmpty(command.data)) { | ||
const data = JSON.stringify(limit(command.data)); | ||
this.sendLog({ cid: command.cid, level: this.options.seleniumCommandsLogLevel, message: `${method}\n${data}` }); | ||
} else { | ||
this.sendLog({ cid: command.cid, level: this.options.seleniumCommandsLogLevel, message: `${method}` }); | ||
} | ||
} | ||
start(event) { | ||
this.isMultiremote = event.isMultiremote; | ||
} | ||
runnerResult(command) { | ||
if (this.isMultiremote) { | ||
return; | ||
} | ||
const parentId = this.getParentId(command.cid); | ||
if (!parentId) { | ||
return; | ||
} | ||
const isScreenshot = command.requestOptions.uri.path.match(/\/session\/[^/]*\/screenshot/) && command.body.value; | ||
if (isScreenshot) { | ||
if (this.options.enableScreenshotsReporting) { | ||
const obj = { | ||
cid: command.cid, | ||
level: this.options.screenshotsLogLevel, | ||
name: 'screenshot.png', | ||
content: command.body.value, | ||
}; | ||
this.sendFile(obj); | ||
} | ||
} | ||
if (this.options.enableSeleniumCommandReporting) { | ||
if (command.body && !isEmpty(command.body.value)) { | ||
const method = `${command.requestOptions.uri.path}`; | ||
// eslint-disable-next-line no-param-reassign | ||
delete command.body.sessionId; | ||
const data = JSON.stringify(limit(command.body)); | ||
this.sendLog({ cid: command.cid, level: this.options.seleniumCommandsLogLevel, message: `${method}\n${data}` }); | ||
} | ||
} | ||
} | ||
async sendLogToLastFailedItem({ cid, level, message }) { | ||
@@ -233,4 +303,24 @@ if (!(await this.waitForFailedTest(cid, 2000, 10))) { | ||
} | ||
static sendLog(level, message) { | ||
sendToReporter(events.RP_LOG, { level, message }); | ||
} | ||
static sendFile(level, name, content, type = 'image/png') { | ||
// eslint-disable-next-line object-curly-newline | ||
sendToReporter(events.RP_FILE, { level, name, content, type }); | ||
} | ||
static sendLogToLastFailedTest(level, message) { | ||
sendToReporter(events.RP_FAILED_LOG, { level, message }); | ||
} | ||
static sendFileToLastFailedTest(level, name, content, type = 'image/png') { | ||
// eslint-disable-next-line object-curly-newline | ||
sendToReporter(events.RP_FAILED_FILE, { level, name, content, type }); | ||
} | ||
} | ||
ReportPortalReporter.reporterName = 'reportportal'; | ||
module.exports = ReportPortalReporter; |
@@ -1,3 +0,10 @@ | ||
/* eslint-disable no-console */ | ||
/* eslint-disable no-console,no-param-reassign */ | ||
const stringify = require('json-stringify-safe'); | ||
const OBJLENGTH = 10; | ||
const ARRLENGTH = 10; | ||
const STRINGLIMIT = 1000; | ||
const STRINGTRUNCATE = 200; | ||
const notBase64 = /[^A-Z0-9+/=]/i; | ||
const logger = { | ||
@@ -21,2 +28,75 @@ info(msg) { | ||
module.exports = { promiseErrorHandler, logger }; | ||
const isEmpty = object => !object || Object.keys(object).length === 0; | ||
const isBase64 = (str) => { | ||
if (typeof str !== 'string') { | ||
return false; | ||
} | ||
const len = str.length; | ||
if (!len || len % 4 !== 0 || notBase64.test(str)) { | ||
return false; | ||
} | ||
const firstPaddingChar = str.indexOf('='); | ||
return firstPaddingChar === -1 || | ||
firstPaddingChar === len - 1 || | ||
(firstPaddingChar === len - 2 && str[len - 1] === '='); | ||
}; | ||
/** | ||
* Limit the length of an arbitrary variable of any type, suitable for being logged or displayed | ||
* @param {Any} val Any variable | ||
* @return {Any} Limited var of same type | ||
*/ | ||
const limit = (val) => { | ||
if (!val) return val; | ||
// Ensure we're working with a copy | ||
let value = JSON.parse(stringify(val)); | ||
switch (Object.prototype.toString.call(value)) { | ||
case '[object String]': | ||
if (value.length > 100 && isBase64(value)) { | ||
return `[base64] ${value.length} bytes`; | ||
} | ||
if (value.length > STRINGLIMIT) { | ||
return `${value.substr(0, STRINGTRUNCATE)} ... (${value.length - STRINGTRUNCATE} more bytes)`; | ||
} | ||
return value; | ||
case '[object Array]': { | ||
const { length } = value; | ||
if (length > ARRLENGTH) { | ||
value = value.slice(0, ARRLENGTH); | ||
value.push(`(${length - ARRLENGTH} more items)`); | ||
} | ||
return value.map(limit); | ||
} | ||
case '[object Object]': { | ||
const keys = Object.keys(value); | ||
const removed = []; | ||
for (let i = 0, l = keys.length; i < l; i += 1) { | ||
if (i < OBJLENGTH) { | ||
value[keys[i]] = limit(value[keys[i]]); | ||
} else { | ||
delete value[keys[i]]; | ||
removed.push(keys[i]); | ||
} | ||
} | ||
if (removed.length) { | ||
value._ = `${keys.length - OBJLENGTH} more keys: ${JSON.stringify(removed)}`; | ||
} | ||
return value; | ||
} | ||
default: { | ||
return value; | ||
} | ||
} | ||
}; | ||
const sendToReporter = (event, msg = {}) => { | ||
process.send({ event, ...msg }); | ||
}; | ||
module.exports = { promiseErrorHandler, logger, isEmpty, limit, sendToReporter }; |
{ | ||
"name": "wdio-reportportal-reporter", | ||
"version": "0.0.7", | ||
"version": "0.0.8", | ||
"description": "A WebdriverIO plugin. Report results to Report Portal.", | ||
@@ -31,3 +31,4 @@ "main": "build/reporter.js", | ||
"wdio-plugin", | ||
"wdio-reporter" | ||
"wdio-reporter", | ||
"reportportal" | ||
], | ||
@@ -42,2 +43,3 @@ "author": "Boris Osipov <osipov.boris@gmail.com>", | ||
"babel-runtime": "^6.26.0", | ||
"json-stringify-safe": "~5.0.1", | ||
"reportportal-client": "https://github.com/reportportal/client-javascript.git#b09e49b" | ||
@@ -67,2 +69,3 @@ }, | ||
"wdio-cucumber-framework": "^2.2.7", | ||
"wdio-mocha-framework": "^0.6.3", | ||
"wdio-phantomjs-service": "^0.2.2", | ||
@@ -69,0 +72,0 @@ "webdriverio": "^4.12.0" |
@@ -12,3 +12,3 @@ WDIO Report Portal Reporter | ||
"devDependencies": { | ||
"wdio-reportportal-reporter": "~0.0.6" | ||
"wdio-reportportal-reporter": "~0.0.8" | ||
} | ||
@@ -30,7 +30,13 @@ } | ||
reportportal: { | ||
token: '00000000-0000-0000-0000-00000000000', | ||
endpoint: 'https://reportportal-url/api/v1', | ||
launch: 'launch_name', | ||
project: 'project_name', | ||
mode: 'DEFAULT' | ||
rpConfig: { | ||
token: '00000000-0000-0000-0000-00000000000', | ||
endpoint: 'https://reportportal-url/api/v1', | ||
launch: 'launch_name', | ||
project: 'project_name', | ||
mode: 'DEFAULT' | ||
}, | ||
enableSeleniumCommandReporting: false, | ||
enableScreenshotsReporting: false, | ||
seleniumCommandsLogLevel: 'debug', | ||
screenshotsLogLevel: 'info', | ||
} | ||
@@ -41,1 +47,48 @@ }, | ||
``` | ||
## Additional API | ||
Api methods can be accessed using: | ||
```js | ||
const reporter = require('wdio-allure-reporter') | ||
``` | ||
### Methods description | ||
* `sendLog(level, message) ` – send log to current suite\test item. | ||
* `level` (*String*) - log level. Values ['trace', 'debug', 'info', 'warn', 'error']. | ||
* `message` (*String*)– log message content. | ||
* `sendFile(level, name, content, [type])` – send file to current suite\test item. | ||
* `level` (*String*) - log level. Values ['trace', 'debug', 'info', 'warn', 'error']. | ||
* `name` (*String*)– file name. | ||
* `content` (*String*) – attachment content | ||
* `type` (*String*, optional) – attachment MIME-type, `image/png` by default | ||
* `sendLogToLastFailedTest(level, message)` - send log to last failed test item. | ||
* `level` (*String*) - log level. Values ['trace', 'debug', 'info', 'warn', 'error']. | ||
* `message` (*String*)– log message content. | ||
* `sendFileToLastFailedTest(level, name, content, [type])` – send file to last failed test item. | ||
* `level` (*String*) - log level. Values ['trace', 'debug', 'info', 'warn', 'error']. | ||
* `name` (*String*)– file name. | ||
* `content` (*String*) – attachment content | ||
* `type` (*String*, optional) – attachment MIME-type, `image/png` by default | ||
Pay attention: `sendLog`\\`sendFile` sends log to **current test item**. It means if you send log without active test(e.g from hooks or on suite level) you will not be able to access them in Report Portal UI. | ||
Methods `sendLogToLastFailedTest`\\`sendFileToLastFailedTest` are useful when you need to send screenshots or logs to the failed test item from wdio afterTest hook. | ||
Mocha example: | ||
```js | ||
const reporter = require('wdio-allure-reporter'); | ||
exports.config = { | ||
... | ||
afterTest: async function afterTest(test) { | ||
if (test.passed === false) { | ||
const screenshot = await browser.saveScreenshot(); | ||
reporter.sendFileToLastFailedTest('error', 'failed.png', screenshot); | ||
} | ||
}, | ||
... | ||
``` | ||
## License | ||
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details |
34670
737
92
3
24
14
+ Addedjson-stringify-safe@~5.0.1
+ Addedjson-stringify-safe@5.0.1(transitive)