Skip to content

Commit

Permalink
Merge pull request #77 from test-results-reporter/69-performance-jmet…
Browse files Browse the repository at this point in the history
…er-aggregate-csv

feat: support performance results in teams
  • Loading branch information
ASaiAnudeep authored Aug 15, 2022
2 parents 9c23552 + 4234c10 commit 6a38c42
Show file tree
Hide file tree
Showing 12 changed files with 1,016 additions and 942 deletions.
977 changes: 62 additions & 915 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "test-results-reporter",
"version": "1.0.8",
"version": "1.0.9",
"description": "Publish test results to Microsoft Teams, Google Chat and Slack",
"main": "src/index.js",
"types": "./src/index.d.ts",
Expand All @@ -11,8 +11,7 @@
"/src"
],
"scripts": {
"test": "mocha test",
"coverage": "nyc --reporter=lcov --reporter=text npm run test",
"test": "c8 mocha test",
"build": "pkg --out-path dist ."
},
"repository": {
Expand Down Expand Up @@ -45,15 +44,16 @@
"dependencies": {
"async-retry": "^1.3.3",
"dotenv": "^14.3.0",
"performance-results-parser": "0.0.3",
"phin-retry": "^1.0.3",
"pretty-ms": "^7.0.0",
"rosters": "0.0.1",
"sade": "^1.7.4",
"test-results-parser": "^0.1.0"
},
"devDependencies": {
"c8": "^7.12.0",
"mocha": "^10.0.0",
"nyc": "^15.1.0",
"pactum": "^3.1.10",
"pkg": "^5.8.0"
}
Expand Down
21 changes: 15 additions & 6 deletions src/commands/publish.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
const path = require('path');
const { parse } = require('test-results-parser');
const trp = require('test-results-parser');
const prp = require('performance-results-parser');

const { processData } = require('../helpers/helper');
const target_manager = require('../targets');

/**
* @param {import('../index').PublishOptions} opts
*/
async function run(opts) {
if (typeof opts.config === 'string') {
const cwd = process.cwd();
opts.config = require(path.join(cwd, opts.config));
}
const config = processData(opts.config);
for (const report of config.reports) {
const results = [];
for (const result of report.results) {
results.push(parse(result));
const parsed_results = [];
for (const result_options of report.results) {
if (result_options.type === 'jmeter') {
parsed_results.push(prp.parse(result_options));
} else {
parsed_results.push(trp.parse(result_options));
}
}
for (let i = 0; i < results.length; i++) {
const result = results[i];
for (let i = 0; i < parsed_results.length; i++) {
const result = parsed_results[i];
for (const target of report.targets) {
await target_manager.run(target, result);
}
Expand Down
6 changes: 6 additions & 0 deletions src/helpers/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const pretty_ms = require('pretty-ms');

const DATA_REF_PATTERN = /(\{[^\}]+\})/g;
const ALLOWED_CONDITIONS = new Set(['pass', 'fail', 'passorfail']);
const GENERIC_CONDITIONS = new Set(['always', 'never']);

function getPercentage(x, y) {
if (y > 0) {
Expand Down Expand Up @@ -30,6 +31,9 @@ function processText(raw) {
return raw;
}

/**
* @returns {import('../index').PublishConfig }
*/
function processData(data) {
if (typeof data === 'string') {
return processText(data);
Expand Down Expand Up @@ -79,6 +83,8 @@ async function checkCondition({ condition, result, target, extension }) {
const lower_condition = condition.toLowerCase();
if (ALLOWED_CONDITIONS.has(lower_condition)) {
return lower_condition.includes(result.status.toLowerCase());
} else if (GENERIC_CONDITIONS.has(lower_condition)) {
return lower_condition === 'always';
} else {
return eval(condition);
}
Expand Down
140 changes: 140 additions & 0 deletions src/helpers/performance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
const Metric = require("performance-results-parser/src/models/Metric");
const { getPrettyDuration, checkCondition } = require("./helper");

/**
* @param {object} param0
* @param {Metric[]} param0.metrics
* @param {object} param0.result
* @param {object} param0.target
*/
async function getValidMetrics({ metrics, result, target }) {
if (target.inputs.metrics && target.inputs.metrics.length > 0) {
const valid_metrics = [];
for (let i = 0; i < metrics.length; i++) {
const metric = metrics[i];
for (let j = 0; j < target.inputs.metrics.length; j++) {
const metric_config = target.inputs.metrics[j];
if (metric.name === metric_config.name) {
const include = await checkCondition({ condition: metric_config.condition || 'always', result, target });
if (include) valid_metrics.push(metric);
}
}
}
return valid_metrics;
}
return metrics;
}

/**
* @param {Metric} metric
* @param {string[]} fields
*/
function getCounterMetricFieldValue(metric, fields) {
let value = '';
if (fields.includes('sum')) {
const sum_failure = metric.failures.find(_failure => _failure.field === 'sum');
if (sum_failure) {
const emoji = getEmoji(sum_failure.difference);
value = `${emoji} ${metric['sum']} (${getDifferenceSymbol(sum_failure.difference)}${sum_failure.difference}) `
} else {
value = `${metric['sum']} `;
}
}
if (fields.includes('rate')) {
let metric_unit = metric.unit.startsWith('/') ? metric.unit : ` ${metric.unit}`;
const rate_failure = metric.failures.find(_failure => _failure.field === 'rate');
if (rate_failure) {
const emoji = getEmoji(rate_failure.difference);
value += `${emoji} ${metric['rate']}${metric_unit} (${getDifferenceSymbol(rate_failure.difference)}${rate_failure.difference})`
} else {
value += `${metric['rate']}${metric_unit}`;
}
}
return value;
}

/**
* @param {Metric} metric
*/
function getTrendMetricFieldValue(metric, field) {
const failure = metric.failures.find(_failure => _failure.field === field);
if (failure) {
const emoji = getEmoji(failure.difference);
return `${emoji} ${field}=${getPrettyDuration(metric[field])} (${getDifferenceSymbol(failure.difference)}${getPrettyDuration(failure.difference)})`
}
return `${field}=${getPrettyDuration(metric[field])}`;
}

/**
* @param {Metric} metric
*/
function getRateMetricFieldValue(metric) {
const failure = metric.failures.find(_failure => _failure.field === 'rate');
if (failure) {
const emoji = getEmoji(failure.difference);
return `${emoji} ${metric['rate']} ${metric.unit} (${getDifferenceSymbol(failure.difference)}${failure.difference})`;
}
return `${metric['rate']} ${metric.unit}`;
}

function getEmoji(value) {
return value > 0 ? '🔺' : '🔻';
}

function getDifferenceSymbol(value) {
return value > 0 ? `+` : '';
}

/**
*
* @param {object} param0
* @param {Metric} param0.metric
*/
function getDisplayFields({ metric, target }) {
let fields = [];
if (target.inputs.metrics) {
const metric_config = target.inputs.metrics.find(_metric => _metric.name === metric.name);
if (metric_config) {
fields = metric_config.fields;
}
}
if (fields && fields.length > 0) {
return fields;
} else {
switch (metric.type) {
case 'COUNTER':
return ['sum', 'rate'];
case 'RATE':
return ['rate'];
case 'TREND':
return ['avg', 'min', 'med', 'max', 'p90', 'p95', 'p99'];
default:
return ['sum', 'min', 'max'];
}
}
}

/**
* @param {object} param0
* @param {Metric} param0.metric
*/
function getMetricValuesText({ metric, target }) {
const fields = getDisplayFields({ metric, target });
const values = [];
if (metric.type === 'COUNTER') {
values.push(getCounterMetricFieldValue(metric, fields));
} else if (metric.type === 'TREND') {
for (let i = 0; i < fields.length; i++) {
const field_metric = fields[i];
values.push(getTrendMetricFieldValue(metric, field_metric));
}
} else if (metric.type === 'RATE') {
values.push(getRateMetricFieldValue(metric));
}
return values.join(' | ');
}

module.exports = {
getValidMetrics,
getMetricValuesText
}
16 changes: 12 additions & 4 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { User, Schedule } from 'rosters';
import TestResult from 'test-results-parser/src/models/TestResult';
import { PerformanceParseOptions } from 'performance-results-parser';

export type ExtensionName = 'report-portal-analysis' | 'hyperlinks' | 'mentions' | 'report-portal-history' | 'quick-chart-test-summary' | 'custom';
export type Hook = 'start' | 'end';
Expand All @@ -12,7 +13,7 @@ export interface ConditionFunctionContext {
result: TestResult;
}
export type ConditionFunction = (ctx: ConditionFunctionContext) => boolean | Promise<boolean>;
export type Condition = 'pass' | 'fail' | 'passOrFail' | ConditionFunction;
export type Condition = 'pass' | 'fail' | 'passOrFail' | 'always' | 'never' | ConditionFunction;

/**
* Extensions
Expand Down Expand Up @@ -123,6 +124,12 @@ export interface HyperlinksExtension extends Extension {
* Targets
*/

export interface MetricConfig {
name: string;
condition: Condition;
fields: string[];
}

export interface TargetInputs {
url: string;
title?: string;
Expand All @@ -131,15 +138,16 @@ export interface TargetInputs {
duration?: string;
publish?: PublishReportType;
only_failures?: boolean;
metrics?: MetricConfig[];
}

export interface SlackInputs extends TargetInputs {}
export interface SlackInputs extends TargetInputs { }

export interface TeamsInputs extends TargetInputs {
width?: string;
}

export interface ChatInputs extends TargetInputs {}
export interface ChatInputs extends TargetInputs { }

export interface CustomTargetFunctionContext {
target: Target;
Expand All @@ -166,7 +174,7 @@ export interface PublishResult {

export interface PublishReport {
targets: Target[];
results: PublishResult[];
results: PublishResult[] | PerformanceParseOptions[];
}

export interface PublishConfig {
Expand Down
4 changes: 2 additions & 2 deletions src/targets/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function setSuiteBlock({ result, target, payload }) {
let texts = [];
for (let i = 0; i < result.suites.length; i++) {
const suite = result.suites[i];
if (target.inputs.only_failure_suites && suite.status !== 'FAIL') {
if (target.inputs.only_failures && suite.status !== 'FAIL') {
continue;
}
// if suites length eq to 1 then main block will include suite summary
Expand Down Expand Up @@ -125,7 +125,7 @@ const default_options = {
const default_inputs = {
publish: 'test-summary',
include_suites: true,
only_failure_suites: false,
only_failures: false,
include_failure_details: false,
duration: 'colonNotation'
};
Expand Down
4 changes: 2 additions & 2 deletions src/targets/slack.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function setSuiteBlock({ result, target, payload }) {
if (target.inputs.include_suites) {
for (let i = 0; i < result.suites.length; i++) {
const suite = result.suites[i];
if (target.inputs.only_failure_suites && suite.status !== 'FAIL') {
if (target.inputs.only_failures && suite.status !== 'FAIL') {
continue;
}
// if suites length eq to 1 then main block will include suite summary
Expand Down Expand Up @@ -153,7 +153,7 @@ const default_options = {
const default_inputs = {
publish: 'test-summary',
include_suites: true,
only_failure_suites: false,
only_failures: false,
include_failure_details: false,
duration: 'colonNotation'
}
Expand Down
Loading

0 comments on commit 6a38c42

Please sign in to comment.