semantic-release-unsquash
Version:
A tiny wrapper for commit-analyzer and release-notes-generator which works with squashed MRs
530 lines (503 loc) • 15.9 kB
JavaScript
const { getUnsquashedCommits } = require('./get-unsquashed-commits');
describe(getUnsquashedCommits.name, () => {
it('should return unsquashed commits', () => {
const commits = [
{
subject: 'Init',
body:
'* fix: restrict job branches\n' +
'\n' +
'Foobar string\n' +
'\n' +
'* ci: use image with git\n' +
'\n' +
'* ci: fix job\n' +
'\n' +
'* feat: initial commit\n',
hash: '0d63669040808f99dde29bc1b08346ab4e572572',
message:
'Init\n' +
'\n' +
'* fix: restrict job branches\n' +
'\n' +
'Foobar string\n' +
'\n' +
'* ci: use image with git\n' +
'\n' +
'* ci: fix job\n' +
'\n' +
'* feat: initial commit',
},
];
const context = { commits };
expect(getUnsquashedCommits(context)).toMatchInlineSnapshot(`
[
{
"body": "",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "",
"subject": "",
},
{
"body": "Foobar string",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "fix: restrict job branches
Foobar string",
"subject": "fix: restrict job branches",
},
{
"body": "",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "ci: use image with git",
"subject": "ci: use image with git",
},
{
"body": "",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "ci: fix job",
"subject": "ci: fix job",
},
{
"body": "",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "feat: initial commit",
"subject": "feat: initial commit",
},
]
`);
});
it('should leave commit unchanged when the first non-empty line is not a list item', () => {
const commits = [
{
subject: 'docs: update README',
body:
'docs: update README with deployment steps\n' +
'\n' +
'* chore: unrelated bullet that should be ignored\n',
hash: 'd5b14fc5f5438214cb643b846579c94f0bd68e24',
message:
'docs: update README\n' +
'\n' +
'docs: update README with deployment steps\n' +
'\n' +
'* chore: unrelated bullet that should be ignored',
},
];
const context = { commits };
expect(getUnsquashedCommits(context)).toEqual(commits);
});
it('should split when the first list item follows leading blank lines', () => {
const commits = [
{
subject: 'chore: release v1.2.3',
body: '\n* fix: correct typo in docs\n* feat: add deployment script',
hash: 'd5b14fc5f5438214cb643b846579c94f0bd68e24',
message:
'chore: release v1.2.3\n' +
'\n' +
'* fix: correct typo in docs\n' +
'* feat: add deployment script',
},
];
const context = { commits };
expect(getUnsquashedCommits(context)).toEqual([
{
...commits[0],
subject: '',
body: '',
message: '',
},
{
...commits[0],
subject: 'fix: correct typo in docs',
body: '',
message: 'fix: correct typo in docs',
},
{
...commits[0],
subject: 'feat: add deployment script',
body: '',
message: 'feat: add deployment script',
},
]);
});
it('should not split when only "-" bullets are present without custom prefixes', () => {
const commits = [
{
subject: 'docs: changelog updates',
body: '- fix: adjust workflow docs\n- chore: cleanup unused scripts\n',
hash: '0d63669040808f99dde29bc1b08346ab4e572572',
message:
'docs: changelog updates\n' +
'\n' +
'- fix: adjust workflow docs\n' +
'- chore: cleanup unused scripts\n',
},
];
const context = { commits };
expect(getUnsquashedCommits(context)).toEqual(commits);
});
describe('with pluginConfig', () => {
it('should return unsquashed commits with sectionRegex', () => {
const commits = [
{
subject: 'Init',
body:
'### Changes\n' +
'\n' +
'* fix: restrict job branches\n' +
'\n' +
'Foobar string\n' +
'\n' +
'* ci: use image with git\n' +
'\n' +
'* ci: fix job\n' +
'\n' +
'* feat: initial commit\n',
hash: '0d63669040808f99dde29bc1b08346ab4e572572',
message:
'Init\n' +
'\n' +
'* fix: restrict job branches\n' +
'\n' +
'Foobar string\n' +
'\n' +
'* ci: use image with git\n' +
'\n' +
'* ci: fix job\n' +
'\n' +
'* feat: initial commit',
},
];
const context = { commits };
const pluginConfig = {
// Another sample may be ### Changes(?:\n|\\n|\\\\n){2}([\s\S]*?)(?=###)
sectionRegexStr: '### Changes(?:\n|\\n|\\\\n){2}([\\s\\S]*?)(?=$)',
};
expect(getUnsquashedCommits(context, pluginConfig))
.toMatchInlineSnapshot(`
[
{
"body": "",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "",
"subject": "",
},
{
"body": "Foobar string",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "fix: restrict job branches
Foobar string",
"subject": "fix: restrict job branches",
},
{
"body": "",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "ci: use image with git",
"subject": "ci: use image with git",
},
{
"body": "",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "ci: fix job",
"subject": "ci: fix job",
},
{
"body": "",
"hash": "0d63669040808f99dde29bc1b08346ab4e572572",
"message": "feat: initial commit",
"subject": "feat: initial commit",
},
]
`);
});
it('should fall back to whole match when sectionRegex has no capture group', () => {
const commits = [
{
subject: 'Init',
body:
'### Changes\n' +
'\n' +
'* refactor: adjust workflow (f8c544a)\n' +
'\n' +
'* docs: document shared module (40129ad)\n',
hash: 'b671c1a316c6205ac8fb73313f9c2fbeeb66b1d5',
message: 'Init',
},
];
const context = { commits };
const pluginConfig = {
sectionRegexStr: String.raw`### Changes(?:\n|\r\n){2}[\s\S]*?(?=$)`,
};
expect(getUnsquashedCommits(context, pluginConfig)).toEqual([
{
...commits[0],
subject: '',
body: '',
message: '',
},
{
...commits[0],
subject: 'refactor: adjust workflow (f8c544a)',
body: '',
message: 'refactor: adjust workflow (f8c544a)',
},
{
...commits[0],
subject: 'docs: document shared module (40129ad)',
body: '',
message: 'docs: document shared module (40129ad)',
},
]);
});
it('should fall back to sectionHeading when sectionRegexStr is invalid', () => {
const commits = [
{
subject: 'refactor: invalid regex',
body:
'### Changes\n' +
'\n' +
'* fix: patch issue (1234567)\n' +
'* feat: add improvement (7654321)\n',
hash: 'd5b14fc5f5438214cb643b846579c94f0bd68e24',
message: 'refactor: invalid regex',
},
];
const context = { commits };
const pluginConfig = {
sectionRegexStr: '[',
sectionHeading: '### Changes',
};
expect(getUnsquashedCommits(context, pluginConfig)).toEqual([
{
...commits[0],
subject: '',
body: '',
message: '',
},
{
...commits[0],
subject: 'fix: patch issue (1234567)',
body: '',
message: 'fix: patch issue (1234567)',
},
{
...commits[0],
subject: 'feat: add improvement (7654321)',
body: '',
message: 'feat: add improvement (7654321)',
},
]);
});
it('should use sectionHeading when provided', () => {
const commits = [
{
subject: 'refactor: move workflow',
body:
'refactor: move workflow to script for better testing process (#2)\n' +
'### Overview\n' +
'\n' +
'Details about the changes\n' +
'\n' +
'### Changes\n' +
'- refactor: move workflow to script for better testing process (f8c544a)\n' +
'- docs: added JSDoc blocks describing function purpose, inputs, and outputs so the shared action module is self-documented (40129ad)\n',
hash: 'd5b14fc5f5438214cb643b846579c94f0bd68e24',
message: 'squashed commit',
},
];
const context = { commits };
const pluginConfig = {
sectionHeading: '### Changes',
listItemPrefixes: ['- '],
};
expect(getUnsquashedCommits(context, pluginConfig)).toEqual([
{
...commits[0],
subject: '',
body: '',
message: '',
},
{
...commits[0],
subject:
'refactor: move workflow to script for better testing process (f8c544a)',
body: '',
message:
'refactor: move workflow to script for better testing process (f8c544a)',
},
{
...commits[0],
subject:
'docs: added JSDoc blocks describing function purpose, inputs, and outputs so the shared action module is self-documented (40129ad)',
body: '',
message:
'docs: added JSDoc blocks describing function purpose, inputs, and outputs so the shared action module is self-documented (40129ad)',
},
]);
});
it('should support multi-line commits and configurable list item prefixes', () => {
const commits = [
{
subject: 'feature: summary',
body:
'### Changes\n' +
'\n' +
'* refactor: move workflow to script for better testing process (f8c544a)\n' +
' \n' +
' body of the first commit\n' +
'- docs: added JSDoc blocks describing function purpose, inputs, and outputs so the shared action module is self-documented (40129ad)\n' +
' \n' +
' body of the second commit\n',
hash: 'a6c8de336a46f19e8bad017d53e8eee329b40621',
message: 'feature summary',
},
];
const context = { commits };
const pluginConfig = {
sectionHeading: '### Changes',
listItemPrefixes: ['* ', '- '],
};
expect(getUnsquashedCommits(context, pluginConfig)).toEqual([
{
...commits[0],
subject: '',
body: '',
message: '',
},
{
...commits[0],
subject:
'refactor: move workflow to script for better testing process (f8c544a)',
body: 'body of the first commit',
message:
'refactor: move workflow to script for better testing process (f8c544a)\n \n body of the first commit',
},
{
...commits[0],
subject:
'docs: added JSDoc blocks describing function purpose, inputs, and outputs so the shared action module is self-documented (40129ad)',
body: 'body of the second commit',
message:
'docs: added JSDoc blocks describing function purpose, inputs, and outputs so the shared action module is self-documented (40129ad)\n \n body of the second commit',
},
]);
});
it('should fall back to defaults when listItemRegexStr is invalid', () => {
const commits = [
{
subject: 'feat: fallback',
body: '* fix: address crash\n* chore: tidy scripts\n',
hash: 'd5b14fc5f5438214cb643b846579c94f0bd68e24',
message:
'feat: fallback\n' +
'\n' +
'* fix: address crash\n' +
'* chore: tidy scripts\n',
},
];
const context = { commits };
const pluginConfig = {
listItemRegexStr: '[',
};
expect(getUnsquashedCommits(context, pluginConfig)).toEqual([
{
...commits[0],
subject: '',
body: '',
message: '',
},
{
...commits[0],
subject: 'fix: address crash',
body: '',
message: 'fix: address crash',
},
{
...commits[0],
subject: 'chore: tidy scripts',
body: '',
message: 'chore: tidy scripts',
},
]);
});
it('should support custom listItemRegexStr for non-space bullet style', () => {
const commits = [
{
subject: 'feat: compact bullets',
body:
'### Changes\n' +
'\n' +
'*refactor: adjust workflow (f8c544a)\n' +
'*docs: document shared module (40129ad)\n',
hash: 'fccf5a587416e71d23aeb502f38fd2af455a8287',
message: 'feat compact bullets',
},
];
const context = { commits };
const pluginConfig = {
sectionHeading: '### Changes',
listItemRegexStr: String.raw`^\s*\*`,
};
expect(getUnsquashedCommits(context, pluginConfig)).toEqual([
{
...commits[0],
subject: '',
body: '',
message: '',
},
{
...commits[0],
subject: 'refactor: adjust workflow (f8c544a)',
body: '',
message: 'refactor: adjust workflow (f8c544a)',
},
{
...commits[0],
subject: 'docs: document shared module (40129ad)',
body: '',
message: 'docs: document shared module (40129ad)',
},
]);
});
it('should honor listItemPrefix when a single custom marker is provided', () => {
const commits = [
{
subject: 'feat: custom prefix',
body:
'### Changes\n' +
'\n' +
'-> feat: add timeline widget (1234567)\n' +
'-> fix: stop crash on safari (89abcde)\n',
hash: 'e77d38081d69ee053f6bf17d0fd68de6a0c623f8',
message: 'custom prefix commit',
},
];
const context = { commits };
const pluginConfig = {
sectionHeading: '### Changes',
listItemPrefix: '-> ',
};
expect(getUnsquashedCommits(context, pluginConfig)).toEqual([
{
...commits[0],
subject: '',
body: '',
message: '',
},
{
...commits[0],
subject: 'feat: add timeline widget (1234567)',
body: '',
message: 'feat: add timeline widget (1234567)',
},
{
...commits[0],
subject: 'fix: stop crash on safari (89abcde)',
body: '',
message: 'fix: stop crash on safari (89abcde)',
},
]);
});
});
});