UNPKG

@reportportal/agent-js-jasmine

Version:

Agent for integration Jasmine with ReportPortal.

429 lines (365 loc) 12.7 kB
/* * Copyright 2020 EPAM Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const { EVENTS } = require('@reportportal/client-javascript/lib/constants/events'); const { RP_STATUSES } = require('@reportportal/client-javascript/lib/constants/statuses'); const clientHelpers = require('@reportportal/client-javascript/lib/helpers'); const SpecificUtils = require('./specificUtils'); const { entityType, hookTypes, hookTypesMap } = require('./constants/itemTypes'); const LOG_LEVELS = require('./constants/logLevels'); const { JasmineStatuses } = require('./constants/testStatuses'); const promiseErrorHandler = (promise) => { promise.catch((err) => { console.error(err); }); }; class ReportportalReporter { constructor(conf, onSetLaunchStatus) { this.client = conf.client; this.tempLaunchId = conf.tempLaunchId; this.parentsInfo = []; this.conf = conf; this.setLaunchStatus = onSetLaunchStatus; this.registerListeners(); this.reportHooks(); this.additionalCustomParams = {}; this.suiteDescription = new Map(); this.suiteLogs = new Map(); this.suiteAttributes = new Map(); this.suiteTestCaseIds = new Map(); this.suiteStatuses = new Map(); this.hookIds = new Map(); this.itemStartTime = null; this.currentTestFilePathIndex; this.prevSuite; } reportHooks() { this.conf.reportHooks && this.installHooks(); } getParentInfo() { if (!this.parentsInfo.length) { return null; } return this.parentsInfo[this.parentsInfo.length - 1]; } getParentOfParentInfo() { if (this.parentsInfo.length > 1) { return this.parentsInfo[this.parentsInfo.length - 2]; } return null; } setParentInfo(info) { this.parentsInfo.push(info); } finishParent() { this.parentsInfo.pop(); } getTopLevelType() { if (!this.parentsInfo.length) { return entityType.SUITE; } return entityType.TEST; } getTime() { return clientHelpers.now(); } addAttributes(attr) { if (attr && attr.suite) { const attributes = (this.suiteAttributes.get(attr.suite) || []).concat(attr.attributes); this.suiteAttributes.set(attr.suite, attributes); } else { const attributes = this.additionalCustomParams.attributes ? { attributes: this.additionalCustomParams.attributes.concat(attr.attributes) } : attr; this.additionalCustomParams = Object.assign(this.additionalCustomParams, attributes); } } setDescription(description) { if (description && description.suite) { this.suiteDescription.set(description.suite, description.text); } else { this.additionalCustomParams = Object.assign( this.additionalCustomParams, description && { description: description.text } ); } } setTestCaseId(testCase) { if (testCase && testCase.suite) { this.suiteTestCaseIds.set(testCase.suite, testCase.testCaseId); } else { this.additionalCustomParams = Object.assign( this.additionalCustomParams, testCase && { testCaseId: testCase.testCaseId } ); } } setStatus(data) { if (data && data.suite) { this.suiteStatuses.set(data.suite, data.status); } else { this.additionalCustomParams = Object.assign( this.additionalCustomParams, data && { customStatus: data.status } ); } } addTestItemLog(testItemLog) { const logWithTime = Object.assign(testItemLog.log, { time: this.getTime() }); if (testItemLog && testItemLog.suite) { const logs = (this.suiteLogs.get(testItemLog.suite) || []).concat([logWithTime]); this.suiteLogs.set(testItemLog.suite, logs); } else { const logs = this.additionalCustomParams.logs ? { logs: this.additionalCustomParams.logs.concat([logWithTime]) } : { logs: [logWithTime] }; this.additionalCustomParams = Object.assign(this.additionalCustomParams, logs); } } sendLaunchLog(log) { this.sendLog(this.tempLaunchId, log); } sendLog(tempId, { level, message = '', file, time }) { this.client.sendLog( tempId, { message, level, time: time || this.getTime(), }, file ); } registerListeners() { process.on(EVENTS.ADD_ATTRIBUTES, this.addAttributes.bind(this)); process.on(EVENTS.SET_DESCRIPTION, this.setDescription.bind(this)); process.on(EVENTS.SET_TEST_CASE_ID, this.setTestCaseId.bind(this)); process.on(EVENTS.SET_STATUS, this.setStatus.bind(this)); process.on(EVENTS.SET_LAUNCH_STATUS, this.setLaunchStatus.bind(this)); process.on(EVENTS.ADD_LOG, this.addTestItemLog.bind(this)); process.on(EVENTS.ADD_LAUNCH_LOG, this.sendLaunchLog.bind(this)); } getSuiteAttributesBySuite(suite) { return this.suiteAttributes.get(suite); } getSuiteDescriptionBySuite(suite) { return this.suiteDescription.get(suite); } getSuiteTestCaseIdBySuite(suite) { return this.suiteTestCaseIds.get(suite); } getSuiteStatusBySuite(suite) { return this.suiteStatuses.get(suite); } getSuiteLogsBySuite(suite) { return this.suiteLogs.get(suite); } changeCurrentTestFilePath(suite) { if (this.currentTestFilePathIndex === undefined) { this.currentTestFilePathIndex = 0; return; } if (this.prevSuite === suite.fullName) return; if (suite.description === suite.fullName) { this.currentTestFilePathIndex += 1; } } suiteStarted(suite) { this.changeCurrentTestFilePath(suite); this.prevSuite = suite.fullName; const fullSuiteName = SpecificUtils.getFullTestName(suite); const promise = SpecificUtils.getCodeRef(this.currentTestFilePathIndex, fullSuiteName); const suiteTitle = suite.description; const attributes = this.getSuiteAttributesBySuite(suiteTitle); const description = this.getSuiteDescriptionBySuite(suiteTitle); const testCaseId = this.getSuiteTestCaseIdBySuite(suiteTitle); const logs = this.getSuiteLogsBySuite(suiteTitle); const type = this.getTopLevelType(); return promise.then((codeRef) => { this.itemStartTime = this.getTime(); const parent = this.getParentInfo(); const suiteObj = this.client.startTestItem( Object.assign( { type, description: suiteTitle, startTime: this.itemStartTime, name: suiteTitle, }, attributes && { attributes }, description && { description }, testCaseId && { testCaseId }, codeRef && { codeRef } ), this.tempLaunchId, parent && parent.tempId ); this.setParentInfo({ tempId: suiteObj.tempId, startTime: this.itemStartTime }); logs && logs.forEach((log) => this.sendLog(suiteObj.tempId, log)); promiseErrorHandler(suiteObj.promise); this.additionalCustomParams = {}; this.suiteAttributes.delete(suiteTitle); this.suiteDescription.delete(suiteTitle); this.suiteTestCaseIds.delete(suiteTitle); this.suiteLogs.delete(suiteTitle); }); } specStarted(spec) { const fullTestName = SpecificUtils.getFullTestName(spec); const promise = SpecificUtils.getCodeRef(this.currentTestFilePathIndex, fullTestName); return promise.then((codeRef) => { this.itemStartTime = this.getTime(); const parent = this.getParentInfo(); const stepObj = this.client.startTestItem( Object.assign( { type: entityType.STEP, description: spec.description, startTime: this.itemStartTime, name: spec.description, }, codeRef && { codeRef } ), this.tempLaunchId, parent && parent.tempId ); this.setParentInfo({ tempId: stepObj.tempId, startTime: this.itemStartTime }); promiseErrorHandler(stepObj.promise); }); } // TODO: update getHookStartTime(hookType, parent) { if (hookType === entityType.BEFORE_METHOD || hookType === entityType.BEFORE_SUITE) { return Math.max(parent && parent.startTime, this.itemStartTime - 1); } return this.itemStartTime || this.getTime(); } hookStarted(hook) { const parent = this.getParentOfParentInfo(); const type = hookTypesMap[hook]; const startTime = this.getHookStartTime(type, parent); const hookObj = this.client.startTestItem( { type, startTime, name: hook, }, this.tempLaunchId, parent && parent.tempId ); this.hookIds.set(hook, hookObj.tempId); promiseErrorHandler(hookObj.promise); } hookDone(hook, RPStatus, err) { const hookId = this.hookIds.get(hook); if (hookId) { this.hookIds.delete(hook); if (err) { const log = { message: `message: ${err}`, level: LOG_LEVELS.ERROR }; this.sendLog(hookId, log); } const hookDonePromise = this.client.finishTestItem(hookId, { status: RPStatus || RP_STATUSES.PASSED, endTime: this.getTime(), }); promiseErrorHandler(hookDonePromise.promise); } } specDone(spec) { const { attributes, description, testCaseId, logs = [], customStatus, } = this.additionalCustomParams; let status = customStatus || spec.status; if (Object.values(JasmineStatuses).includes(status)) { status = RP_STATUSES.SKIPPED; } const parent = this.getParentInfo(); let promise = Promise.resolve(null); if (this.conf.attachPicturesToLogs) { promise = SpecificUtils.takeScreenshot(spec.fullName); } return promise.then((fileObj) => { const allLogs = SpecificUtils.getAllLogs(spec, fileObj, logs); const issue = status === RP_STATUSES.SKIPPED && this.conf.skippedIssue === false ? { issueType: 'NOT_ISSUE' } : null; allLogs && allLogs.forEach((log) => this.sendLog(parent && parent.tempId, log)); const finishTestItemPromise = this.client.finishTestItem( parent && parent.tempId, Object.assign( { status, }, attributes && { attributes }, description && { description }, testCaseId && { testCaseId }, issue && { issue } ) ); promiseErrorHandler(finishTestItemPromise.promise); this.additionalCustomParams = {}; this.finishParent(); this.itemStartTime = null; }); } suiteDone(suite) { const status = this.getSuiteStatusBySuite(suite.description); const parent = this.getParentInfo(); const suiteDonePromise = this.client.finishTestItem( parent && parent.tempId, Object.assign({}, status && { status }) ); promiseErrorHandler(suiteDonePromise.promise); this.finishParent(); this.suiteStatuses.delete(suite.description); this.itemStartTime = null; } installHooks() { const jasmineBeforeAll = global.beforeAll; const jasmineAfterAll = global.afterAll; const jasmineBeforeEach = global.beforeEach; const jasmineAfterEach = global.afterEach; const wrapperBeforeAll = SpecificUtils.makeHooksWrapper( jasmineBeforeAll, () => this.hookStarted(hookTypes.BEFORE_ALL), (status, err) => this.hookDone(hookTypes.BEFORE_ALL, status, err) ); const wrapperAfterAll = SpecificUtils.makeHooksWrapper( jasmineAfterAll, () => this.hookStarted(hookTypes.AFTER_ALL), (status, err) => this.hookDone(hookTypes.AFTER_ALL, status, err) ); const wrapperBeforeEach = SpecificUtils.makeHooksWrapper( jasmineBeforeEach, () => this.hookStarted(hookTypes.BEFORE_EACH), (status, err) => this.hookDone(hookTypes.BEFORE_EACH, status, err) ); const wrapperAfterEach = SpecificUtils.makeHooksWrapper( jasmineAfterEach, () => this.hookStarted(hookTypes.AFTER_EACH), (status, err) => this.hookDone(hookTypes.AFTER_EACH, status, err) ); global.beforeAll = wrapperBeforeAll; global.afterAll = wrapperAfterAll; global.beforeEach = wrapperBeforeEach; global.afterEach = wrapperAfterEach; } } module.exports = ReportportalReporter;