Skip to content

Commit 14bf394

Browse files
committed
feat: add test suite
1 parent 704eef2 commit 14bf394

File tree

6 files changed

+298
-2
lines changed

6 files changed

+298
-2
lines changed

.github/workflows/testing.yml

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Testing
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Check out code
13+
uses: actions/checkout@v3
14+
- name: Use Node LTS version
15+
uses: actions/setup-node@v3
16+
with:
17+
cache: yarn
18+
node-version: 18
19+
- name: Install dependencies
20+
run: yarn install --frozen-lockfile
21+
- name: Run build
22+
run: yarn build
23+
- name: Run test suite
24+
run: yarn test
25+
26+
coverage:
27+
permissions:
28+
pull-requests: write
29+
runs-on: ubuntu-latest
30+
steps:
31+
- uses: actions/checkout@v3
32+
- uses: actions/setup-node@v3
33+
with:
34+
cache: yarn
35+
node-version: 16
36+
- name: Install dependencies
37+
run: yarn install --frozen-lockfile
38+
- name: Run build
39+
run: yarn build
40+
- name: Run coverage report
41+
run: yarn coverage
42+
- uses: actions/upload-artifact@v3
43+
with:
44+
name: coverage-data-${{ github.run_id }}
45+
path: |
46+
coverage/coverage*.json
47+
- name: Coverage summary
48+
uses: irongut/[email protected]
49+
with:
50+
filename: coverage/cobertura-coverage.xml
51+
format: 'markdown'
52+
output: 'both'
53+
- name: Add coverage PR comment
54+
uses: marocchino/[email protected]
55+
with:
56+
recreate: true
57+
path: code-coverage-results.md
58+
- name: Write to job summary
59+
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY

__mocks__/@actions/core.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export function getInput(key: string) {
2+
switch (key) {
3+
case 'token':
4+
return '5269636b204173746c6579202d204e6576657220676f6e6e65206769766520796f75207570';
5+
case 'limit':
6+
return '300';
7+
case 'include_drafts':
8+
return 'true';
9+
case 'include_labels':
10+
return 'foo,bar';
11+
case 'exclude_labels':
12+
return 'baz';
13+
}
14+
}
15+
16+
export const debug = jest.fn().mockName('core.debug');
17+
export const info = jest.fn().mockName('core.info');
18+
export const error = jest.fn().mockName('core.error');
19+
export const setOutput = jest.fn().mockName('core.setOutput');
20+
export const setFailed = jest.fn().mockName('core.setFailed');
21+
export const warning = jest.fn().mockName('core.warning');

__mocks__/@actions/github.ts

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
export const context = {
2+
eventName: 'push',
3+
repo: {
4+
owner: 'monalisa',
5+
repo: 'helloworld',
6+
},
7+
payload: {
8+
ref: 'main',
9+
},
10+
};
11+
12+
// https://developer.github.com/v3/pulls/#list-pull-requests
13+
const PRs = [{
14+
number: 1,
15+
state: 'open',
16+
labels: [{
17+
name: 'foo',
18+
}],
19+
created_at: '2021-01-26T19:01:12Z',
20+
updated_at: '2021-11-26T19:01:12Z',
21+
closed_at: null,
22+
merged_at: null,
23+
base: { ref: 'main' },
24+
author_association: "OWNER",
25+
auto_merge: null,
26+
draft: false
27+
}, {
28+
number: 2,
29+
state: 'open',
30+
labels: [],
31+
created_at: '2021-01-26T19:01:12Z',
32+
updated_at: '2021-12-26T19:01:12Z',
33+
closed_at: null,
34+
merged_at: null,
35+
base: { ref: 'main' },
36+
author_association: "OWNER",
37+
auto_merge: null,
38+
draft: false
39+
}, {
40+
number: 3,
41+
state: 'closed',
42+
labels: [],
43+
created_at: '2021-01-26T19:01:12Z',
44+
updated_at: '2021-12-26T19:01:12Z',
45+
closed_at: '2021-12-30T19:01:12Z',
46+
merged_at: null,
47+
base: { ref: 'main' },
48+
author_association: "OWNER",
49+
auto_merge: null,
50+
draft: false
51+
}, {
52+
number: 4,
53+
state: 'open',
54+
labels: [],
55+
created_at: '2021-01-26T19:01:12Z',
56+
updated_at: '2022-01-26T19:01:12Z',
57+
closed_at: null,
58+
merged_at: null,
59+
base: { ref: 'main' },
60+
author_association: "OWNER",
61+
auto_merge: null,
62+
draft: true
63+
},
64+
];
65+
66+
export const getOctokit = jest.fn().mockImplementation(() => ({
67+
rest: {
68+
pulls: {
69+
list: jest.fn()
70+
.mockName('github.pulls.list')
71+
.mockImplementation((args) => {
72+
const page = args.page - 1;
73+
const cleaned = PRs.filter((pr) => {
74+
if (args.state === 'open' && pr.merged_at === null) return false;
75+
if (args.base !== undefined && pr.base.ref !== args.base) return false;
76+
return true;
77+
}) ?? [];
78+
79+
const sorted = cleaned.sort((a, b) => {
80+
if (args.sort === 'updated') {
81+
return new Date(a.updated_at)?.getTime() < new Date(b.updated_at)?.getTime() ? 1 : -1;
82+
}
83+
84+
return 0;
85+
});
86+
87+
if (args.direction === 'desc') sorted.reverse();
88+
89+
return {
90+
data: sorted.slice(page * args.per_page, (page + 1) * args.per_page)
91+
};
92+
}),
93+
updateBranch: jest.fn().mockResolvedValue({}),
94+
},
95+
},
96+
}));

