zebrunner-playwright-agent
Advanced tools
Comparing version 0.0.15 to 0.0.16
{ | ||
"name": "zebrunner-playwright-agent", | ||
"version": "0.0.15", | ||
"version": "0.0.16", | ||
"main": "dist/index.js", | ||
@@ -15,6 +15,7 @@ "license": "MIT", | ||
"debug": "playwright test --config ./playwright.config.ts --workers=1", | ||
"start":"node ./woka.js" | ||
"start": "node ./woka.js" | ||
}, | ||
"devDependencies": { | ||
"@playwright/test": "^1.17.1", | ||
"@slack/web-api": "^6.5.1", | ||
"playwright": "^1.17.1", | ||
@@ -21,0 +22,0 @@ "prettier": "^2.5.1", |
@@ -33,2 +33,7 @@ // playwright.config.ts | ||
concurrentTasks: 19, | ||
slackEnabled: true, | ||
slackDisplayNumberOfFailures: 10, | ||
slackReportOnlyOnFailures: true, | ||
slackReportingChannels: 'zeb,general', | ||
slackStacktraceLength: 270, | ||
}, | ||
@@ -35,0 +40,0 @@ ], |
@@ -63,4 +63,50 @@ # pw-zeb ![Biulds](https://github.com/ryanrosello-og/zebrunner-playwright-agent/actions/workflows/main.yml/badge.svg) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ryanrosello-og/zebrunner-playwright-agent/blob/master/LICENSE) | ||
# Slack Notification | ||
To enable Slack notification, you must firstly generate OAuth token for you bot. Use the existing **[Slack configuration documentation](https://zebrunner.com/documentation/integrations/slack#configuration)** provided by Zebrunner to obtain the token. | ||
Once the OAuth token is generated, you can then provide this value as an environment variable when invoking your tests via the CLI: | ||
e.g. | ||
`ZEB_API_KEY=YOUR_ZEBRUNNER_KEY SLACK_BOT_USER_OAUTH_TOKEN=xoxb-28794-YOUR_BOT_TOKEN npx playwright test` | ||
There are other configurable items available in order to customize the results posted into Slack | ||
**slackEnabled** <default: true> - when true, the reporter will post the test summary to the desired Slack channels | ||
**slackDisplayNumberOfFailures**: <default: 10> - How many failed tests will be show in the Slack message | ||
**slackReportOnlyOnFailures**: <default: true> - Slack message will only be posted if at least 1 failed test exists | ||
**slackReportingChannels**: e.g.'zeb,general' - comma separated values denoting the channel(s) where the test summary will be posted to | ||
**slackStacktraceLength**: <default: 270> - the maximum number of characters from the stack trace to be included in the summary for each failed test | ||
The snippet below shows a typical configuration of the reporter: | ||
``` | ||
reporter: [ | ||
[ | ||
'./node_modules/zebrunner-playwright-agent/src/build/src/lib/zebReporter.js', | ||
{ | ||
reporterBaseUrl: 'https://default.zebrunner.com', | ||
projectKey: 'DEF', | ||
enabled: true, | ||
concurrentTasks: 19, | ||
slackEnabled: true, | ||
slackDisplayNumberOfFailures: 10, | ||
slackReportOnlyOnFailures: true, | ||
slackReportingChannels: 'zeb,general', | ||
slackStacktraceLength: 270, | ||
}, | ||
], | ||
], | ||
``` | ||
The example above will send the test summary results to both the #zeb and #general channels. It will only post results if more than 1 failed test is encountered. Only the first 10 failures will be sent and the length of the stack trace will be limited to 270 characters. | ||
After successful configuration, you should now see results posted to Slack similar to the image below: | ||
![Slack - successful configuration](https://github.com/ryanrosello-og/zebrunner-playwright-agent/blob/main/assets/slack.png?raw=true) | ||
# Contribution | ||
# License |
@@ -34,2 +34,7 @@ "use strict"; | ||
concurrentTasks: 19, | ||
slackEnabled: true, | ||
slackDisplayNumberOfFailures: 10, | ||
slackReportOnlyOnFailures: true, | ||
slackReportingChannels: 'zeb,general', | ||
slackStacktraceLength: 270, | ||
}, | ||
@@ -36,0 +41,0 @@ ], |
@@ -6,3 +6,7 @@ "use strict"; | ||
_result; | ||
constructor(results) { | ||
_build; | ||
_environment; | ||
constructor(results, config) { | ||
this._build = process.env.BUILD_INFO ? process.env.BUILD_INFO : new Date().toISOString(); | ||
this._environment = process.env.TEST_ENVIRONMENT ? process.env.TEST_ENVIRONMENT : '-'; | ||
this._result = { | ||
@@ -12,2 +16,5 @@ tests: [], | ||
title: '', | ||
testRunName: `${process.env.BUILD_INFO ? process.env.BUILD_INFO : new Date().toISOString()} ${process.env.TEST_ENVIRONMENT ? process.env.TEST_ENVIRONMENT : '-'}`, | ||
build: this._build, | ||
environment: this._environment, | ||
}; | ||
@@ -17,5 +24,14 @@ this._resultsData = results; | ||
} | ||
get build() { | ||
return this._build; | ||
} | ||
get environment() { | ||
return this._environment; | ||
} | ||
async getParsedResults() { | ||
return this._result; | ||
} | ||
getRunStartTime() { | ||
return new Date(this._result.tests[0].startedAt).getTime() - 1000; | ||
} | ||
async parse() { | ||
@@ -22,0 +38,0 @@ for (const project of this._resultsData.suites) { |
@@ -21,7 +21,6 @@ "use strict"; | ||
constructor(config) { | ||
const zebRunnerConf = config.reporter.filter((f) => f[0].includes('zeb') || f[1]?.includes('zeb')); | ||
this._accessToken = process.env.ZEB_API_KEY; | ||
this._projectKey = zebRunnerConf[0][1].projectKey; | ||
this._reportBaseUrl = zebRunnerConf[0][1].reporterBaseUrl; | ||
if (zebRunnerConf[0][1].enabled) { | ||
this._projectKey = config.projectKey; | ||
this._reportBaseUrl = config.reporterBaseUrl; | ||
if (config.enabled) { | ||
this._enabled = true; | ||
@@ -32,3 +31,3 @@ } | ||
} | ||
this._concurrentTasks = zebRunnerConf[0][1].concurrentTasks || this._defaultConcurrentTask; | ||
this._concurrentTasks = config.concurrentTasks || this._defaultConcurrentTask; | ||
if (this._concurrentTasks > this._maximumConcurrentTask) { | ||
@@ -35,0 +34,0 @@ this._concurrentTasks = this._maximumConcurrentTask; |
@@ -6,11 +6,26 @@ "use strict"; | ||
const promise_pool_1 = require("@supercharge/promise-pool"); | ||
const SlackReporter_1 = require("./SlackReporter"); | ||
class ZebRunnerReporter { | ||
config; | ||
suite; | ||
zebConfig; | ||
zebAgent; | ||
slackReporter; | ||
testRunId; | ||
onBegin(config, suite) { | ||
const configKeys = config.reporter.filter((f) => f[0].includes('zeb') || f[1]?.includes('zeb')); | ||
this.zebConfig = { | ||
projectKey: configKeys[0][1].projectKey, | ||
reporterBaseUrl: configKeys[0][1].reporterBaseUrl, | ||
enabled: configKeys[0][1].enabled, | ||
concurrentTasks: configKeys[0][1].concurrentTasks, | ||
slackEnabled: configKeys[0][1].slackEnabled, | ||
slackReportOnlyOnFailures: configKeys[0][1].slackReportOnlyOnFailures, | ||
slackDisplayNumberOfFailures: configKeys[0][1].slackDisplayNumberOfFailures, | ||
slackReportingChannels: configKeys[0][1].slackReportingChannels, | ||
slackStacktraceLength: configKeys[0][1].slackStacktraceLength, | ||
}; | ||
this.config = config; | ||
this.suite = suite; | ||
this.zebAgent = new ZebAgent_1.default(this.config); | ||
this.zebAgent = new ZebAgent_1.default(this.zebConfig); | ||
} | ||
@@ -23,3 +38,3 @@ async onEnd() { | ||
await this.zebAgent.initialize(); | ||
let resultsParser = new ResultsParser_1.default(this.suite); | ||
let resultsParser = new ResultsParser_1.default(this.suite, this.zebConfig); | ||
await resultsParser.parse(); | ||
@@ -29,3 +44,5 @@ let parsedResults = await resultsParser.getParsedResults(); | ||
console.time('Duration'); | ||
let zebrunnerResults = await this.postResultsToZebRunner(parsedResults); | ||
let zebrunnerResults = await this.postResultsToZebRunner(resultsParser.getRunStartTime(), parsedResults); | ||
const slackResults = zebrunnerResults.testsExecutions.results; | ||
delete zebrunnerResults.testsExecutions.results; // omit results from printing | ||
console.log(zebrunnerResults); | ||
@@ -36,9 +53,14 @@ console.log(zebrunnerResults.resultsLink !== '' | ||
console.timeEnd('Duration'); | ||
// post to Slack (if enabled) | ||
this.slackReporter = new SlackReporter_1.default(this.zebConfig); | ||
if (this.slackReporter.isEnabled) { | ||
let testSummary = await this.slackReporter.getSummaryResults(this.testRunId, slackResults, resultsParser.build, resultsParser.environment); | ||
await this.slackReporter.sendMessage(testSummary, zebrunnerResults.resultsLink); | ||
} | ||
} | ||
async postResultsToZebRunner(testResults) { | ||
let runStartTime = new Date(testResults.tests[0].startedAt).getTime() - 1000; | ||
let testRunName = `${process.env.BUILD_INFO ? process.env.BUILD_INFO : new Date().toISOString()} ${process.env.TEST_ENVIRONMENT ? process.env.TEST_ENVIRONMENT : '-'}`; | ||
let testRunId = await this.startTestRuns(runStartTime, testRunName); | ||
console.log('testRuns >>', testRunId); | ||
let testRunTags = await this.addTestRunTags(testRunId, [ | ||
async postResultsToZebRunner(runStartTime, testResults) { | ||
let testRunName = testResults.testRunName; | ||
await this.startTestRuns(runStartTime, testRunName); | ||
console.log('testRuns >>', this.testRunId); | ||
let testRunTags = await this.addTestRunTags(this.testRunId, [ | ||
{ | ||
@@ -49,9 +71,9 @@ key: 'group', | ||
]); // broke - labels does not appear in the UI | ||
let testsExecutions = await this.startTestExecutions(testRunId, testResults.tests); | ||
let testTags = await this.addTestTags(testRunId, testsExecutions.results); | ||
let screenshots = await this.addScreenshots(testRunId, testsExecutions.results); | ||
let testSteps = await this.sendTestSteps(testRunId, testsExecutions.results); | ||
let endTestExecutions = await this.finishTestExecutions(testRunId, testsExecutions.results); | ||
let testSessions = await this.sendTestSessions(testRunId, runStartTime, testsExecutions.results); | ||
let stopTestRunsResult = await this.stopTestRuns(testRunId, new Date().toISOString()); | ||
let testsExecutions = await this.startTestExecutions(this.testRunId, testResults.tests); | ||
let testTags = await this.addTestTags(this.testRunId, testsExecutions.results); | ||
let screenshots = await this.addScreenshots(this.testRunId, testsExecutions.results); | ||
let testSteps = await this.sendTestSteps(this.testRunId, testsExecutions.results); | ||
let endTestExecutions = await this.finishTestExecutions(this.testRunId, testsExecutions.results); | ||
let testSessions = await this.sendTestSessions(this.testRunId, runStartTime, testsExecutions.results); | ||
let stopTestRunsResult = await this.stopTestRuns(this.testRunId, new Date().toISOString()); | ||
let summary = { | ||
@@ -61,2 +83,3 @@ testsExecutions: { | ||
errors: testsExecutions.errors.length, | ||
results: testsExecutions.results, | ||
}, | ||
@@ -91,3 +114,3 @@ testRunTags: { | ||
}, | ||
resultsLink: testRunId | ||
resultsLink: this.testRunId | ||
? `${this.zebAgent.baseUrl}/projects/${this.zebAgent.projectKey}/test-runs/${this.testRunId}` | ||
@@ -94,0 +117,0 @@ : '', |
@@ -89,3 +89,3 @@ "use strict"; | ||
parsedResults: async ({ testData }, use) => { | ||
let resultsParser = new ResultsParser_1.default(testData); | ||
let resultsParser = new ResultsParser_1.default(testData, null); | ||
await resultsParser.parse(); | ||
@@ -92,0 +92,0 @@ let r = await resultsParser.getParsedResults(); |
@@ -143,6 +143,7 @@ "use strict"; | ||
}, undefined); | ||
let resultsParser = new ResultsParser_1.default(testData); | ||
let resultsParser = new ResultsParser_1.default(testData, null); | ||
await resultsParser.parse(); | ||
let parsedResults = await resultsParser.getParsedResults(); | ||
let result = await zeb.postResultsToZebRunner(parsedResults); | ||
let result = await zeb.postResultsToZebRunner(1000, parsedResults); | ||
delete result.testsExecutions.results; | ||
(0, test_1.expect)(result).toEqual({ | ||
@@ -149,0 +150,0 @@ testsExecutions: { |
@@ -0,1 +1,3 @@ | ||
import {zebrunnerConfig} from './zebReporter'; | ||
export type testResult = { | ||
@@ -39,9 +41,31 @@ suiteName: string; | ||
title: string; | ||
testRunName: string; | ||
build: string; | ||
environment: string; | ||
}; | ||
export type testSummary = { | ||
build: string; | ||
environment: string; | ||
passed: number; | ||
failed: number; | ||
skipped: number; | ||
aborted: number; | ||
duration: number; | ||
failures: { | ||
zebResult: string; | ||
test: string; | ||
message: string; | ||
}[]; | ||
}; | ||
export default class ResultsParser { | ||
private _resultsData: any; | ||
private _result: testRun; | ||
private _build: string; | ||
private _environment: string; | ||
constructor(results) { | ||
constructor(results, config: zebrunnerConfig) { | ||
this._build = process.env.BUILD_INFO ? process.env.BUILD_INFO : new Date().toISOString(); | ||
this._environment = process.env.TEST_ENVIRONMENT ? process.env.TEST_ENVIRONMENT : '-'; | ||
this._result = { | ||
@@ -51,2 +75,7 @@ tests: [], | ||
title: '', | ||
testRunName: `${process.env.BUILD_INFO ? process.env.BUILD_INFO : new Date().toISOString()} ${ | ||
process.env.TEST_ENVIRONMENT ? process.env.TEST_ENVIRONMENT : '-' | ||
}`, | ||
build: this._build, | ||
environment: this._environment, | ||
}; | ||
@@ -57,2 +86,10 @@ this._resultsData = results; | ||
public get build() { | ||
return this._build; | ||
} | ||
public get environment() { | ||
return this._environment; | ||
} | ||
async getParsedResults(): Promise<testRun> { | ||
@@ -62,2 +99,6 @@ return this._result; | ||
getRunStartTime(): number { | ||
return new Date(this._result.tests[0].startedAt).getTime() - 1000; | ||
} | ||
async parse() { | ||
@@ -64,0 +105,0 @@ for (const project of this._resultsData.suites) { |
@@ -8,2 +8,3 @@ import {AxiosResponse} from 'axios'; | ||
import {testStep} from './ResultsParser'; | ||
import {zebrunnerConfig} from './zebReporter'; | ||
@@ -23,11 +24,8 @@ export default class ZebAgent { | ||
constructor(config: {reporter: any[]}) { | ||
const zebRunnerConf = config.reporter.filter( | ||
(f) => f[0].includes('zeb') || f[1]?.includes('zeb') | ||
); | ||
constructor(config: zebrunnerConfig) { | ||
this._accessToken = process.env.ZEB_API_KEY; | ||
this._projectKey = zebRunnerConf[0][1].projectKey; | ||
this._reportBaseUrl = zebRunnerConf[0][1].reporterBaseUrl; | ||
this._projectKey = config.projectKey; | ||
this._reportBaseUrl = config.reporterBaseUrl; | ||
if (zebRunnerConf[0][1].enabled) { | ||
if (config.enabled) { | ||
this._enabled = true; | ||
@@ -38,3 +36,3 @@ } else { | ||
this._concurrentTasks = zebRunnerConf[0][1].concurrentTasks || this._defaultConcurrentTask; | ||
this._concurrentTasks = config.concurrentTasks || this._defaultConcurrentTask; | ||
@@ -41,0 +39,0 @@ if (this._concurrentTasks > this._maximumConcurrentTask) { |
// playwright.config.ts | ||
import {FullConfig, Reporter, Suite} from '@playwright/test/reporter'; | ||
import ZebAgent from './ZebAgent'; | ||
import ResultsParser, {testResult, testRun, testSuite} from './ResultsParser'; | ||
import ResultsParser, {testResult, testRun} from './ResultsParser'; | ||
import {PromisePool} from '@supercharge/promise-pool'; | ||
import SlackReporter from './SlackReporter'; | ||
export type zebrunnerConfig = { | ||
projectKey: string; | ||
reporterBaseUrl: string; | ||
enabled: boolean; | ||
concurrentTasks: number; | ||
slackEnabled: boolean; | ||
slackReportOnlyOnFailures: boolean; | ||
slackDisplayNumberOfFailures: number; | ||
slackReportingChannels: string; | ||
slackStacktraceLength: number; | ||
}; | ||
class ZebRunnerReporter implements Reporter { | ||
private config!: FullConfig; | ||
private suite!: Suite; | ||
private zebConfig: zebrunnerConfig; | ||
private zebAgent: ZebAgent; | ||
private slackReporter: SlackReporter; | ||
private testRunId: number; | ||
onBegin(config: FullConfig, suite: Suite) { | ||
const configKeys = config.reporter.filter((f) => f[0].includes('zeb') || f[1]?.includes('zeb')); | ||
this.zebConfig = { | ||
projectKey: configKeys[0][1].projectKey, | ||
reporterBaseUrl: configKeys[0][1].reporterBaseUrl, | ||
enabled: configKeys[0][1].enabled, | ||
concurrentTasks: configKeys[0][1].concurrentTasks, | ||
slackEnabled: configKeys[0][1].slackEnabled, | ||
slackReportOnlyOnFailures: configKeys[0][1].slackReportOnlyOnFailures, | ||
slackDisplayNumberOfFailures: configKeys[0][1].slackDisplayNumberOfFailures, | ||
slackReportingChannels: configKeys[0][1].slackReportingChannels, | ||
slackStacktraceLength: configKeys[0][1].slackStacktraceLength, | ||
}; | ||
this.config = config; | ||
this.suite = suite; | ||
this.zebAgent = new ZebAgent(this.config); | ||
this.zebAgent = new ZebAgent(this.zebConfig); | ||
} | ||
@@ -26,3 +53,3 @@ | ||
let resultsParser = new ResultsParser(this.suite); | ||
let resultsParser = new ResultsParser(this.suite, this.zebConfig); | ||
await resultsParser.parse(); | ||
@@ -32,3 +59,9 @@ let parsedResults = await resultsParser.getParsedResults(); | ||
console.time('Duration'); | ||
let zebrunnerResults = await this.postResultsToZebRunner(parsedResults); | ||
let zebrunnerResults = await this.postResultsToZebRunner( | ||
resultsParser.getRunStartTime(), | ||
parsedResults | ||
); | ||
const slackResults = zebrunnerResults.testsExecutions.results; | ||
delete zebrunnerResults.testsExecutions.results; // omit results from printing | ||
console.log(zebrunnerResults); | ||
@@ -41,13 +74,22 @@ console.log( | ||
console.timeEnd('Duration'); | ||
// post to Slack (if enabled) | ||
this.slackReporter = new SlackReporter(this.zebConfig); | ||
if (this.slackReporter.isEnabled) { | ||
let testSummary = await this.slackReporter.getSummaryResults( | ||
this.testRunId, | ||
slackResults, | ||
resultsParser.build, | ||
resultsParser.environment | ||
); | ||
await this.slackReporter.sendMessage(testSummary, zebrunnerResults.resultsLink); | ||
} | ||
} | ||
async postResultsToZebRunner(testResults: testRun) { | ||
let runStartTime = new Date(testResults.tests[0].startedAt).getTime() - 1000; | ||
let testRunName = `${ | ||
process.env.BUILD_INFO ? process.env.BUILD_INFO : new Date().toISOString() | ||
} ${process.env.TEST_ENVIRONMENT ? process.env.TEST_ENVIRONMENT : '-'}`; | ||
let testRunId = await this.startTestRuns(runStartTime, testRunName); | ||
console.log('testRuns >>', testRunId); | ||
async postResultsToZebRunner(runStartTime: number, testResults: testRun) { | ||
let testRunName = testResults.testRunName; | ||
await this.startTestRuns(runStartTime, testRunName); | ||
console.log('testRuns >>', this.testRunId); | ||
let testRunTags = await this.addTestRunTags(testRunId, [ | ||
let testRunTags = await this.addTestRunTags(this.testRunId, [ | ||
{ | ||
@@ -59,13 +101,16 @@ key: 'group', | ||
let testsExecutions = await this.startTestExecutions(testRunId, testResults.tests); | ||
let testTags = await this.addTestTags(testRunId, testsExecutions.results); | ||
let screenshots = await this.addScreenshots(testRunId, testsExecutions.results); | ||
let testSteps = await this.sendTestSteps(testRunId, testsExecutions.results); | ||
let endTestExecutions = await this.finishTestExecutions(testRunId, testsExecutions.results); | ||
let testsExecutions = await this.startTestExecutions(this.testRunId, testResults.tests); | ||
let testTags = await this.addTestTags(this.testRunId, testsExecutions.results); | ||
let screenshots = await this.addScreenshots(this.testRunId, testsExecutions.results); | ||
let testSteps = await this.sendTestSteps(this.testRunId, testsExecutions.results); | ||
let endTestExecutions = await this.finishTestExecutions( | ||
this.testRunId, | ||
testsExecutions.results | ||
); | ||
let testSessions = await this.sendTestSessions( | ||
testRunId, | ||
this.testRunId, | ||
runStartTime, | ||
testsExecutions.results | ||
); | ||
let stopTestRunsResult = await this.stopTestRuns(testRunId, new Date().toISOString()); | ||
let stopTestRunsResult = await this.stopTestRuns(this.testRunId, new Date().toISOString()); | ||
@@ -76,2 +121,3 @@ let summary = { | ||
errors: testsExecutions.errors.length, | ||
results: testsExecutions.results, | ||
}, | ||
@@ -106,3 +152,3 @@ testRunTags: { | ||
}, | ||
resultsLink: testRunId | ||
resultsLink: this.testRunId | ||
? `${this.zebAgent.baseUrl}/projects/${this.zebAgent.projectKey}/test-runs/${this.testRunId}` | ||
@@ -109,0 +155,0 @@ : '', |
@@ -100,3 +100,3 @@ import {test as base, expect} from '@playwright/test'; | ||
parsedResults: async ({testData}, use) => { | ||
let resultsParser = new ResultsParser(testData); | ||
let resultsParser = new ResultsParser(testData, null); | ||
await resultsParser.parse(); | ||
@@ -103,0 +103,0 @@ let r = await resultsParser.getParsedResults(); |
@@ -155,6 +155,7 @@ import {test, expect} from '@playwright/test'; | ||
let resultsParser = new ResultsParser(testData); | ||
let resultsParser = new ResultsParser(testData, null); | ||
await resultsParser.parse(); | ||
let parsedResults = await resultsParser.getParsedResults(); | ||
let result = await zeb.postResultsToZebRunner(parsedResults); | ||
let result = await zeb.postResultsToZebRunner(1000, parsedResults); | ||
delete result.testsExecutions.results; | ||
expect(result).toEqual({ | ||
@@ -161,0 +162,0 @@ testsExecutions: { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
546271
55
3674
112
23
5