Skip to content

Commit

Permalink
test: convert to typescript and use Jest (#188)
Browse files Browse the repository at this point in the history
* chore(tests): Convert to typescript and use Jest

* chore(lint): Run prettier

* fix: Add missing closing bracket

* chore(lint): Run Prettier

* refactor(tests): Replace some tap with jest equivalents

- Replace `t.ok()` with `expect().toBeTruthy()`
- Replace `t.plan()` with `expect.assertions()`

* chore(package): Add types for simple-mock

* fix(typescript): Make sure to use the simple-mock types

* chore(tests): Migrate integration tests to TypeScript + Jest

* chore(tests): Finish most of the Jest transition

* fix(typescript): Fix some type errors

* fix(tests): Some more fixes

* fix(tests): Get tests to run

* fix(tests): Fix some jest errors

Jest expects functions in the `expect()` function when using the `.toThrow()` property

* fix(tests): Fix some type errors

* refactor: Remove un-needed argument

* fix(tests): Fix various problems in the tests

Fix some types
Replace some remaining tap functions
Ignore some TypeScript type errors, they are there on purpose to test if the function throws an error

* test(integration/middlewares): fix mocks to make tests pass

* test(middleware): add check to assure the number of assertions expected to run are executed

* test(middlewares): fix typing of unit test for middlewares

* ♻️ refactor(jest): use toHaveBeenCalled instead of toHaveBeenCalledTimes(1)

* 🚨 test(event-handler-test): add missing done() callback to make async test to wait until all expect

* tests: Set node environment in Jest config

* test(server-test): mock errorHandler using jest.fn() (#198)

* tests: Fix tests after #190

- Pull in the relevant types package for `@sinonjs/fake-timers`
- Convert those tests from tap to Jest
- Add some Typescript types to those tests

* fix(typescript): Fix some type errors in the tests

* test(types): add missing types to variables and paramters on event-handler-test (#212)

* Fix sign test type compilation error (#213)

* test(server-test): fix typescript errors (#214)

* test(middleware-test): add test coverage for middleware when there is a timeout (#215)

* Revert 2b7c0bb and instead add a ts-ignore statement

Adds back the existing error message that was removed

* build(aggregate-error): upgrade version to support Custom Errors (#233)

* test(sign-test): restore 100% coverage for sign-test (#234)

* fix(sign): make sign() parameters non-optional (#237)

Co-authored-by: Oscar Dominguez <dominguez.celada@gmail.com>
  • Loading branch information
wolfy1339 and oscard0m committed Aug 25, 2020
1 parent d4bd29b commit 7e8defa
Show file tree
Hide file tree
Showing 26 changed files with 13,216 additions and 9,865 deletions.
21,587 changes: 12,425 additions & 9,162 deletions package-lock.json

Large diffs are not rendered by default.

24 changes: 21 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,32 @@
"@tsconfig/node10": "^1.0.3",
"@types/debug": "^4.1.5",
"@types/express": "^4.17.6",
"@types/jest": "^26.0.9",
"@types/node": "^14.0.14",
"@types/prettier": "^2.0.0",
"@types/simple-mock": "^0.8.1",
"@types/sinonjs__fake-timers": "^6.0.1",
"axios": "^0.20.0",
"cheerio": "^1.0.0-rc.2",
"get-port": "^5.0.0",
"jest": "^26.2.2",
"pascal-case": "^3.0.0",
"prettier": "^2.0.1",
"proxyquire": "^2.0.0",
"semantic-release": "^17.0.0",
"simple-mock": "^0.8.0",
"table-builder": "^2.1.1",
"tap": "^14.0.0",
"ts-jest": "^26.2.0",
"typescript": "^4.0.2"
},
"scripts": {
"build": "pika build",
"coverage": "tap --coverage-report=html && open coverage/lcov-report/index.html",
"coverage": "jest --coverage && open coverage/lcov-report/index.html",
"update-known-events": "node scripts/update-known-events.js",
"lint": "prettier --check 'src/**/*.{ts,json}' 'scripts/**/*' 'test/**/*' README.md package.json",
"lint:fix": "prettier --write 'src/**/*.{ts,json}' 'scripts/**/*' 'test/**/*' README.md package.json",
"pretest": "npm run -s lint && npm run build -s",
"test": "tap --100 --no-coverage 'test/**/*-test.js'",
"test": "jest --coverage",
"generate-types": "node scripts/generate-types.js",
"validate:ts": "tsc --noEmit --noImplicitAny --target es2020 --esModuleInterop --moduleResolution node test/typescript-validate.ts"
},
Expand All @@ -63,6 +67,20 @@
]
]
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"testMatch": null,
"testRegex": "test/.*/.*.ts",
"coverageThreshold": {
"global": {
"statements": 100,
"branches": 100,
"functions": 100,
"lines": 100
}
}
},
"release": {
"plugins": [
"@semantic-release/commit-analyzer",
Expand Down
1 change: 1 addition & 0 deletions src/sign/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createHmac } from "crypto";

export function sign(secret: string, payload: string | object): string {
// @ts-ignore throw friendly error message when required options are missing
if (!secret || !payload) {
throw new TypeError("secret & payload required");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { test } from "tap";
import { createEventHandler } from "../../pkg/dist-src/event-handler";
import { createEventHandler } from "../../src/event-handler";
import pushEventPayload from "../fixtures/push-payload.json";
import installationCreatedPayload from "../fixtures/installation-created-payload.json";
import { WebhookError, WebhookEvent } from "../../src/types";

test("events", (t) => {
t.plan(7);
test("events", (done) => {
const eventHandler = createEventHandler({});

const eventHandler = createEventHandler();

const hooksCalled = [];
const hooksCalled: string[] = [];
function hook1() {
return Promise.resolve().then(() => {
hooksCalled.push("hook1");
Expand All @@ -29,7 +27,7 @@ test("events", (t) => {
function hook6() {
hooksCalled.push("installation.created");
}
function hook7(event) {
function hook7(event: WebhookEvent) {
hooksCalled.push(`* (${event.name})`);
}

Expand Down Expand Up @@ -61,7 +59,7 @@ test("events", (t) => {
})

.then(() => {
t.deepEqual(hooksCalled, [
expect(hooksCalled).toStrictEqual([
"hook2",
"* (push)",
"hook1",
Expand All @@ -70,10 +68,10 @@ test("events", (t) => {
"* (installation)",
]);

eventHandler.on("error", (error) => {
t.ok(error.event.payload);
t.pass("error event triggered");
t.match(error.message, /oops/);
eventHandler.on("error", (error: WebhookError) => {
expect(error.event.payload).toBeTruthy();
// t.pass("error event triggered");
expect(error.message).toMatch(/oops/);
});

eventHandler.on("push", () => {
Expand All @@ -88,29 +86,34 @@ test("events", (t) => {
})

.catch((error) => {
t.match(error.message, /oops/);
expect(error.message).toMatch(/oops/);

const errors = Array.from(error);

t.is(errors.length, 1);
t.is(Array.from(error)[0].message, "oops");
expect(errors.length).toBe(1);
expect((Array.from(error) as { message: string }[])[0].message).toBe(
"oops"
);
})

.catch(t.error);
.catch((e) => expect(e instanceof Error).toBeTruthy())
.finally(done);
});

test("options.transform", (t) => {
t.plan(2);
test("options.transform", (done) => {
expect.assertions(2);

const eventHandler = createEventHandler({
transform: (event) => {
t.is(event.id, "123");
expect(event.id).toBe("123");
return "funky";
},
});

eventHandler.on("push", (event) => {
t.is(event, "funky");
eventHandler.on("push", (event: WebhookEvent) => {
expect(event).toBe("funky");

done();
});

eventHandler.receive({
Expand All @@ -120,16 +123,16 @@ test("options.transform", (t) => {
});
});

test("async options.transform", (t) => {
test("async options.transform", (done) => {
const eventHandler = createEventHandler({
transform: (event) => {
return Promise.resolve("funky");
},
});

eventHandler.on("push", (event) => {
t.is(event, "funky");
t.end();
eventHandler.on("push", (event: WebhookEvent) => {
expect(event).toBe("funky");
done();
});

eventHandler.receive({
Expand All @@ -139,10 +142,10 @@ test("async options.transform", (t) => {
});
});

test("multiple errors in same event handler", (t) => {
t.plan(2);
test("multiple errors in same event handler", (done) => {
expect.assertions(2);

const eventHandler = createEventHandler();
const eventHandler = createEventHandler({});

eventHandler.on("push", () => {
throw new Error("oops");
Expand All @@ -160,9 +163,10 @@ test("multiple errors in same event handler", (t) => {
})

.catch((error) => {
t.match(error.message, "oops");
t.is(Array.from(error).length, 2);
expect(error.message).toMatch("oops");
expect(Array.from(error).length).toBe(2);
})

.catch(t.error);
.catch((e) => expect(e instanceof Error).toBeTruthy())
.finally(done);
});
56 changes: 0 additions & 56 deletions test/integration/middleware-test.js

This file was deleted.

74 changes: 74 additions & 0 deletions test/integration/middleware-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { EventEmitter } from "events";
import { Buffer } from "buffer";
import { createMiddleware } from "../../src/middleware";

enum RequestMethodType {
POST = "POST",
GET = "GET",
}

type RequestMock = EventEmitter & {
method: RequestMethodType;
headers: { [key: string]: string };
url: string;
};

const headers = {
"x-github-delivery": "123e4567-e89b-12d3-a456-426655440000",
"x-github-event": "push",
"x-hub-signature": "sha1=f4d795e69b5d03c139cc6ea991ad3e5762d13e2f",
};

test("Invalid payload", (done) => {
const requestMock: RequestMock = Object.assign(new EventEmitter(), {
method: RequestMethodType.POST,
headers,
url: "/",
setEncoding: function (encoding: string) {},
});

const responseMock = {
end: jest.fn(),
statusCode: 0,
};

const middleware = createMiddleware({ secret: "mysecret" });
middleware(requestMock, responseMock).then(() => {
expect(responseMock.statusCode).toBe(400);
expect(responseMock.end).toHaveBeenCalledWith(
expect.stringContaining("SyntaxError: Invalid JSON")
);
done();
});

requestMock.emit("data", Buffer.from("foo"));
requestMock.emit("end");
expect.assertions(2);
});

test("request error", (done) => {
const requestMock: RequestMock = Object.assign(new EventEmitter(), {
method: RequestMethodType.POST,
headers,
url: "/",
setEncoding: function (encoding: string) {},
});

const responseMock = {
end: jest.fn(),
statusCode: 0,
};

const middleware = createMiddleware({ secret: "mysecret" });
middleware(requestMock, responseMock).then(() => {
expect(responseMock.statusCode).toBe(500);
expect(responseMock.end).toHaveBeenCalledWith(
expect.stringContaining("Error: oops")
);
done();
});

const error = new Error("oops");
requestMock.emit("error", error);
expect.assertions(2);
});
Loading

0 comments on commit 7e8defa

Please sign in to comment.