Skip to content

Commit

Permalink
Initial commit of code pushed to npmjs as version 1.0.6
Browse files Browse the repository at this point in the history
  • Loading branch information
awaragi committed Feb 22, 2017
1 parent 1db7bf3 commit 6e1f406
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
node-debug*
.c9/
*.iml
.idea/
npm-debug.log
dist
6 changes: 6 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.iml
.idea/
tsconfig.json
src/
dist/test
*.map
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
# mocha-testrail-reporter
#Testrails Reporter for Mocha

Pushes test results into TestRail system.

## Installation

```shell
$ npm install mocha-testrail-reporter --save-dev
```

## Usage
Run mocha with `mocha-testrail-reporter`:

```shell
$ mocha test --reporter mocha-testrail-reporter --reporter-options domain=example,[email protected],password=12345678,projectId=1,suiteId=1
```
all reporter-options are mandatory except the following:

- assignedToId


Reference: https://github.com/michaelleeallen/mocha-testrail-reporter/blob/master/index.js
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("./dist/lib/mocha-testrail-reporter").MochaTestRailReporter
46 changes: 46 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "mocha-testrail-reporter",
"version": "1.0.6",
"description": "A Testrails reporter for mocha.",
"main": "index.js",
"private": false,
"keywords": [
"mocha",
"testrail",
"reporter"
],
"author": {
"name": "Pierre Awaragi",
"email": "[email protected]"
},
"license": "MIT",
"readmeFilename": "README.md",
"repository": {
"type": "git",
"url": "https://github.com/awaragi/mocha-testrail-reporter.git"
},
"bugs": {
"url": "https://github.com/awaragi/mocha-testrail-reporter/issues"
},
"scripts": {
"tsc": "tsc",
"clean": "rimraf dist",
"test": "mocha dist/test",
"build": "npm run clean && npm run tsc"
},
"dependencies": {
"btoa": "^1.1.2",
"unirest": "^0.5.1"
},
"peerDependencies": {
"mocha": "^3 || ^2.2.5"
},
"devDependencies": {
"@types/chai": "^3.4.34",
"@types/mocha": "^2.2.36",
"@types/node": "^6.0.58",
"chai": "^3.5.0",
"rimraf": "^2.5.4",
"typescript": "^2.1.4"
}
}
97 changes: 97 additions & 0 deletions src/lib/mocha-testrail-reporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {reporters} from 'mocha';
import {TestRail} from "./testrail";
import {titleToCaseIds} from "./shared";


export class MochaTestRailReporter extends reporters.Spec {
private testCases: TestCase[] = [];
private passes: number = 0;
private fails: number = 0;
private pending: number = 0;
private out: string[] = [];

constructor(runner: any, options: any) {
super(runner);

let reporterOptions: TestRailOptions = <TestRailOptions>options.reporterOptions;
this.validate(reporterOptions, 'domain');
this.validate(reporterOptions, 'username');
this.validate(reporterOptions, 'password');
this.validate(reporterOptions, 'projectId');
this.validate(reporterOptions, 'suiteId');

runner.on('start', () => {
});

runner.on('suite', (suite) => {
});

runner.on('suite end', () => {
});

runner.on('pending', (test) => {
this.pending++;
this.out.push(test.fullTitle() + ': pending');
});

runner.on('pass', (test) => {
this.passes++;
this.out.push(test.fullTitle() + ': pass');
let caseIds = titleToCaseIds(test.title);
if (caseIds.length > 0) {
if (test.speed === 'fast') {
let testCases = caseIds.map(caseId => {
return {
caseId: caseId,
pass: true,
comment: test.title
};
});
this.testCases.push(...testCases);
} else {
let testCases = caseIds.map(caseId => {
return {
caseId: caseId,
pass: true,
comment: `${test.title} (${test.duration}ms)`
};
});
this.testCases.push(...testCases);
}
}
});

runner.on('fail', (test) => {
this.fails++;
this.out.push(test.fullTitle() + ': fail');
let caseIds = titleToCaseIds(test.title);
if (caseIds.length > 0) {
let testCases = caseIds.map(caseId => {
return {
caseId: caseId,
pass: false,
comment: `${test.title}
${test.err}`
};
});
this.testCases.push(...testCases);
}
});

runner.on('end', () => {
if (this.testCases.length == 0) {
console.warn("No testcases were matched. Ensure that your tests are declared correctly and matches TCxxx");
}
new TestRail(reporterOptions).publish(this.passes, this.fails, this.pending, this.out, this.testCases);
});
}

private validate(options: TestRailOptions, name: string) {
if (options == null) {
throw new Error("Missing --reporter-options in mocha.opts");
}
if (options[name] == null) {
throw new Error("Missing ${name} value. Please update --reporter-options in mocha.opts");
}
}
}
17 changes: 17 additions & 0 deletions src/lib/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Search for all applicable test cases
* @param title
* @returns {any}
*/
export function titleToCaseIds(title: string): number[] {
let caseIds: number[] = [];

let testCaseIdRegExp: RegExp = /\bT?C(\d+)\b/g;
let m;
while((m = testCaseIdRegExp.exec(title)) !== null) {
let caseId = parseInt(m[1]);
caseIds.push(caseId);
}
return caseIds;
}

