675 lines
18 KiB
TypeScript
675 lines
18 KiB
TypeScript
import { DEFAULT_CONVENTIONAL_COMMITS_CONFIG } from '../../src/command-line/release/config/conventional-commits';
|
|
import type { GitCommit } from '../../src/command-line/release/utils/git';
|
|
import defaultChangelogRenderer from './index';
|
|
|
|
jest.mock('../../src/project-graph/file-map-utils', () => ({
|
|
createFileMapUsingProjectGraph: jest.fn().mockImplementation(() => {
|
|
return Promise.resolve({
|
|
allWorkspaceFiles: [],
|
|
fileMap: {
|
|
nonProjectFiles: [],
|
|
projectFileMap: {
|
|
'pkg-a': [
|
|
{
|
|
file: 'packages/pkg-a/src/index.ts',
|
|
},
|
|
],
|
|
'pkg-b': [
|
|
{
|
|
file: 'packages/pkg-b/src/index.ts',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
}),
|
|
}));
|
|
|
|
describe('defaultChangelogRenderer()', () => {
|
|
const projectGraph = {
|
|
nodes: {},
|
|
} as any;
|
|
const commits: GitCommit[] = [
|
|
{
|
|
message: 'fix: all packages fixed',
|
|
shortHash: '4130f65',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body: '"\n\nM\tpackages/pkg-a/src/index.ts\nM\tpackages/pkg-b/src/index.ts\n"',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description: 'all packages fixed',
|
|
type: 'fix',
|
|
scope: '',
|
|
references: [
|
|
{
|
|
value: '4130f65',
|
|
type: 'hash',
|
|
},
|
|
],
|
|
isBreaking: false,
|
|
revertedHashes: [],
|
|
affectedFiles: [
|
|
'packages/pkg-a/src/index.ts',
|
|
'packages/pkg-b/src/index.ts',
|
|
],
|
|
},
|
|
{
|
|
message: 'feat(pkg-b): and another new capability',
|
|
shortHash: '7dc5ec3',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body: '"\n\nM\tpackages/pkg-b/src/index.ts\n"',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description: 'and another new capability',
|
|
type: 'feat',
|
|
scope: 'pkg-b',
|
|
references: [
|
|
{
|
|
value: '7dc5ec3',
|
|
type: 'hash',
|
|
},
|
|
],
|
|
isBreaking: false,
|
|
revertedHashes: [],
|
|
affectedFiles: ['packages/pkg-b/src/index.ts'],
|
|
},
|
|
{
|
|
message: 'feat(pkg-a): new hotness',
|
|
shortHash: 'd7a58a2',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body: '"\n\nM\tpackages/pkg-a/src/index.ts\n"',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description: 'new hotness',
|
|
type: 'feat',
|
|
scope: 'pkg-a',
|
|
references: [
|
|
{
|
|
value: 'd7a58a2',
|
|
type: 'hash',
|
|
},
|
|
],
|
|
isBreaking: false,
|
|
revertedHashes: [],
|
|
affectedFiles: ['packages/pkg-a/src/index.ts'],
|
|
},
|
|
{
|
|
message: 'feat(pkg-b): brand new thing',
|
|
shortHash: 'feace4a',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body: '"\n\nM\tpackages/pkg-b/src/index.ts\n"',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description: 'brand new thing',
|
|
type: 'feat',
|
|
scope: 'pkg-b',
|
|
references: [
|
|
{
|
|
value: 'feace4a',
|
|
type: 'hash',
|
|
},
|
|
],
|
|
isBreaking: false,
|
|
revertedHashes: [],
|
|
affectedFiles: ['packages/pkg-b/src/index.ts'],
|
|
},
|
|
{
|
|
message: 'fix(pkg-a): squashing bugs',
|
|
shortHash: '6301405',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body: '"\n\nM\tpackages/pkg-a/src/index.ts\n',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description: 'squashing bugs',
|
|
type: 'fix',
|
|
scope: 'pkg-a',
|
|
references: [
|
|
{
|
|
value: '6301405',
|
|
type: 'hash',
|
|
},
|
|
],
|
|
isBreaking: false,
|
|
revertedHashes: [],
|
|
affectedFiles: ['packages/pkg-a/src/index.ts'],
|
|
},
|
|
];
|
|
|
|
describe('workspaceChangelog', () => {
|
|
it('should generate markdown for all projects by organizing commits by type, then grouped by scope within the type (sorted alphabetically), then chronologically within the scope group', async () => {
|
|
const markdown = await defaultChangelogRenderer({
|
|
projectGraph,
|
|
commits,
|
|
releaseVersion: 'v1.1.0',
|
|
project: null,
|
|
entryWhenNoChanges: false,
|
|
changelogRenderOptions: {
|
|
authors: true,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
});
|
|
expect(markdown).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
|
|
### 🚀 Features
|
|
|
|
- **pkg-a:** new hotness
|
|
- **pkg-b:** brand new thing
|
|
- **pkg-b:** and another new capability
|
|
|
|
### 🩹 Fixes
|
|
|
|
- all packages fixed
|
|
- **pkg-a:** squashing bugs
|
|
|
|
### ❤️ Thank You
|
|
|
|
- James Henry"
|
|
`);
|
|
});
|
|
|
|
it('should not generate a Thank You section when changelogRenderOptions.authors is false', async () => {
|
|
const markdown = await defaultChangelogRenderer({
|
|
projectGraph,
|
|
commits,
|
|
// Major version, should use single # for generated heading
|
|
releaseVersion: 'v1.0.0',
|
|
project: null,
|
|
entryWhenNoChanges: false,
|
|
changelogRenderOptions: {
|
|
authors: false,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
});
|
|
expect(markdown).toMatchInlineSnapshot(`
|
|
"# v1.0.0
|
|
|
|
|
|
### 🚀 Features
|
|
|
|
- **pkg-a:** new hotness
|
|
- **pkg-b:** brand new thing
|
|
- **pkg-b:** and another new capability
|
|
|
|
### 🩹 Fixes
|
|
|
|
- all packages fixed
|
|
- **pkg-a:** squashing bugs"
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe('project level configs', () => {
|
|
it('should generate markdown for the given project by organizing commits by type, then chronologically', async () => {
|
|
const otherOpts = {
|
|
projectGraph,
|
|
commits,
|
|
releaseVersion: 'v1.1.0',
|
|
entryWhenNoChanges: false as const,
|
|
changelogRenderOptions: {
|
|
authors: true,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
};
|
|
|
|
expect(
|
|
await defaultChangelogRenderer({
|
|
...otherOpts,
|
|
project: 'pkg-a',
|
|
})
|
|
).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
|
|
### 🚀 Features
|
|
|
|
- **pkg-a:** new hotness
|
|
|
|
|
|
### 🩹 Fixes
|
|
|
|
- all packages fixed
|
|
|
|
- **pkg-a:** squashing bugs
|
|
|
|
|
|
### ❤️ Thank You
|
|
|
|
- James Henry"
|
|
`);
|
|
|
|
expect(
|
|
await defaultChangelogRenderer({
|
|
...otherOpts,
|
|
project: 'pkg-a',
|
|
// test that the authors option is being respected for project changelogs and therefore no Thank You section exists
|
|
changelogRenderOptions: {
|
|
authors: false,
|
|
},
|
|
})
|
|
).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
|
|
### 🚀 Features
|
|
|
|
- **pkg-a:** new hotness
|
|
|
|
|
|
### 🩹 Fixes
|
|
|
|
- all packages fixed
|
|
|
|
- **pkg-a:** squashing bugs"
|
|
`);
|
|
|
|
expect(
|
|
await defaultChangelogRenderer({
|
|
...otherOpts,
|
|
project: 'pkg-b',
|
|
})
|
|
).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
|
|
### 🚀 Features
|
|
|
|
- **pkg-b:** brand new thing
|
|
|
|
- **pkg-b:** and another new capability
|
|
|
|
|
|
### 🩹 Fixes
|
|
|
|
- all packages fixed
|
|
|
|
|
|
### ❤️ Thank You
|
|
|
|
- James Henry"
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe('entryWhenNoChanges', () => {
|
|
it('should respect the entryWhenNoChanges option for the workspace changelog', async () => {
|
|
const otherOpts = {
|
|
projectGraph,
|
|
commits: [],
|
|
releaseVersion: 'v1.1.0',
|
|
project: null, // workspace changelog
|
|
changelogRenderOptions: {
|
|
authors: true,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
};
|
|
|
|
expect(
|
|
await defaultChangelogRenderer({
|
|
...otherOpts,
|
|
entryWhenNoChanges: 'Nothing at all!',
|
|
})
|
|
).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
Nothing at all!"
|
|
`);
|
|
|
|
expect(
|
|
await defaultChangelogRenderer({
|
|
...otherOpts,
|
|
entryWhenNoChanges: false, // should not create an entry
|
|
})
|
|
).toMatchInlineSnapshot(`""`);
|
|
});
|
|
|
|
it('should respect the entryWhenNoChanges option for project changelogs', async () => {
|
|
const otherOpts = {
|
|
projectGraph,
|
|
commits: [],
|
|
releaseVersion: 'v1.1.0',
|
|
project: 'pkg-a',
|
|
changelogRenderOptions: {
|
|
authors: true,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
};
|
|
|
|
expect(
|
|
await defaultChangelogRenderer({
|
|
...otherOpts,
|
|
entryWhenNoChanges: 'Nothing at all!',
|
|
})
|
|
).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
Nothing at all!"
|
|
`);
|
|
|
|
expect(
|
|
await defaultChangelogRenderer({
|
|
...otherOpts,
|
|
entryWhenNoChanges: false, // should not create an entry
|
|
})
|
|
).toMatchInlineSnapshot(`""`);
|
|
});
|
|
});
|
|
|
|
describe('revert commits', () => {
|
|
it('should generate a Revert section for the changelog if the reverted commit is not part of the same release', async () => {
|
|
const commitsWithOnlyRevert: GitCommit[] = [
|
|
{
|
|
message:
|
|
'Revert "fix(release): do not update dependents when they already use "*" (#20607)"',
|
|
shortHash: '6528e88aa',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body: 'This reverts commit 6d68236d467812aba4557a2bc7f667157de80fdb.\n"\n\nM\tpackages/js/src/generators/release-version/release-version.spec.ts\nM\tpackages/js/src/generators/release-version/release-version.ts\n',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description:
|
|
'Revert "fix(release): do not update dependents when they already use "*" (#20607)"',
|
|
type: 'revert',
|
|
scope: 'release',
|
|
references: [
|
|
{
|
|
type: 'pull-request',
|
|
value: '#20607',
|
|
},
|
|
{
|
|
value: '6528e88aa',
|
|
type: 'hash',
|
|
},
|
|
],
|
|
isBreaking: false,
|
|
revertedHashes: ['6d68236d467812aba4557a2bc7f667157de80fdb'],
|
|
affectedFiles: [
|
|
'packages/js/src/generators/release-version/release-version.spec.ts',
|
|
'packages/js/src/generators/release-version/release-version.ts',
|
|
],
|
|
},
|
|
];
|
|
|
|
const markdown = await defaultChangelogRenderer({
|
|
projectGraph,
|
|
commits: commitsWithOnlyRevert,
|
|
releaseVersion: 'v1.1.0',
|
|
project: null,
|
|
entryWhenNoChanges: false,
|
|
changelogRenderOptions: {
|
|
authors: true,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
});
|
|
|
|
expect(markdown).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
|
|
### ⏪ Revert
|
|
|
|
- **release:** Revert "fix(release): do not update dependents when they already use "*" (#20607)"
|
|
|
|
### ❤️ Thank You
|
|
|
|
- James Henry"
|
|
`);
|
|
});
|
|
|
|
it('should strip both the original commit and its revert if they are both included in the current range of commits', async () => {
|
|
const commitsWithRevertAndOriginal: GitCommit[] = [
|
|
{
|
|
message:
|
|
'Revert "fix(release): do not update dependents when they already use "*" (#20607)"',
|
|
shortHash: '6528e88aa',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body: 'This reverts commit 6d68236d467812aba4557a2bc7f667157de80fdb.\n"\n\nM\tpackages/js/src/generators/release-version/release-version.spec.ts\nM\tpackages/js/src/generators/release-version/release-version.ts\n',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description:
|
|
'Revert "fix(release): do not update dependents when they already use "*" (#20607)"',
|
|
type: 'revert',
|
|
scope: 'release',
|
|
references: [
|
|
{
|
|
type: 'pull-request',
|
|
value: '#20607',
|
|
},
|
|
{
|
|
value: '6528e88aa',
|
|
type: 'hash',
|
|
},
|
|
],
|
|
isBreaking: false,
|
|
revertedHashes: ['6d68236d467812aba4557a2bc7f667157de80fdb'],
|
|
affectedFiles: [
|
|
'packages/js/src/generators/release-version/release-version.spec.ts',
|
|
'packages/js/src/generators/release-version/release-version.ts',
|
|
],
|
|
},
|
|
{
|
|
message:
|
|
'fix(release): do not update dependents when they already use "*" (#20607)',
|
|
shortHash: '6d68236d4',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body: '"\n\nM\tpackages/js/src/generators/release-version/release-version.spec.ts\nM\tpackages/js/src/generators/release-version/release-version.ts\n',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description: 'do not update dependents when they already use "*"',
|
|
type: 'fix',
|
|
scope: 'release',
|
|
references: [
|
|
{
|
|
type: 'pull-request',
|
|
value: '#20607',
|
|
},
|
|
{
|
|
value: '6d68236d4',
|
|
type: 'hash',
|
|
},
|
|
],
|
|
isBreaking: false,
|
|
revertedHashes: [],
|
|
affectedFiles: [
|
|
'packages/js/src/generators/release-version/release-version.spec.ts',
|
|
'packages/js/src/generators/release-version/release-version.ts',
|
|
],
|
|
},
|
|
];
|
|
|
|
const markdown = await defaultChangelogRenderer({
|
|
projectGraph,
|
|
commits: commitsWithRevertAndOriginal,
|
|
releaseVersion: 'v1.1.0',
|
|
project: null,
|
|
entryWhenNoChanges: false,
|
|
changelogRenderOptions: {
|
|
authors: true,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
});
|
|
|
|
expect(markdown).toMatchInlineSnapshot(`""`);
|
|
});
|
|
});
|
|
|
|
describe('breaking changes', () => {
|
|
it('should work for breaking changes with just the ! and no explanation', async () => {
|
|
const breakingChangeCommitWithExplanation: GitCommit = {
|
|
// ! after the type, no BREAKING CHANGE: in the body
|
|
message: 'feat(WebSocketSubject)!: no longer extends `Subject`.',
|
|
shortHash: '54f2f6ed1',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body:
|
|
'M\tpackages/rxjs/src/internal/observable/dom/WebSocketSubject.ts\n' +
|
|
'"',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description: 'no longer extends `Subject`.',
|
|
type: 'feat',
|
|
scope: 'WebSocketSubject',
|
|
references: [{ value: '54f2f6ed1', type: 'hash' }],
|
|
isBreaking: true,
|
|
revertedHashes: [],
|
|
affectedFiles: [
|
|
'packages/rxjs/src/internal/observable/dom/WebSocketSubject.ts',
|
|
],
|
|
};
|
|
|
|
const markdown = await defaultChangelogRenderer({
|
|
projectGraph,
|
|
commits: [breakingChangeCommitWithExplanation],
|
|
releaseVersion: 'v1.1.0',
|
|
project: null,
|
|
entryWhenNoChanges: false,
|
|
changelogRenderOptions: {
|
|
authors: true,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
});
|
|
|
|
expect(markdown).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
|
|
### 🚀 Features
|
|
|
|
- ⚠️ **WebSocketSubject:** no longer extends \`Subject\`.
|
|
|
|
#### ⚠️ Breaking Changes
|
|
|
|
- ⚠️ **WebSocketSubject:** no longer extends \`Subject\`.
|
|
|
|
### ❤️ Thank You
|
|
|
|
- James Henry"
|
|
`);
|
|
});
|
|
|
|
it('should extract the explanation of a breaking change and render it preferentially', async () => {
|
|
const breakingChangeCommitWithExplanation: GitCommit = {
|
|
// No ! after the type, but BREAKING CHANGE: in the body
|
|
message: 'feat(WebSocketSubject): no longer extends `Subject`.',
|
|
shortHash: '54f2f6ed1',
|
|
author: {
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
body:
|
|
'BREAKING CHANGE: `WebSocketSubject` is no longer `instanceof Subject`. Check for `instanceof WebSocketSubject` instead.\n' +
|
|
'"\n' +
|
|
'\n' +
|
|
'M\tpackages/rxjs/src/internal/observable/dom/WebSocketSubject.ts\n' +
|
|
'"',
|
|
authors: [
|
|
{
|
|
name: 'James Henry',
|
|
email: 'jh@example.com',
|
|
},
|
|
],
|
|
description: 'no longer extends `Subject`.',
|
|
type: 'feat',
|
|
scope: 'WebSocketSubject',
|
|
references: [{ value: '54f2f6ed1', type: 'hash' }],
|
|
isBreaking: true,
|
|
revertedHashes: [],
|
|
affectedFiles: [
|
|
'packages/rxjs/src/internal/observable/dom/WebSocketSubject.ts',
|
|
],
|
|
};
|
|
|
|
const markdown = await defaultChangelogRenderer({
|
|
projectGraph,
|
|
commits: [breakingChangeCommitWithExplanation],
|
|
releaseVersion: 'v1.1.0',
|
|
project: null,
|
|
entryWhenNoChanges: false,
|
|
changelogRenderOptions: {
|
|
authors: true,
|
|
},
|
|
conventionalCommitsConfig: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
|
});
|
|
|
|
expect(markdown).toMatchInlineSnapshot(`
|
|
"## v1.1.0
|
|
|
|
|
|
### 🚀 Features
|
|
|
|
- ⚠️ **WebSocketSubject:** no longer extends \`Subject\`.
|
|
|
|
#### ⚠️ Breaking Changes
|
|
|
|
- **WebSocketSubject:** \`WebSocketSubject\` is no longer \`instanceof Subject\`. Check for \`instanceof WebSocketSubject\` instead.
|
|
|
|
### ❤️ Thank You
|
|
|
|
- James Henry"
|
|
`);
|
|
});
|
|
});
|
|
});
|