-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✅ Added Unit Tests (and fixed some discovered bugs) (#4)
* 🔧 Switched from outdated extension * ➕ Added Jest and related Deps * 🔧 Added Jest config * ♻️ Reworked input code Error handling is no longer required * 🔥 Removed dead code and uneeded code * 🐛 Adjust names to be proper names * 🐛 Also using getEmojiName on extracted code * ✅ Added Tests for shared * ✅ Added tests for gitmoji * 🐛 Better Tag to version mapping Based on supported prefix v/V * ✅ Added tests for missing history functions * 🐛 Improved Emoji Name extraction for variation emojis * 🐛 Handling link not existing * ♻️ Using emoji name now hence no transform needed * ✅ Added test cases for special chars * ✅ Added tests for extracting * ✅ Added tests for generating
- Loading branch information
Showing
17 changed files
with
5,187 additions
and
998 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import fs from 'node:fs/promises'; | ||
import { extractEmojiFromMessage, getLastChangelogVersion, getPackageVersion } from '../../src/changelog/extract.js'; | ||
|
||
describe('Extract', () => { | ||
const basePath = '/workspaces/gitmoji-changelog/'; | ||
const changelogFile = 'CHANGELOG.md'; | ||
const packageFile = 'package.json'; | ||
|
||
describe('getLastChangelogVersion', () => { | ||
let readFileMock: typeof jest; | ||
|
||
beforeAll(() => { | ||
readFileMock = jest.mock('node:fs/promises'); | ||
}); | ||
|
||
afterEach(() => { | ||
(fs.readFile as unknown as jest.SpyInstance).mockRestore(); | ||
}); | ||
|
||
afterAll(() => { | ||
readFileMock.restoreAllMocks(); | ||
}); | ||
|
||
it('should obtain version from changelog', async () => { | ||
fs.readFile = jest.fn().mockResolvedValue('<a name="0.0.1"></a>'); | ||
|
||
const version = await getLastChangelogVersion(`${basePath}${changelogFile}`); | ||
|
||
expect(version).toEqual('0.0.1'); | ||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.readFile).toHaveBeenCalledWith(`${basePath}${changelogFile}`, { encoding: 'utf-8' }); | ||
}); | ||
|
||
it('should return empty string if changelog is empty', async () => { | ||
fs.readFile = jest.fn().mockResolvedValue(''); | ||
|
||
const version = await getLastChangelogVersion(`${basePath}${changelogFile}`); | ||
|
||
expect(version).toEqual(''); | ||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.readFile).toHaveBeenCalledWith(`${basePath}${changelogFile}`, { encoding: 'utf-8' }); | ||
}); | ||
|
||
it('should return empty string if anchor is not in changelog', async () => { | ||
fs.readFile = jest | ||
.fn() | ||
.mockResolvedValue( | ||
'- ➕ Added Jest and related Deps [[1dc0987](https://github.com/Templum/gitmoji-changelog/commit/1dc09876a225128aa63c4cba4e65d4bd6d820ba7)] (by Templum)', | ||
); | ||
|
||
const version = await getLastChangelogVersion(`${basePath}${changelogFile}`); | ||
|
||
expect(version).toEqual(''); | ||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.readFile).toHaveBeenCalledWith(`${basePath}${changelogFile}`, { encoding: 'utf-8' }); | ||
}); | ||
|
||
it('should return empty string if error occured', async () => { | ||
fs.readFile = jest.fn().mockRejectedValue(new Error('Failed to read file')); | ||
|
||
const version = await getLastChangelogVersion(`${basePath}${changelogFile}`); | ||
|
||
expect(version).toEqual(''); | ||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.readFile).toHaveBeenCalledWith(`${basePath}${changelogFile}`, { encoding: 'utf-8' }); | ||
}); | ||
}); | ||
|
||
describe('getPackageVersion', () => { | ||
let readFileMock: typeof jest; | ||
|
||
beforeAll(() => { | ||
readFileMock = jest.mock('node:fs/promises'); | ||
}); | ||
|
||
afterEach(() => { | ||
(fs.readFile as unknown as jest.SpyInstance).mockRestore(); | ||
}); | ||
|
||
afterAll(() => { | ||
readFileMock.restoreAllMocks(); | ||
}); | ||
|
||
it('should return version obtained from package.json', async () => { | ||
fs.readFile = jest.fn().mockResolvedValue('{ "version": "0.0.1" }'); | ||
|
||
const version = await getPackageVersion(basePath); | ||
|
||
expect(version).toEqual('0.0.1'); | ||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.readFile).toHaveBeenCalledWith(`${basePath}${packageFile}`, { encoding: 'utf-8' }); | ||
}); | ||
|
||
it('should return 0.0.0 if package.json has no version field', async () => { | ||
fs.readFile = jest.fn().mockResolvedValue('{ "name": "test" }'); | ||
|
||
const version = await getPackageVersion(basePath); | ||
|
||
expect(version).toEqual('0.0.0'); | ||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.readFile).toHaveBeenCalledWith(`${basePath}${packageFile}`, { encoding: 'utf-8' }); | ||
}); | ||
|
||
it('should rethrow any error encountered', async () => { | ||
fs.readFile = jest.fn().mockRejectedValue(new Error('Oops')); | ||
|
||
expect(async () => await getPackageVersion(basePath)).rejects.toThrow('Oops'); | ||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.readFile).toHaveBeenCalledWith(`${basePath}${packageFile}`, { encoding: 'utf-8' }); | ||
}); | ||
}); | ||
|
||
describe('extractEmojiFromMessage', () => { | ||
test.each([ | ||
{ | ||
message: '⚡️ Improved Performance', | ||
emojiName: 'zap', | ||
cleanedMessage: 'Improved Performance', | ||
}, | ||
{ | ||
message: ':fire: Removed unused files', | ||
emojiName: 'fire', | ||
cleanedMessage: 'Removed unused files', | ||
}, | ||
{ | ||
message: 'feat: allow provided config object to extend other configs', | ||
emojiName: '', | ||
cleanedMessage: 'at: allow provided config object to extend other configs', | ||
}, | ||
])('extractEmojiFromMessage should extract "$emojiName" from message', ({ message, emojiName, cleanedMessage }) => { | ||
const [extractedEmoji, extractedMessage] = extractEmojiFromMessage(message); | ||
|
||
expect(extractedEmoji).toEqual(emojiName); | ||
expect(extractedMessage).toEqual(cleanedMessage); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
import { GitCommit } from '../../src/git/history.js'; | ||
import { generateChangelog, writeChangelog } from '../../src/changelog/generate.js'; | ||
import fs from 'node:fs/promises'; | ||
|
||
describe('Generate', () => { | ||
describe('generateChangelog', () => { | ||
const name = 'Templum'; | ||
const email = '[email protected]'; | ||
|
||
const short = 'e2cfc89'; | ||
const long = 'e2cfc89fae5b43594b2c649fd4c05bcc6d2d12ac'; | ||
const hash = { short, long }; | ||
|
||
const author = { | ||
name, | ||
email, | ||
}; | ||
|
||
it('should create a changelog using provided history', () => { | ||
const history: GitCommit[] = [ | ||
{ | ||
message: ':sparkles: Added Enterprise Logging', // Added | ||
author, | ||
hash, | ||
}, | ||
{ | ||
message: ':fire: Removed Transition Code', // Removed | ||
author, | ||
hash, | ||
}, | ||
{ | ||
message: '⚡️ Doubled Performance', // Changed | ||
author, | ||
hash, | ||
}, | ||
{ | ||
message: '🔐 Secrets added', // Miscellaneous | ||
author, | ||
hash, | ||
}, | ||
]; | ||
|
||
const changelog = generateChangelog(history, { addAuthors: true }, '1.0.0'); | ||
|
||
const entries = changelog.split('\n').filter((current) => current.length > 0); | ||
|
||
const expectedCategories = ['Added', 'Removed', 'Changed', 'Miscellaneous']; | ||
const actualCategories = entries.filter((current) => current.startsWith('###')).map((current) => current.replace('###', '').trim()); | ||
|
||
for (const expectedCategory of expectedCategories) { | ||
expect(actualCategories).toContain(expectedCategory); | ||
} | ||
|
||
const commits = entries.filter((current) => current.startsWith('-')); | ||
|
||
for (const commit of commits) { | ||
expect(commit).toContain(name); | ||
expect(commit).toContain(hash.short); | ||
expect(commit).toContain(hash.long); | ||
} | ||
}); | ||
|
||
it('should ignore history entry that dont fit a category', () => { | ||
const history: GitCommit[] = [ | ||
{ | ||
message: ':sparkles: Added Enterprise Logging', | ||
author, | ||
hash, | ||
}, | ||
{ | ||
message: ':children_crossing: Improved UX', | ||
author, | ||
hash, | ||
}, | ||
{ | ||
message: 'feat: Wrong commit convention', | ||
author, | ||
hash, | ||
}, | ||
]; | ||
|
||
const changelog = generateChangelog(history, { addAuthors: true }, '1.0.0'); | ||
|
||
const commits = changelog | ||
.split('\n') | ||
.filter((current) => current.length > 0) | ||
.filter((current) => current.startsWith('-')); | ||
|
||
for (const commit of commits) { | ||
expect(commit).not.toContain('Improved UX'); | ||
expect(commit).not.toContain('Wrong commit convention'); | ||
} | ||
}); | ||
|
||
it('should not add authors if addAuthors is set to false', () => { | ||
const history: GitCommit[] = [ | ||
{ | ||
message: ':sparkles: Added Enterprise Logging', | ||
author: { name: 'HideMe', email }, | ||
hash, | ||
}, | ||
]; | ||
|
||
const changelog = generateChangelog(history, { addAuthors: false }, '1.0.0'); | ||
|
||
const commits = changelog | ||
.split('\n') | ||
.filter((current) => current.length > 0) | ||
.filter((current) => current.startsWith('-')); | ||
|
||
for (const commit of commits) { | ||
expect(commit).not.toContain('HideMe'); | ||
} | ||
}); | ||
|
||
it('should embbed version in link', () => { | ||
const history: GitCommit[] = [ | ||
{ | ||
message: ':sparkles: Added Enterprise Logging', | ||
author: { name: 'HideMe', email }, | ||
hash, | ||
}, | ||
]; | ||
|
||
const changelog = generateChangelog(history, { addAuthors: false }, '1.0.0'); | ||
|
||
const [link, heading] = changelog.split('\n').filter((current) => current.length > 0); | ||
expect(link).toContain('1.0.0'); | ||
expect(heading).toContain('1.0.0'); | ||
}); | ||
}); | ||
|
||
describe('writeChangelog', () => { | ||
const changelogPath = '/workspaces/gitmoji-changelog/CHANGELOG.md'; | ||
|
||
let fsMock: typeof jest; | ||
|
||
beforeAll(() => { | ||
fsMock = jest.mock('node:fs/promises'); | ||
}); | ||
|
||
afterEach(() => { | ||
(fs.writeFile as unknown as jest.SpyInstance).mockRestore(); | ||
(fs.readFile as unknown as jest.SpyInstance).mockRestore(); | ||
}); | ||
|
||
afterAll(() => { | ||
fsMock.restoreAllMocks(); | ||
}); | ||
|
||
it('should write initial changelog containing heading Changelog', async () => { | ||
fs.writeFile = jest.fn().mockResolvedValue(Promise.resolve()); | ||
fs.readFile = jest.fn().mockRejectedValue(new Error('Should not be called')); | ||
|
||
await writeChangelog(changelogPath, 'Initial', true); | ||
|
||
expect(fs.writeFile).toHaveBeenCalledTimes(1); | ||
expect(fs.writeFile).toHaveBeenCalledWith(changelogPath, `# Changelog\n\nInitial`, { encoding: 'utf-8' }); | ||
}); | ||
|
||
it('should append changelog', async () => { | ||
fs.writeFile = jest.fn().mockResolvedValue(Promise.resolve()); | ||
fs.readFile = jest.fn().mockResolvedValue('# Changelog\n\nInitial'); | ||
|
||
await writeChangelog(changelogPath, 'Added', false); | ||
|
||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.readFile).toHaveBeenCalledWith(changelogPath, { encoding: 'utf-8' }); | ||
expect(fs.writeFile).toHaveBeenCalledTimes(1); | ||
expect(fs.writeFile).toHaveBeenCalledWith(changelogPath, `# Changelog\n\nAdded\n\nInitial`, { encoding: 'utf-8' }); | ||
}); | ||
|
||
it('should simply log encountered errors', async () => { | ||
fs.writeFile = jest.fn().mockResolvedValue(Promise.resolve()); | ||
fs.readFile = jest.fn().mockRejectedValue(new Error('Oops')); | ||
|
||
await writeChangelog(changelogPath, 'Initial'); | ||
|
||
expect(fs.readFile).toHaveBeenCalledTimes(1); | ||
expect(fs.writeFile).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.