Skip to content

Commit 7bff95f

Browse files
committed
feat(action): add unit tests
- Added comprehensive unit tests - Mocked external dependencies - Covered various error scenarios - Improved code coverage - Ensured robust action logic
1 parent a51bf5e commit 7bff95f

2 files changed

Lines changed: 200 additions & 1 deletion

File tree

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ async function initializeServices() {
2727
};
2828
}
2929

30-
async function run() {
30+
export async function run() {
3131
try {
3232
process.chdir(TARGET_PATH);
3333
const releaseType = RELEASE_TYPE;

tests/index.test.ts

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2+
import * as core from '@actions/core';
3+
import { UpdaterService, GitService, ChangelogService } from '../src/services';
4+
import { UpdaterRegistry } from '../src/registry';
5+
import { FileHandler, safeParseJSON } from '../src/utils';
6+
import {
7+
PlatformDetectionError,
8+
VersionBumpError,
9+
FileNotFoundError,
10+
InvalidManifestError,
11+
} from '../src/errors';
12+
13+
// Mock all external dependencies
14+
vi.mock('@actions/core');
15+
vi.mock('../src/services');
16+
vi.mock('../src/registry');
17+
vi.mock('../src/utils');
18+
vi.mock('../src/config', async (importOriginal) => {
19+
const actual = await importOriginal();
20+
return {
21+
...actual,
22+
RELEASE_TYPE: 'patch',
23+
TARGET_PLATFORM: 'node',
24+
GIT_TAG: true,
25+
TARGET_PATH: './',
26+
BUMP_TARGETS: '[]',
27+
};
28+
});
29+
30+
// Mock process.chdir
31+
const chdirSpy = vi.spyOn(process, 'chdir').mockImplementation(() => {});
32+
33+
describe('Main Action Logic', () => {
34+
let mockUpdaterService: vi.Mocked<UpdaterService>;
35+
let mockGitService: vi.Mocked<GitService>;
36+
let mockChangelogService: vi.Mocked<ChangelogService>;
37+
let mockUpdaterRegistry: vi.Mocked<UpdaterRegistry>;
38+
let mockFileHandler: vi.Mocked<FileHandler>;
39+
40+
beforeEach(() => {
41+
// Reset mocks before each test
42+
vi.clearAllMocks();
43+
44+
mockUpdaterService = new UpdaterService(new UpdaterRegistry()) as vi.Mocked<UpdaterService>;
45+
mockGitService = new GitService('main') as vi.Mocked<GitService>;
46+
mockChangelogService = new ChangelogService(
47+
new FileHandler(),
48+
new GitService('main'),
49+
'CHANGELOG.md',
50+
) as vi.Mocked<ChangelogService>;
51+
mockUpdaterRegistry = new UpdaterRegistry() as vi.Mocked<UpdaterRegistry>;
52+
mockFileHandler = new FileHandler() as vi.Mocked<FileHandler>;
53+
54+
// Mock implementations for services
55+
(UpdaterService as unknown as vi.Mock).mockImplementation(() => mockUpdaterService);
56+
(GitService as unknown as vi.Mock).mockImplementation(() => mockGitService);
57+
(ChangelogService as unknown as vi.Mock).mockImplementation(() => mockChangelogService);
58+
(UpdaterRegistry as unknown as vi.Mock).mockImplementation(() => mockUpdaterRegistry);
59+
(FileHandler as unknown as vi.Mock).mockImplementation(() => mockFileHandler);
60+
61+
// Default mock return values
62+
mockUpdaterRegistry.loadUpdaters.mockResolvedValue(undefined);
63+
mockUpdaterService.getPlatform.mockReturnValue('node');
64+
mockUpdaterService.updateVersion.mockReturnValue('1.0.1');
65+
mockGitService.getLatestTag.mockResolvedValue('v1.0.0');
66+
mockGitService.getCommitsSinceTag.mockResolvedValue(['feat: initial commit']);
67+
mockChangelogService.generateChangelog.mockReturnValue('Changelog content');
68+
mockChangelogService.updateChangelog.mockResolvedValue(undefined);
69+
mockGitService.configureGitUser.mockResolvedValue(undefined);
70+
mockGitService.createReleaseBranch.mockResolvedValue(null); // No branch by default
71+
mockGitService.createPullRequest.mockResolvedValue('pr_url');
72+
mockGitService.createAndPushTag.mockResolvedValue(undefined);
73+
(safeParseJSON as vi.Mock).mockReturnValue([]); // Default empty bump targets
74+
});
75+
76+
afterEach(() => {
77+
vi.restoreAllMocks();
78+
});
79+
80+
it('should run successfully and perform all steps', async () => {
81+
// Import the module to run the `run()` function
82+
const { run } = await import('../src/index');
83+
await run();
84+
85+
expect(chdirSpy).toHaveBeenCalledWith('./');
86+
expect(mockUpdaterRegistry.loadUpdaters).toHaveBeenCalled();
87+
expect(mockUpdaterService.getPlatform).toHaveBeenCalledWith('node');
88+
expect(core.info).toHaveBeenCalledWith('Detected platform: node');
89+
expect(safeParseJSON).toHaveBeenCalledWith('[]');
90+
expect(mockUpdaterService.updateVersion).toHaveBeenCalledWith('node', 'patch', []);
91+
expect(core.setOutput).toHaveBeenCalledWith('new_version', '1.0.1');
92+
expect(mockGitService.getLatestTag).toHaveBeenCalled();
93+
expect(mockGitService.getCommitsSinceTag).toHaveBeenCalledWith('v1.0.0');
94+
expect(mockChangelogService.generateChangelog).toHaveBeenCalledWith(
95+
['feat: initial commit'],
96+
'1.0.1',
97+
);
98+
expect(mockChangelogService.updateChangelog).toHaveBeenCalledWith('Changelog content');
99+
expect(mockGitService.configureGitUser).toHaveBeenCalled();
100+
expect(mockGitService.createReleaseBranch).toHaveBeenCalledWith('1.0.1');
101+
expect(mockGitService.createAndPushTag).toHaveBeenCalledWith('1.0.1');
102+
expect(core.setFailed).not.toHaveBeenCalled();
103+
});
104+
105+
it('should create a release branch and PR if createReleaseBranch returns a branch name', async () => {
106+
mockGitService.createReleaseBranch.mockResolvedValue('release/1.0.1');
107+
108+
const { run } = await import('../src/index');
109+
await run();
110+
111+
expect(mockGitService.createReleaseBranch).toHaveBeenCalledWith('1.0.1');
112+
expect(core.info).toHaveBeenCalledWith('✅ Created branch: release/1.0.1');
113+
expect(mockGitService.createPullRequest).toHaveBeenCalledWith('release/1.0.1', '1.0.1');
114+
expect(core.info).toHaveBeenCalledWith('✅ PR created: pr_url');
115+
});
116+
117+
it('should not create a tag if GIT_TAG is false', async () => {
118+
vi.doMock('../src/config', async (importOriginal) => {
119+
const actual = await importOriginal();
120+
return {
121+
...actual,
122+
GIT_TAG: false,
123+
};
124+
});
125+
126+
// Re-import to apply the new mock for GIT_TAG
127+
vi.resetModules();
128+
const { run } = await import('../src/index');
129+
await run();
130+
131+
expect(mockGitService.createAndPushTag).not.toHaveBeenCalled();
132+
});
133+
134+
it('should handle PlatformDetectionError', async () => {
135+
mockUpdaterService.getPlatform.mockImplementation(() => {
136+
throw new PlatformDetectionError('No platform detected');
137+
});
138+
139+
const { run } = await import('../src/index');
140+
await run();
141+
142+
expect(core.setFailed).toHaveBeenCalledWith('No platform detected');
143+
});
144+
145+
it('should handle VersionBumpError', async () => {
146+
mockUpdaterService.updateVersion.mockImplementation(() => {
147+
throw new VersionBumpError('Failed to bump version');
148+
});
149+
150+
const { run } = await import('../src/index');
151+
await run();
152+
153+
expect(core.setFailed).toHaveBeenCalledWith('Failed to bump version');
154+
});
155+
156+
it('should handle FileNotFoundError', async () => {
157+
mockGitService.getLatestTag.mockImplementation(() => {
158+
throw new FileNotFoundError('File not found');
159+
});
160+
161+
const { run } = await import('../src/index');
162+
await run();
163+
164+
expect(core.setFailed).toHaveBeenCalledWith('File not found');
165+
});
166+
167+
it('should handle InvalidManifestError', async () => {
168+
(safeParseJSON as vi.Mock).mockImplementation(() => {
169+
throw new InvalidManifestError('Invalid JSON');
170+
});
171+
172+
const { run } = await import('../src/index');
173+
await run();
174+
175+
expect(core.setFailed).toHaveBeenCalledWith('Invalid JSON');
176+
});
177+
178+
it('should handle generic Error', async () => {
179+
mockGitService.configureGitUser.mockImplementation(() => {
180+
throw new Error('Generic error occurred');
181+
});
182+
183+
const { run } = await import('../src/index');
184+
await run();
185+
186+
expect(core.setFailed).toHaveBeenCalledWith('Generic error occurred');
187+
});
188+
189+
it('should handle unknown errors', async () => {
190+
mockGitService.configureGitUser.mockImplementation(() => {
191+
throw 'unknown error type';
192+
});
193+
194+
const { run } = await import('../src/index');
195+
await run();
196+
197+
expect(core.setFailed).toHaveBeenCalledWith('unknown error type');
198+
});
199+
});

0 commit comments

Comments
 (0)