Skip to content

Commit d7e0d0e

Browse files
authored
Add release functions tests (lucacome#60)
1 parent 126f368 commit d7e0d0e

File tree

5 files changed

+301
-8
lines changed

5 files changed

+301
-8
lines changed

__mocks__/@actions/github.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const mockApi = {
2+
rest: {
3+
issues: {
4+
addLabels: jest.fn(),
5+
removeLabel: jest.fn(),
6+
},
7+
pulls: {
8+
get: jest.fn().mockResolvedValue({}),
9+
listFiles: {
10+
endpoint: {
11+
merge: jest.fn().mockReturnValue({}),
12+
},
13+
},
14+
},
15+
repos: {
16+
getContent: jest.fn(),
17+
listReleases: jest.fn(),
18+
createRelease: jest.fn(),
19+
updateRelease: jest.fn(),
20+
generateReleaseNotes: jest.fn(),
21+
},
22+
},
23+
paginate: jest.fn().mockImplementation(async (method, options) => {
24+
const response = await method(options)
25+
return response.data
26+
}),
27+
}
28+
export const context = {
29+
payload: {
30+
pull_request: {
31+
number: 123,
32+
},
33+
},
34+
repo: {
35+
owner: 'monalisa',
36+
repo: 'helloworld',
37+
},
38+
ref: 'refs/heads/main',
39+
}
40+
41+
export const getOctokit = jest.fn().mockImplementation(() => mockApi)

__tests__/release.test.ts

+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
import {getRelease, createOrUpdateRelease} from '../src/release'
2+
import * as github from '@actions/github'
3+
import {Inputs} from '../src/context'
4+
5+
const fs = jest.requireActual('fs')
6+
7+
jest.mock('@actions/core')
8+
jest.mock('@actions/github')
9+
10+
let gh: ReturnType<typeof github.getOctokit>
11+
12+
describe('getRelease', () => {
13+
beforeEach(() => {
14+
jest.clearAllMocks()
15+
gh = github.getOctokit('_')
16+
})
17+
18+
it('should return the latest release when multiple releases exist', async () => {
19+
const mockResponse: any = {
20+
headers: {},
21+
status: 200,
22+
data: [
23+
{
24+
tag_name: 'v1.0.2',
25+
target_commitish: 'main',
26+
draft: false,
27+
},
28+
{
29+
tag_name: 'v1.0.1',
30+
target_commitish: 'main',
31+
draft: false,
32+
},
33+
{
34+
tag_name: 'v1.0.0',
35+
target_commitish: 'main',
36+
draft: false,
37+
},
38+
],
39+
}
40+
41+
const mockReleases = jest.spyOn(gh.rest.repos, 'listReleases')
42+
mockReleases.mockResolvedValue(mockResponse)
43+
44+
const [releases, latestRelease] = await getRelease(gh)
45+
46+
expect(releases).toHaveLength(3)
47+
expect(latestRelease).toBe('v1.0.2')
48+
})
49+
50+
it('should return the latest for the current branch', async () => {
51+
const mockResponse: any = {
52+
headers: {},
53+
status: 200,
54+
data: [
55+
{
56+
tag_name: 'v1.0.2',
57+
target_commitish: 'dev',
58+
draft: false,
59+
},
60+
{
61+
tag_name: 'v1.0.1',
62+
target_commitish: 'main',
63+
draft: false,
64+
},
65+
{
66+
tag_name: 'v1.0.0',
67+
target_commitish: 'main',
68+
draft: false,
69+
},
70+
],
71+
}
72+
73+
const mockReleases = jest.spyOn(gh.rest.repos, 'listReleases')
74+
mockReleases.mockResolvedValue(mockResponse)
75+
76+
const [releases, latestRelease] = await getRelease(gh)
77+
78+
expect(releases).toHaveLength(3)
79+
expect(latestRelease).toBe('v1.0.1')
80+
})
81+
82+
it('should return the latest non-draft release', async () => {
83+
const mockResponse: any = {
84+
headers: {},
85+
status: 200,
86+
data: [
87+
{
88+
tag_name: 'v1.0.2',
89+
target_commitish: 'dev',
90+
draft: false,
91+
},
92+
{
93+
tag_name: 'v1.0.1',
94+
target_commitish: 'main',
95+
draft: true,
96+
},
97+
{
98+
tag_name: 'v1.0.0',
99+
target_commitish: 'main',
100+
draft: false,
101+
},
102+
],
103+
}
104+
105+
const mockReleases = jest.spyOn(gh.rest.repos, 'listReleases')
106+
mockReleases.mockResolvedValue(mockResponse)
107+
108+
const [releases, latestRelease] = await getRelease(gh)
109+
110+
expect(releases).toHaveLength(3)
111+
expect(latestRelease).toBe('v1.0.0')
112+
})
113+
114+
it('should return v0.0.0 when no releases exist', async () => {
115+
const mockResponse: any = {
116+
headers: {},
117+
status: 200,
118+
data: [],
119+
}
120+
121+
const mockReleases = jest.spyOn(gh.rest.repos, 'listReleases')
122+
mockReleases.mockResolvedValue(mockResponse)
123+
124+
const [releases, latestRelease] = await getRelease(gh)
125+
126+
expect(releases).toHaveLength(0)
127+
expect(latestRelease).toBe('v0.0.0')
128+
})
129+
})
130+
131+
describe('createOrUpdateRelease', () => {
132+
let mockResponse: any
133+
let mockNotes: any
134+
const inputs: Inputs = {
135+
githubToken: '_',
136+
majorLabel: 'major',
137+
minorLabel: 'minor',
138+
header: 'header',
139+
footer: 'footer',
140+
}
141+
beforeEach(() => {
142+
jest.clearAllMocks()
143+
gh = github.getOctokit('_')
144+
mockResponse = {
145+
headers: {},
146+
status: 200,
147+
data: [
148+
{
149+
id: 1,
150+
tag_name: 'v1.0.0',
151+
target_commitish: 'main',
152+
draft: false,
153+
body: 'header',
154+
},
155+
{
156+
id: 2,
157+
tag_name: 'v1.0.1',
158+
target_commitish: 'main',
159+
draft: true,
160+
body: 'header',
161+
},
162+
],
163+
}
164+
165+
mockNotes = {
166+
headers: {},
167+
status: 200,
168+
data: {
169+
body: 'header',
170+
name: 'v1.0.1',
171+
},
172+
}
173+
})
174+
175+
it('should create a new release draft', async () => {
176+
const mockInputCreate: any = {
177+
headers: {},
178+
status: 200,
179+
data: [
180+
{
181+
id: 1,
182+
tag_name: 'v1.0.0',
183+
target_commitish: 'main',
184+
draft: false,
185+
},
186+
],
187+
}
188+
189+
const mockReleases = jest.spyOn(gh.rest.repos, 'createRelease')
190+
mockReleases.mockResolvedValue(mockResponse)
191+
192+
const mockRelease = jest.spyOn(gh.rest.repos, 'listReleases')
193+
mockRelease.mockResolvedValue(mockInputCreate)
194+
195+
const mockReleaseNotes = jest.spyOn(gh.rest.repos, 'generateReleaseNotes')
196+
mockReleaseNotes.mockResolvedValue(mockNotes)
197+
198+
const response = await createOrUpdateRelease(gh, inputs, mockInputCreate.data, 'v1.0.0', 'v1.0.1')
199+
200+
expect(mockReleases).toHaveBeenCalledTimes(1)
201+
})
202+
203+
it('should update an existing release draft', async () => {
204+
const mockInputUpdate: any = {
205+
headers: {},
206+
status: 200,
207+
data: [
208+
{
209+
id: 1,
210+
tag_name: 'v1.0.0',
211+
target_commitish: 'main',
212+
draft: false,
213+
},
214+
{
215+
id: 2,
216+
tag_name: 'v1.0.1',
217+
target_commitish: 'main',
218+
draft: true,
219+
},
220+
],
221+
}
222+
223+
const mockReleases = jest.spyOn(gh.rest.repos, 'updateRelease')
224+
mockReleases.mockResolvedValue(mockResponse)
225+
226+
const mockRelease = jest.spyOn(gh.rest.repos, 'listReleases')
227+
mockRelease.mockResolvedValue(mockInputUpdate)
228+
229+
const mockReleaseNotes = jest.spyOn(gh.rest.repos, 'generateReleaseNotes')
230+
mockReleaseNotes.mockResolvedValue(mockNotes)
231+
232+
const response = await createOrUpdateRelease(gh, inputs, mockInputUpdate.data, 'v1.0.0', 'v1.0.1')
233+
234+
expect(mockReleases).toHaveBeenCalledTimes(1)
235+
})
236+
})

package-lock.json

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"semver": "^7.3.8"
3333
},
3434
"devDependencies": {
35+
"@types/jest": "^29.5.0",
3536
"@types/js-yaml": "^4.0.5",
3637
"@types/node": "^18.15.3",
3738
"@typescript-eslint/parser": "^5.55.0",

tsconfig.json

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
{
22
"compilerOptions": {
3-
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
4-
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
5-
"outDir": "./lib", /* Redirect output structure to the directory. */
6-
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
7-
"strict": true, /* Enable all strict type-checking options. */
8-
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
9-
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
3+
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
4+
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
5+
"outDir": "./lib", /* Redirect output structure to the directory. */
6+
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
7+
"strict": true, /* Enable all strict type-checking options. */
8+
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
9+
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
1010
},
11-
"exclude": ["node_modules", "**/*.test.ts"]
11+
"exclude": [
12+
"node_modules",
13+
"**/*.test.ts",
14+
"**/__mocks__"
15+
]
1216
}

0 commit comments

Comments
 (0)