|
| 1 | +const { Formatter, Status } = require('@cucumber/cucumber'); |
| 2 | +const RPClient = require('@reportportal/client-javascript'); |
| 3 | +class RPFormatter extends Formatter { |
| 4 | + launchId = null; |
| 5 | + |
| 6 | + constructor(options) { |
| 7 | + super(options); |
| 8 | + options.eventBroadcaster.on('envelope', this.processEnvelope.bind(this)); |
| 9 | + this.rpConfig = options.parsedArgvOptions.rpConfig; |
| 10 | + this.rpClient = new RPClient(this.rpConfig); |
| 11 | + } |
| 12 | + |
| 13 | + async processEnvelope(envelope) { |
| 14 | + if (envelope.testRunStarted) { |
| 15 | + await this.startLaunch(); |
| 16 | + } |
| 17 | + else if (envelope.testRunFinished) { |
| 18 | + await this.finishLaunch(); |
| 19 | + } |
| 20 | + else if (envelope.testCaseFinished) { |
| 21 | + await this.finishTest(envelope); |
| 22 | + } |
| 23 | + } |
| 24 | + |
| 25 | + async startLaunch() { |
| 26 | + const launchObj = this.rpClient.startLaunch({ |
| 27 | + name: this.rpConfig.launch, |
| 28 | + startTime: this.rpClient.helpers.now(), |
| 29 | + description: this.rpConfig.description, |
| 30 | + attributes: this.rpConfig.tags |
| 31 | + }); |
| 32 | + |
| 33 | + this.launchId = launchObj.tempId; |
| 34 | + this.features = {}; |
| 35 | + await launchObj.promise; |
| 36 | + } |
| 37 | + |
| 38 | + async finishLaunch() { |
| 39 | + for (const featureName in this.features) { |
| 40 | + await this.rpClient.finishTestItem(this.features[featureName], { status: 'PASSED' }).promise; |
| 41 | + } |
| 42 | + |
| 43 | + await this.rpClient.finishLaunch(this.launchId, { |
| 44 | + endTime: this.rpClient.helpers.now() |
| 45 | + }).promise; |
| 46 | + } |
| 47 | + |
| 48 | + async finishTest(envelope) { |
| 49 | + const testCase = this.eventDataCollector.getTestCaseAttempt(envelope.testCaseFinished.testCaseStartedId); |
| 50 | + const featureName = testCase.gherkinDocument.feature.name; |
| 51 | + if (!this.features[featureName]) { |
| 52 | + const featureItem = this.rpClient.startTestItem({ |
| 53 | + description: |
| 54 | + this.formatTags(testCase.gherkinDocument.feature.tags) + |
| 55 | + '\n' + |
| 56 | + testCase.gherkinDocument.feature.description, |
| 57 | + name: featureName, |
| 58 | + startTime: this.rpClient.helpers.now(), |
| 59 | + type: 'SUITE' |
| 60 | + }, this.launchId); |
| 61 | + this.features[featureName] = featureItem.tempId; |
| 62 | + await featureItem.promise; |
| 63 | + } |
| 64 | + |
| 65 | + const featureTempId = this.features[featureName] |
| 66 | + // Start test item |
| 67 | + const testItem = this.rpClient.startTestItem({ |
| 68 | + description: this.formatTags(testCase.pickle.tags), |
| 69 | + name: testCase.pickle.name, |
| 70 | + startTime: this.rpClient.helpers.now(), |
| 71 | + type: 'STEP' |
| 72 | + }, this.launchId, featureTempId); |
| 73 | + await testItem.promise; |
| 74 | + |
| 75 | + //send steps |
| 76 | + const steps = this.getStepResults(testCase) |
| 77 | + for (const step of steps) { |
| 78 | + const attachment = step.attachment && step.attachment[0] |
| 79 | + ? { |
| 80 | + name: 'attachment', |
| 81 | + type: step.attachment[0].mediaType, |
| 82 | + content: step.attachment[0].body |
| 83 | + } |
| 84 | + : undefined; |
| 85 | + await this.rpClient.sendLog(testItem.tempId, { |
| 86 | + level: step.result.status === Status.PASSED |
| 87 | + ? 'INFO' |
| 88 | + : 'ERROR', |
| 89 | + message: this.getMessage(step), |
| 90 | + time: this.rpClient.helpers.now() |
| 91 | + }, attachment).promise |
| 92 | + } |
| 93 | + |
| 94 | + //finish test item |
| 95 | + const status = Object.values(testCase.stepResults).some(step => step.status !== Status.PASSED) |
| 96 | + ? Status.FAILED.toLowerCase() |
| 97 | + : Status.PASSED.toLowerCase() |
| 98 | + await this.rpClient.finishTestItem(testItem.tempId, { |
| 99 | + status |
| 100 | + }).promise; |
| 101 | + } |
| 102 | + |
| 103 | + getStepResults(testCase) { |
| 104 | + return testCase.testCase.testSteps.map(step => ({ |
| 105 | + result: testCase.stepResults[step.id], |
| 106 | + pickle: testCase.pickle.steps.find(pickle => pickle.id === step.pickleStepId), |
| 107 | + attachment: testCase.stepAttachments[step.id] |
| 108 | + })) |
| 109 | + } |
| 110 | + |
| 111 | + getMessage(step) { |
| 112 | + if (!step.pickle) return 'Hook'; |
| 113 | + const messageParts = [step.pickle.text]; |
| 114 | + if (step.pickle.argument) { |
| 115 | + if (step.pickle.argument.dataTable) messageParts.push( |
| 116 | + this.formatTable(step.pickle.argument.dataTable) |
| 117 | + ) |
| 118 | + if (step.pickle.argument.docString) messageParts.push(this.formatDocString(step.pickle.argument.docString)) |
| 119 | + } |
| 120 | + if (step.result.status === Status.FAILED) messageParts.push(step.result.message) |
| 121 | + |
| 122 | + return messageParts.join('\n') |
| 123 | + } |
| 124 | + |
| 125 | + formatTable(dataTable) { |
| 126 | + const TR = '<tr>'; |
| 127 | + const TRE = '</tr>'; |
| 128 | + const TD = '<td>'; |
| 129 | + const TDE = '</td>'; |
| 130 | + const formatRow = row => TR + row.cells.map(cell => TD + cell.value + TDE).join('') + TRE; |
| 131 | + return '<table><tbody>' + dataTable.rows.map(formatRow).join('') + '</tbody></table>' |
| 132 | + } |
| 133 | + |
| 134 | + formatDocString(docString) { |
| 135 | + return '<pre><code>' + docString.content + '</code></pre>' |
| 136 | + } |
| 137 | + |
| 138 | + formatTags(tags) { |
| 139 | + return tags.map(tag => '<code>' + tag.name + '</code>').join('') |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +module.exports = RPFormatter |
0 commit comments