5 changes: 5 additions & 0 deletions src/lib/test.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
interface TestCase {
caseId: number,
pass: boolean,
comment?: String,
}
8 changes: 8 additions & 0 deletions src/lib/testrail-options.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
interface TestRailOptions {
domain: string,
username: string,
password: string,
projectId: number,
suiteId: number,
assignedToId?: number,
}
75 changes: 75 additions & 0 deletions src/lib/testrail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import btoa = require('btoa');
import unirest = require("unirest");

export class TestRail {
private base: String;

constructor(private options: TestRailOptions) {
// compute base url
this.base = `https://${options.domain}/index.php`;
}

private _post(api: String, body: any, callback: Function, error?: Function) {
var req = unirest("POST", this.base)
.query(`/api/v2/${api}`)
.headers({
"content-type": "application/json"
})
.type("json")
.send(body)
.auth(this.options.username, this.options.password)
.end((res) => {
if (res.error) {
console.log("Error: %s", JSON.stringify(res.body));
if (error) {
error(res.error);
} else {
throw new Error(res.error);
}
}
callback(res.body);
});
}

publish(passes: number, fails: number, pending: number, out: string[], tests: TestCase[], callback?: Function): void {
let total = passes + fails + pending;
let results: any = [];
for (let test of tests) {
results.push({
"case_id": test.caseId,
"status_id": test.pass ? 1 : 5,
"comment": test.comment ? test.comment: "",
});
}

console.log(`Publishing ${results.length} test result(s) to ${this.base}`)
let executionDateTime = new Date().toISOString();
this._post(`add_run/${this.options.projectId}`, {
"suite_id": this.options.suiteId,
"name": `Automated test run ${executionDateTime}`,
"description": `Automated test run executed on ${executionDateTime}
Execution summary:
Passes: ${passes}
Fails: ${fails}
Pending: ${pending}
Total: ${total}
Execution details:
${out.join('\n')}
`,
"assignedto_id": this.options.assignedToId,
"include_all": true
}, (body) => {
const runId = body.id
console.log(`Results published to ${this.base}?/runs/view/${runId}`)
this._post(`add_results_for_cases/${runId}`, {
results: results
}, (body) => {
// execute callback if specified
if(callback) {
callback();
}
})
});
}
}
38 changes: 38 additions & 0 deletions src/test/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as chai from "chai";
chai.should();

import {titleToCaseIds} from "../lib/shared";

describe("Shared functions", () => {
describe("titleToCaseIds", () => {
it("Single test case id present", () => {
let caseIds = titleToCaseIds("TC123 Test title");
caseIds.length.should.be.equals(1);
caseIds[0].should.be.equals(123);

caseIds = titleToCaseIds("Execution of TC123 Test title");
caseIds.length.should.be.equals(1);
caseIds[0].should.be.equals(123);
});
it("Multiple test case ids present", () => {
let caseIds = titleToCaseIds("Execution TC321 TC123 Test title");
caseIds.length.should.be.equals(2);
caseIds[0].should.be.equals(321);
caseIds[1].should.be.equals(123);
});
it("No test case ids present", () => {
let caseIds = titleToCaseIds("Execution Test title");
caseIds.length.should.be.equals(0);
});
});

describe("Misc tests", () => {
it("String join", () => {
let out: string[] = [];
out.push("Test 1: fail");
out.push("Test 2: pass");
out.join('\n').should.be.equals(`Test 1: fail
Test 2: pass`);
});
});
});
23 changes: 23 additions & 0 deletions src/test/testrail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {TestRail} from "../lib/testrail";

describe.skip("TestRail API", () => {
it("Publish test run", (done) => {
new TestRail({
domain: "testingoutone",
username: "[email protected]",
password: "XyMp8uojG3wkzNNNXiTk-dP4MnBmOiQhVC2xGvmyY",
projectId: 1,
suiteId: 2,
assignedToId: 1,
}).publish(0, 0, 0, ["test 1: pass", "test 2: fail"], [
{
caseId: 74,
pass: true
}, {
caseId: 75,
pass: false,
comment: "Failure...."
}
], done);
})
});
19 changes: 19 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false,
"pretty": true,
"outDir": "dist",
"typeRoots": ["node_modules/@types"]
},
"exclude": [
"node_modules",
"dist"
]
}

0 comments on commit 6e1f406

Please sign in to comment.