Skip to content

Commit

Permalink
Added a whole structure
Browse files Browse the repository at this point in the history
  • Loading branch information
emiride committed Jul 6, 2022
1 parent 0745120 commit 32f950a
Show file tree
Hide file tree
Showing 15 changed files with 21,086 additions and 0 deletions.
102 changes: 102 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# Next.js build output
.next


# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port
.DS_Store
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/index.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#Sending JUnit report to Slack
15 changes: 15 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: 'xUnit XML to Slack'
description: 'Send xUnit-like xml report to Slack'
branding:
icon: "bell"
color: "green"
inputs:
slack-webhook-url:
description: 'Webhook URL for Slack'
required: true
directory-path:
description: 'Directory path to JUnit XML file'
required: true
runs:
using: 'node16'
main: 'dist/index.js'
13 changes: 13 additions & 0 deletions app/action-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import * as github from '@actions/github';

export default class ActionInfo{
workflowName: string;
runUrl: string;
buildUrl: string;
constructor(){
this.workflowName = github.context.workflow;
this.runUrl = `${github.context.serverUrl}/${github.context.repo.owner}/${github.context.repo.repo}/actions/runs/${github.context.runId}`;
this.buildUrl = `${github.context.serverUrl}/${github.context.repo.owner}/${github.context.repo.repo}/runs/${github.context.runNumber}`;
}
}
73 changes: 73 additions & 0 deletions app/results-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as fs from 'fs';
import { parse } from 'junit2json';

class TestResult {
name: string;
status: string;
time: string;
constructor(name: string, status: string, time: string) {
this.name = name;
this.status = status;
this.time = time;
}
}

export default class ResultsParser {
filePath: string;
passedTests: number;
failedTests: number;
skippedTests: number;
executionTime: string;
failedTestsList: string[];
constructor(filePath: string) {
this.filePath = filePath;
this.passedTests = 0;
this.failedTests = 0;
this.skippedTests = 0;
this.executionTime = "";
this.failedTestsList = [];
}

async parse() {
const file = fs.readFileSync(this.filePath, 'utf8');
const output = await parse(file);
const timeSeconds = Math.floor(output["time"]);
this.executionTime = Math.floor(timeSeconds/60) + "m " + timeSeconds%60 + "s";
const testResults = this.extract(output["testsuite"]);
for (let testResult of testResults) {
if (testResult.status === "failed") {
this.failedTests++;
this.failedTestsList.push(testResult.name);
} else if (testResult.status === "skipped") {
this.skippedTests++;
} else if (testResult.status === "passed") {
this.passedTests++;
}
}
}

private extract(testsuites): TestResult[] {
const testResults: TestResult[] = [];
for (let suite of testsuites) {
for (let test of suite["testcase"]){
const testName = test["name"];
const testTime = test["time"];
const testStatus = this.getTestStatus(test);
testResults.push(new TestResult(testName, testStatus, testTime));
}
}
return testResults;
}

private getTestStatus(testJson){
if(testJson["failure"] !== undefined){
return "failed";
}
else if(testJson["skipped"] !== undefined){
return "skipped";
}
else{
return "passed";
}
}
}
106 changes: 106 additions & 0 deletions app/slack-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//@ts-check
import { IncomingWebhook } from '@slack/webhook';
import ActionInfo from './action-info';
import ResultsParser from './results-parser';

export default class SlackMessage {
testResults: ResultsParser;
constructor(testResults: ResultsParser) {
this.testResults = testResults;
}

async send(slackWebhookUrl: string, actionInfo: ActionInfo): Promise<void> {
const webhook = new IncomingWebhook(slackWebhookUrl);
const blocks = this.getBlocks(this.testResults, actionInfo);
await webhook.send({ text: `${actionInfo.workflowName} - ${this.testResults.failedTests > 0 ? "Failed": "Passed"}`, blocks: JSON.parse(blocks) });
}

private getFailedTestsSections(failed, failedTestsList: string[]): string {
const template = (testName: string, isFailed: boolean) => `{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${isFailed ? ":red_circle: " + testName : ":tada: *ALL PASSED*"} "
}
},`;
if (!failed) {
return template("", false);
}
else{
return failedTestsList.map(testName => template(testName, true)).join("\n");
}
}

private getOverralTestsSection(passedTests, skippedTests, failedTests): string {
const passedSubstring = passedTests > 0 ? `:large_green_circle: *PASSED: ${passedTests}*` : "";
const failedSubstring = failedTests > 0 ? `:red_circle: *FAILED: ${failedTests}*` : "";
const skippedSubstring = skippedTests > 0 ? `:white_circle: *SKIPPED: ${skippedTests}*` : "";
const template = (passedTests, skippedTests, failedTests) => `{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${passedSubstring} ${failedSubstring} ${skippedSubstring}"
}
},`;
return template(passedTests, skippedTests, failedTests);

}

getBlocks(testResults: ResultsParser, actionInfo: ActionInfo): string {
const failedTests = testResults.failedTests;
const skippedTests = testResults.skippedTests;
const passedTests = testResults.passedTests;
const failedTestsList = testResults.failedTestsList;
const failed = failedTests > 0;
const failedTestsSections = this.getFailedTestsSections(failed, failedTestsList);
const overralTestsSection = this.getOverralTestsSection(passedTests, skippedTests, failedTests);
const nesta = `
[
{
"type": "context",
"elements": [
{
"type": "plain_text",
"text": "Action: ${actionInfo.workflowName}",
"emoji": true
}
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":clock1: *Execution time:* ${testResults.executionTime}"
}
},
${overralTestsSection}
{
"type": "divider"
},
${failedTestsSections}
{
"type": "divider"
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Go to Action",
"emoji": true
},
"value": "action_go",
"url": "${actionInfo.runUrl}"
}
]
}
]
`
return nesta;
}
}
Loading

0 comments on commit 32f950a

Please sign in to comment.