Files

161 lines
5.0 KiB
TypeScript
Raw Permalink Normal View History

import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { generatePullRequestTitle } from './generatePullRequestTitle.mts';
const added = (addedFiles: string[], title: string) =>
generatePullRequestTitle({ addedFiles, changedFiles: [] }, title);
const changed = (changedFiles: string[], title: string) =>
generatePullRequestTitle({ addedFiles: [], changedFiles }, title);
describe('generatePullRequestTitle', () => {
describe('icon titles', () => {
it('generates a title for a single icon and dedupes svg and json', () => {
assert.equal(
added(['icons/heart.svg', 'icons/heart.json'], 'add heart'),
'feat(icons): added `heart` icon',
);
});
it('lists multiple icons', () => {
assert.equal(
added(['icons/heart.svg', 'icons/heart-off.svg'], 'icons'),
'feat(icons): added `heart`, `heart-off` icons',
);
});
it('summarises many icons', () => {
assert.equal(
added(['icons/a.svg', 'icons/b.svg', 'icons/c.svg', 'icons/d.svg', 'icons/e.svg'], 'x'),
'feat(icons): added `a`, `b`, `c` and 2 more icons',
);
});
it('generates a title for lab icons', () => {
assert.equal(added(['lab/ghost.svg'], 'x'), 'feat(lab): added `ghost` icon');
});
it('returns null when the title is already correct', () => {
assert.equal(added(['icons/heart.svg'], 'feat(icons): added `heart` icon'), null);
});
it('ignores other changed files when an icon is added', () => {
assert.equal(
generatePullRequestTitle(
{ addedFiles: ['icons/heart.svg'], changedFiles: ['docs/guide.md'] },
'x',
),
'feat(icons): added `heart` icon',
);
});
});
describe('scope resolution', () => {
it('uses the package name as scope', () => {
assert.equal(
changed(['packages/lucide-react/src/a.ts'], 'fix: crash'),
'fix(lucide-react): crash',
);
});
it('joins multiple packages with a pipe', () => {
assert.equal(
changed(['packages/vue/src/b.ts', 'packages/lucide-react/src/a.ts'], 'chore: Fix export'),
'chore(lucide-react|@lucide/vue): Fix export',
);
});
it('summarises more than three packages as `packages`', () => {
assert.equal(
changed(
[
'packages/lucide-react/a.ts',
'packages/vue/a.ts',
'packages/svelte/a.ts',
'packages/angular/a.ts',
],
'refactor: shared types',
),
'refactor(packages): shared types',
);
});
it('prefers packages over docs', () => {
assert.equal(
changed(['docs/guide/react.md', 'packages/lucide-react/src/a.ts'], 'feat: new prop'),
'feat(lucide-react): new prop',
);
});
it('joins docs and site scopes', () => {
assert.equal(
changed(['docs/guide/a.md', 'docs/.vitepress/theme/b.vue'], 'docs: update'),
'docs(docs|site): update',
);
});
it('uses `metadata` for icon and category json files', () => {
assert.equal(changed(['icons/heart.json'], 'fix: tags'), 'fix(metadata): tags');
assert.equal(changed(['categories/shapes.json'], 'fix: category'), 'fix(metadata): category');
});
it('prefers icons over metadata for changed icons', () => {
assert.equal(
changed(['icons/heart.svg', 'icons/heart.json'], 'fix: heart'),
'fix(icons): heart',
);
});
it('supports @lucide/solid', () => {
assert.equal(changed(['packages/solid/src/a.ts'], 'feat: x'), 'feat(@lucide/solid): x');
});
it('does not match lucide-react for lucide-react-native files', () => {
assert.equal(
changed(['packages/lucide-react-native/src/a.ts'], 'fix: x'),
'fix(lucide-react-native): x',
);
});
it('returns null when no rule matches', () => {
assert.equal(changed(['README.md'], 'chore: readme'), null);
});
});
describe('prefix titles', () => {
const files = ['packages/lucide-react/src/a.ts'];
it('keeps a custom scope', () => {
assert.equal(changed(files, 'fix(react): crash'), null);
});
it('extends a generated scope', () => {
assert.equal(
changed([...files, 'packages/vue/src/a.ts'], 'fix(lucide-react): crash'),
'fix(lucide-react|@lucide/vue): crash',
);
});
it('normalises the type casing', () => {
assert.equal(changed(files, 'Fix: crash'), 'fix(lucide-react): crash');
});
it('normalises whitespace and keeps a custom scope', () => {
assert.equal(changed(files, 'feat (react): x'), 'feat(react): x');
});
it('keeps the breaking-change marker', () => {
assert.equal(changed(files, 'feat!: drop old api'), 'feat(lucide-react)!: drop old api');
});
it('returns null for an empty subject', () => {
assert.equal(changed(files, 'fix:'), null);
});
it('falls back to the default type', () => {
assert.equal(changed(files, 'Fix crash'), 'chore(lucide-react): Fix crash');
});
});
});