__tests__/main.test.ts

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* @todo clean up and configure test suite */
2+
3+
import run from '../src/main';
4+
5+
import * as github from '../__mocks__/@actions/github';
6+
import * as core from '../__mocks__/@actions/core';
7+
8+
const fs = jest.requireActual('fs');
9+
10+
jest.mock('@actions/core');
11+
jest.mock('@actions/github');
12+
13+
const setFailedMock = jest.spyOn(core, "setFailed")
14+
const getOctokitMock = jest.spyOn(github, "getOctokit")
15+
16+
const gh = github.getOctokit('_');
17+
18+
const listPullsMock = jest.spyOn(gh.rest.pulls, 'list');
19+
const updateBranchMock = jest.spyOn(gh.rest.pulls, 'updateBranch');
20+
21+
afterAll(() => jest.restoreAllMocks());
22+
23+
// Good reference: https://github.com/simple-icons/release-action/blob/master/test/main.test.js
24+
describe('run', () => {
25+
it('updates open pull requests with default settings', async () => {
26+
27+
await run(core as any, github as any);
28+
29+
// We expect the action to authenticate with GitHub
30+
expect(getOctokitMock).toHaveBeenCalledTimes(1);
31+
32+
// Default limit is 100 so we expect only 1 call to listPulls
33+
expect(listPullsMock).toHaveBeenCalledTimes(1);
34+
35+
expect(updateBranchMock).toHaveBeenCalledWith({
36+
pull_number: 123,
37+
});
38+
});
39+
40+
// it('updates open', async () => {
41+
// mockGitHubResponseChangedFiles('foo.txt');
42+
43+
// await run(core, github);
44+
45+
// // expect(removeLabelMock).toHaveBeenCalledTimes(0);
46+
// // expect(addLabelsMock).toHaveBeenCalledTimes(0);
47+
// });
48+
49+
// it('(with sync-labels: true) it deletes preexisting PR labels that no longer match the glob pattern', async () => {
50+
// const mockInput = {
51+
// 'repo-token': 'foo',
52+
// 'configuration-path': 'bar',
53+
// 'sync-labels': true,
54+
// };
55+
56+
// jest
57+
// .spyOn(core, 'getInput')
58+
// .mockImplementation((name: string, ...opts) => mockInput[name]);
59+
60+
// usingLabelerConfigYaml('only_pdfs.yml');
61+
// mockGitHubResponseChangedFiles('foo.txt');
62+
// getPullMock.mockResolvedValue(<any>{
63+
// data: {
64+
// labels: [{ name: 'touched-a-pdf-file' }],
65+
// },
66+
// });
67+
68+
// await run(core, github);
69+
70+
// expect(addLabelsMock).toHaveBeenCalledTimes(0);
71+
// expect(removeLabelMock).toHaveBeenCalledTimes(1);
72+
// expect(removeLabelMock).toHaveBeenCalledWith({
73+
// owner: 'monalisa',
74+
// repo: 'helloworld',
75+
// issue_number: 123,
76+
// name: 'touched-a-pdf-file',
77+
// });
78+
// });
79+
80+
// it('(with sync-labels: false) it issues no delete calls even when there are preexisting PR labels that no longer match the glob pattern', async () => {
81+
// const mockInput = {
82+
// 'repo-token': 'foo',
83+
// 'configuration-path': 'bar',
84+
// 'sync-labels': false,
85+
// };
86+
87+
// jest
88+
// .spyOn(core, 'getInput')
89+
// .mockImplementation((name: string, ...opts) => mockInput[name]);
90+
91+
// usingLabelerConfigYaml('only_pdfs.yml');
92+
// mockGitHubResponseChangedFiles('foo.txt');
93+
// getPullMock.mockResolvedValue(<any>{
94+
// data: {
95+
// labels: [{ name: 'touched-a-pdf-file' }],
96+
// },
97+
// });
98+
99+
// await run(core, github);
100+
101+
// expect(addLabelsMock).toHaveBeenCalledTimes(0);
102+
// expect(removeLabelMock).toHaveBeenCalledTimes(0);
103+
// });
104+
});

jest.config.js

+15
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,19 @@ module.exports = {
77
'^.+\\.ts$': 'ts-jest',
88
},
99
verbose: true,
10+
collectCoverageFrom: [
11+
'src/*.ts',
12+
'!**/node_modules/**',
13+
'!**/vendor/**',
14+
],
15+
coverageDirectory: './coverage',
16+
coverageReporters: ["cobertura", "json"],
17+
coverageThreshold: {
18+
global: {
19+
branches: 80,
20+
functions: 80,
21+
lines: 80,
22+
statements: -10
23+
},
24+
}
1025
};

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@
2323
"scripts": {
2424
"build": "tsc && ncc build -o bin src/index.ts",
2525
"clean": "rm -rf bin",
26-
"coverage": "echo '🚧 Tests coming soon'",
26+
"coverage": "jest --coverage",
2727
"lint": "yarn lint-staged",
2828
"precommit": "lint-staged",
2929
"prepare": "husky install",
30+
"presemantic-release": "yarn clean && yarn build",
3031
"semantic-release": "semantic-release",
31-
"test": "echo '🚧 Tests coming soon'"
32+
"test": "jest"
3233
},
3334
"dependencies": {
3435
"@actions/core": "^1.10.0",

0 commit comments

Comments
 (0)