Skip to content

Commit

Permalink
Slack mock type script (#52)
Browse files Browse the repository at this point in the history
* add slack mock
* update slack mock debugger
* tsconfig

* publish v0.2.6
  • Loading branch information
YOU54F authored May 6, 2019
1 parent 6375ca0 commit 1147f20
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 517 deletions.
2 changes: 1 addition & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ yarn.lock
.scannerwork
src
samples
fff
config.env.test
1 change: 1 addition & 0 deletions config.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ CIRCLE_PULL_REQUEST=https://github.com/YOU54F/cypress-slack-reporter/pull/45
CIRCLE_REPOSITORY_URL=git@github.com:YOU54F/cypress-slack-reporter.git
CIRCLE_SHA1=e9c38c91f9844b853406b089687eea89fbeb4bc9
CIRCLE_USERNAME=YOU54F
LOG_LEVEL=debug
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cypress-slack-reporter",
"version": "0.2.5",
"version": "0.2.6",
"description": "A slack reporter for mochawesome reports generated by Cypress or other test frameworks using Mocha, for runs generated on CircleCI",
"main": "./bin/index.js",
"bin": {
Expand Down Expand Up @@ -51,7 +51,9 @@
"@cypress/webpack-preprocessor": "4.0.3",
"@types/dotenv": "6.1.1",
"@types/jest": "24.0.12",
"@types/nock": "^10.0.1",
"@types/node": "10.14.4",
"@types/qs": "^6.5.3",
"cypress": "3.2.0",
"cypress-cucumber-preprocessor": "1.11.0",
"cypress-testing-library": "3.0.1",
Expand All @@ -67,17 +69,19 @@
"mochawesome": "3.1.2",
"mochawesome-merge": "1.0.7",
"mochawesome-report-generator": "3.1.5",
"nock": "^10.0.6",
"prettier": "1.17.0",
"qs": "^6.7.0",
"rimraf": "2.6.3",
"slack-mocker": "1.2.7",
"ts-jest": "24.0.2",
"ts-loader": "5.4.5",
"ts-node": "8.1.0",
"tslint": "5.16.0",
"tslint-config-prettier": "1.18.0",
"tslint-no-focused-test": "0.5.0",
"typescript": "3.4.5",
"webpack": "4.30.0"
"webpack": "4.30.0",
"winston": "^2.3.0"
},
"cypress-cucumber-preprocessor": {
"nonGlobalStepDefinitions": false
Expand Down
79 changes: 33 additions & 46 deletions src/slack/test/slack-alert-mocked.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "jest";
import * as SlackMock from "slack-mocker";
// import "jest";
import * as SlackMock from "../../slackMock/slacker";
import * as slacker from "../slack-alert";

const base = process.env.PWD || ".";
Expand All @@ -10,44 +10,24 @@ const videoDirectory: string = base + "/src/slack/test/videosDirPopulated";
const screenshotDirectory: string =
base + "/src/slack/test/screenshotDirPopulated";
const logger: boolean = false;
let mock: SlackMock.Instance;
let mock: any;

function setup() {
beforeAll(async () => {
jest.setTimeout(60000);
mock = await SlackMock({ disableRtm: true });
await mock.incomingWebhooks.reset();
mock = await SlackMock.incomingWebhooks;
});

beforeEach(async () => {
jest.resetModules();
await mock.incomingWebhooks.reset();
expect(mock.incomingWebhooks.calls).toHaveLength(0);
await mock.reset();
expect(mock.calls).toHaveLength(0);
});
afterEach(async () => {
await mock.incomingWebhooks.reset();
await mock.shutdown();
});
}

describe("tester", () => {
setup();
it("can provide a simple report with an unknown vcsroot provider", async () => {
const _vcsRoot = "none";
await slacker.slackRunner(
ciProvider,
_vcsRoot,
reportDirectory,
videoDirectory,
screenshotDirectory,
logger
);
const body = await returnSlackWebhookCall();
const messageBuiltUrl = await messageBuildURL(body);
expect(body).not.toContain("commits");
expect(body).not.toContain("artefacts");
});
});

describe("tester", () => {
setup();

Expand All @@ -63,7 +43,7 @@ describe("tester", () => {
logger
);
const body = await returnSlackWebhookCall();
const messageBuiltUrl = await messageBuildURL(body);

expect(body).toContain("bitbucket");
expect(body).not.toContain("undefined");
});
Expand All @@ -82,7 +62,7 @@ describe("tester", () => {
logger
);
const body = await returnSlackWebhookCall();
const messageBuiltUrl = await messageBuildURL(body);

const buildNum = process.env.CIRCLE_BUILD_NUM;
expect(body).toContain(
`"text":"CircleCI Logs","url":"https://circleci.com/gh/YOU54F/cypress-slack-reporter/${buildNum}"`
Expand All @@ -102,7 +82,7 @@ describe("tester", () => {
logger
);
const body = await returnSlackWebhookCall();
const messageBuiltUrl = await messageBuildURL(body);

checkStatus(body, "passed");
expect(body).toContain("github");
expect(body).not.toContain("undefined");
Expand All @@ -122,7 +102,7 @@ describe("tester", () => {
logger
);
const body = await returnSlackWebhookCall();
const messageBuiltUrl = await messageBuildURL(body);

checkStatus(body, "failed");
expect(body).toContain("github");
expect(body).not.toContain("undefined");
Expand All @@ -141,7 +121,7 @@ describe("tester", () => {
logger
);
const body = await returnSlackWebhookCall();
const messageBuiltUrl = await messageBuildURL(body);

checkStatus(body, "build");
expect(body).toContain("github");
expect(body).not.toContain("undefined");
Expand All @@ -165,28 +145,35 @@ describe("Slack Reporter throws error if we cant find the test report", () => {
});
});

describe("tester", () => {
setup();
it("can provide a simple report with an unknown vcsroot provider", async () => {
const _vcsRoot = "none";
await slacker.slackRunner(
ciProvider,
_vcsRoot,
reportDirectory,
videoDirectory,
screenshotDirectory,
logger
);
const body = await returnSlackWebhookCall();

expect(body).not.toContain("commits");
expect(body).not.toContain("artefacts");
});
});

function returnSlackWebhookCall() {
// This checks the slack mock call counter
expect(mock.incomingWebhooks.calls).toHaveLength(1);
expect(mock.calls).toHaveLength(1);
// Load the response as json
const firstCall = mock.incomingWebhooks.calls[0];
const firstCall = mock.calls[0];
// check our webhook url called in ENV var SLACK_WEBHOOK_URL
expect(firstCall.url).toEqual(process.env.SLACK_WEBHOOK_URL);
const body = firstCall.params;
// tslint:disable-next-line: no-console
console.log(body);
return body;
}
function messageBuildURL(body: string) {
// build a URL to check the message renders
const mbTestUrlBase = "https://api.slack.com/docs/messages/builder?msg=";
// encode our json message request into a URL encoded string
const encodedBody = encodeURIComponent(body);
const mbTestUrl = `${mbTestUrlBase}${encodedBody}`;
// tslint:disable-next-line: no-console
console.log(mbTestUrl);
return mbTestUrl;
}

function checkStatus(body: string, status: string) {
expect(body).not.toContain("undefined");
Expand Down
61 changes: 61 additions & 0 deletions src/slackMock/custom-responses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"use strict";

import { logger } from "./logger";

const allResponses = new Map();

allResponses.set("incoming-webhooks", new Map());

export function set(
type: string,
opts: { url: string; statusCode: any; body: any; headers: any }
) {
const typeResponses = allResponses.get(type);
if (!opts.url) {
opts.url = "any";
}

let urlResponses = typeResponses.get(opts.url);

if (!urlResponses) {
urlResponses = [];
typeResponses.set(opts.url, urlResponses);
}

logger.debug(`added response for ${type}`, opts);

urlResponses.push({
statusCode: opts.statusCode || 200,
body: opts.body || (type === "web" ? { ok: true } : "OK"),
headers: opts.headers || {}
});
}

export function get(type: string, url: string) {
const defaultResponse = { statusCode: 200, body: "OK", headers: {} };
let response = defaultResponse;

let urlResponses = allResponses.get(type).get(url);
if (!urlResponses) {
urlResponses = allResponses.get(type).get("any");
}

if (urlResponses && urlResponses.length) {
response = urlResponses.shift();
logger.debug(`responding to ${type} with override`, response);
}

return [response.statusCode, response.body, response.headers];
}

export function reset(type: string) {
logger.debug(`clearing responses for ${type}`);
allResponses.get(type).clear();
}

export function resetAll() {
for (const key of allResponses.keys()) {
logger.debug(`clearing responses for ${key}`);
allResponses.get(key).clear();
}
}
20 changes: 20 additions & 0 deletions src/slackMock/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Logger, LoggerInstance, LoggerOptions, transports } from "winston";

const defaultLevel = process.env.LOG_LEVEL;

const options: LoggerOptions = {
handleExceptions: true,
humanReadableUnhandledException: true,
level: defaultLevel,
transports: [
new transports.Console({
colorize: true,
showLevel: true,
timestamp: true
})
]
};

const logger: LoggerInstance = new Logger(options);

export { logger };
72 changes: 72 additions & 0 deletions src/slackMock/slacker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"use strict";

export const incomingWebhooks = module.exports as IncomingWebhooks<[]>;
import * as nock from "nock";
import * as customResponses from "./custom-responses";
import { logger } from "./logger";
import parseParams from "./utils";
const baseUrl = "https://hooks.slack.com";

type IncomingWebhookUrl = string;
type IncomingWebhookHttpHeaders = nock.HttpHeaders;

// Slack accepts both GET and POST requests, will intercept API and OAuth calls

nock(baseUrl)
.persist()
.post(/.*/, () => true)
.reply(reply);

incomingWebhooks.calls = [];

incomingWebhooks.reset = () => {
logger.debug(`resetting incoming-webhooks`);

customResponses.reset("incoming-webhooks");
incomingWebhooks.calls.splice(0, incomingWebhooks.calls.length);
};

incomingWebhooks.addResponse = opts => {
logger.debug(`adding incoming-webhook response` + opts);
customResponses.set("incoming-webhooks", opts);
};

incomingWebhooks.shutdown = () => {
logger.debug(`shutting down incoming-webhooks`);
nock(baseUrl).done();
};

function reply(path: string, requestBody: string) {
const url = `${baseUrl}${path}`;

logger.debug(`intercepted incoming-webhooks request`);

incomingWebhooks.calls.push({
url,
params: parseParams(path, requestBody) as [],
headers: {}
});

return customResponses.get("incoming-webhooks", url) as Array<{}>;
}

interface IncomingWebhooks<T> {
addResponse: (opts: IncomingWebhookOptions<T>) => void;
reset: () => void;
start: () => void;
shutdown: () => void;
calls: Array<IncomingWebhookCall<T>>;
}

interface IncomingWebhookOptions<T> {
url: IncomingWebhookUrl;
statusCode: number;
body: T;
headers: IncomingWebhookHttpHeaders;
}

interface IncomingWebhookCall<T> {
url: IncomingWebhookUrl;
params: T;
headers: IncomingWebhookHttpHeaders;
}
Loading

0 comments on commit 1147f20

Please sign in to comment.