Skip to content

Commit afddaca

Browse files
committed
test(@modulify/conventional-git): Added tests for Client class
1 parent 2836209 commit afddaca

File tree

4 files changed

+276
-3
lines changed

4 files changed

+276
-3
lines changed

packages/conventional-git/src/Client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,6 @@ export class Client {
130130
return null
131131
}
132132

133-
return tags.sort(semver.rcompare)[0] ?? null
133+
return tags.sort(semver.rcompare)[0]
134134
}
135135
}

packages/conventional-git/src/parse.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ function parseReference (input: string, action: string | null, patterns: ParsePa
235235
}
236236

237237
function parseReferences (input: string, patterns: ParsePatterns) {
238+
if (!input || !patterns.references) return []
239+
238240
const regex = input.match(patterns.references) ? patterns.references : MATCH_EVERYTHING
239241
const references: CommitReference[] = []
240242

@@ -390,4 +392,4 @@ function trimLineBreaks (text: string) {
390392
}
391393

392394
return text.substring(matches.index, end + 1)
393-
}
395+
}
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
import type { AsyncStream } from '@modulify/git-toolkit/dist/stream'
2+
3+
import {
4+
afterEach,
5+
beforeEach,
6+
describe,
7+
expect,
8+
it,
9+
vi,
10+
} from 'vitest'
11+
12+
import { execSync } from 'node:child_process'
13+
14+
import {
15+
join,
16+
resolve,
17+
} from 'node:path'
18+
19+
import { randomUUID } from 'node:crypto'
20+
21+
import fs from 'fs'
22+
import semver from 'semver'
23+
24+
import { GitClient } from '@modulify/git-toolkit'
25+
26+
import { Client, packagePrefix } from '@/index'
27+
28+
const __temporary = join(__dirname, 'tmp')
29+
30+
const streamToArray = async <T>(stream: AsyncStream<T>): Promise<T[]> => {
31+
const result: T[] = []
32+
33+
for await (const item of stream) {
34+
result.push(item)
35+
}
36+
37+
return result
38+
}
39+
40+
describe('packagePrefix', () => {
41+
it('should return default prefix when package name is not provided', () => {
42+
expect(packagePrefix()).toEqual(/^.+@/)
43+
})
44+
45+
it('should return specific prefix when package name is provided', () => {
46+
expect(packagePrefix('my-pkg')).toBe('my-pkg@')
47+
})
48+
})
49+
50+
describe('Client', () => {
51+
let cwd: string
52+
53+
const exec = (command: string) => execSync(command, {
54+
cwd,
55+
encoding: 'utf-8',
56+
stdio: 'pipe',
57+
})
58+
59+
beforeEach(() => {
60+
cwd = join(__temporary, randomUUID())
61+
62+
fs.mkdirSync(cwd, { recursive: true })
63+
fs.mkdirSync(resolve(cwd, 'git-templates'))
64+
65+
exec('git init --template=./git-templates --initial-branch=main')
66+
exec('git config user.name "Tester"')
67+
exec('git config user.email "tester.modulify@gmail.com"')
68+
})
69+
70+
afterEach(() => {
71+
vi.restoreAllMocks()
72+
73+
try {
74+
if (cwd) {
75+
fs.rmSync(cwd, { recursive: true })
76+
}
77+
78+
if (!fs.readdirSync(__temporary).length) {
79+
fs.rmdirSync(__temporary)
80+
}
81+
} catch { /* empty */
82+
}
83+
})
84+
85+
describe('constructor', () => {
86+
it('should create new GitClient if not provided', () => {
87+
const client = new Client()
88+
expect(client.git).toBeDefined()
89+
})
90+
91+
it('should use provided GitClient instance', () => {
92+
const git = new GitClient()
93+
const client = new Client({ git })
94+
95+
expect(client.git).toBe(git)
96+
})
97+
})
98+
99+
describe('commits', () => {
100+
it('should return a stream of parsed commits', async () => {
101+
exec('git commit -m "feat: Added feature #1" --allow-empty --no-gpg-sign')
102+
exec('git commit -m "fix: Fixed issue #2" --allow-empty --no-gpg-sign')
103+
104+
const client = new Client({ cwd })
105+
const commits = await streamToArray(client.commits())
106+
107+
expect(commits).toHaveLength(2)
108+
expect(commits[0].subject).toBe('Fixed issue #2')
109+
expect(commits[0].type).toBe('fix')
110+
expect(commits[1].subject).toBe('Added feature #1')
111+
expect(commits[1].type).toBe('feat')
112+
})
113+
})
114+
115+
describe('tags', () => {
116+
it('should return all tags by default', async () => {
117+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
118+
exec('git tag v1.0.0')
119+
exec('git tag v1.1.0')
120+
121+
const client = new Client({ cwd })
122+
const tags = await streamToArray(client.tags())
123+
124+
expect(tags).toContain('v1.0.0')
125+
expect(tags).toContain('v1.1.0')
126+
})
127+
128+
it('should filter tags by string prefix', async () => {
129+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
130+
exec('git tag pkg-a@1.0.0')
131+
exec('git tag pkg-b@1.0.0')
132+
133+
const client = new Client({ cwd })
134+
135+
expect(await streamToArray(client.tags({ prefix: 'pkg-a@' }))).toEqual([
136+
'pkg-a@1.0.0',
137+
])
138+
})
139+
140+
it('should filter tags by RegExp prefix', async () => {
141+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
142+
exec('git tag pkg-a@1.0.0')
143+
exec('git tag pkg-b@1.0.0')
144+
145+
const client = new Client({ cwd })
146+
147+
expect(await streamToArray(client.tags({ prefix: /^pkg-b@/ }))).toEqual([
148+
'pkg-b@1.0.0',
149+
])
150+
})
151+
152+
it('should skip unstable versions when skipUnstable is true', async () => {
153+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
154+
exec('git tag 1.0.0')
155+
exec('git tag 1.1.0-beta.1')
156+
157+
const client = new Client({ cwd })
158+
const tags = await streamToArray(client.tags({ skipUnstable: true }))
159+
160+
expect(tags).toContain('1.0.0')
161+
expect(tags).not.toContain('1.1.0-beta.1')
162+
})
163+
164+
it('should clean tags when clean is true', async () => {
165+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
166+
exec('git tag v1.0.0')
167+
exec('git tag pkg@1.1.0')
168+
169+
const client = new Client({ cwd })
170+
171+
expect(await streamToArray(client.tags({ clean: true }))).toContain('1.0.0')
172+
expect(await streamToArray(client.tags({ clean: true, prefix: 'pkg@' }))).toContain('1.1.0')
173+
})
174+
175+
it('should ignore non-semver tags', async () => {
176+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
177+
exec('git tag not-a-version')
178+
exec('git tag 1.0.0')
179+
180+
const client = new Client({ cwd })
181+
182+
expect(await streamToArray(client.tags())).toEqual(['1.0.0'])
183+
})
184+
185+
it('should ignore tags with prefix but invalid version part', async () => {
186+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
187+
exec('git tag pkg@invalid')
188+
exec('git tag pkg@1.0.0')
189+
190+
const client = new Client({ cwd })
191+
192+
expect(await streamToArray(client.tags({ prefix: 'pkg@' }))).toEqual([
193+
'pkg@1.0.0',
194+
])
195+
})
196+
197+
it('should clean tags even if prefix does not match but it is a valid semver', async () => {
198+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
199+
exec('git tag v1.0.0')
200+
201+
const client = new Client({ cwd })
202+
203+
expect(await streamToArray(client.tags({ clean: true, prefix: 'pkg@' }))).toContain('1.0.0')
204+
})
205+
206+
it('should return null if semver.valid returns null for some reason', async () => {
207+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
208+
exec('git tag pkg@1.0.0')
209+
210+
const client = new Client({ cwd })
211+
212+
expect(await streamToArray(client.tags({ clean: true, prefix: 'pkg@1.0.0' }))).toEqual([])
213+
})
214+
215+
it('should handle cleanTag returning null', async () => {
216+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
217+
exec('git tag not-cleanable')
218+
exec('git tag 1.2.3.4')
219+
exec('git tag 1.0.0')
220+
221+
const client = new Client({ cwd })
222+
223+
expect(await streamToArray(client.tags({ clean: true }))).toContain('1.0.0')
224+
})
225+
226+
it('should cover branch when semver.clean returns null despite valid semver', async () => {
227+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
228+
exec('git tag v1.0.0')
229+
exec('git tag pkg@1.1.0')
230+
231+
const client = new Client({ cwd })
232+
233+
vi.spyOn(semver, 'clean').mockReturnValue(null)
234+
235+
expect(await streamToArray(client.tags({ clean: true }))).toEqual([])
236+
expect(await streamToArray(client.tags({ clean: true, prefix: 'pkg@' }))).toEqual([])
237+
})
238+
})
239+
240+
describe('version', () => {
241+
it('should return the latest semver version', async () => {
242+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
243+
exec('git tag 1.0.0')
244+
exec('git tag 1.2.0')
245+
exec('git tag 1.1.0')
246+
247+
const client = new Client({ cwd })
248+
249+
expect(await client.version()).toBe('1.2.0')
250+
})
251+
252+
it('should return null if no tags found', async () => {
253+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
254+
255+
const client = new Client({ cwd })
256+
257+
expect(await client.version()).toBeNull()
258+
})
259+
260+
it('should return the latest version with prefix', async () => {
261+
exec('git commit -m "initial" --allow-empty --no-gpg-sign')
262+
exec('git tag pkg@1.0.0')
263+
exec('git tag pkg@1.1.0')
264+
exec('git tag other@2.0.0')
265+
266+
const client = new Client({ cwd })
267+
268+
expect(await client.version({ prefix: 'pkg@' })).toBe('1.1.0')
269+
})
270+
})
271+
})

packages/conventional-git/tests/parse.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,4 @@ describe('parse', () => {
161161
})
162162
})
163163
})
164-
})
164+
})

0 commit comments

Comments
 (0)