apisurf
Version:
Analyze API surface changes between npm package versions to catch breaking changes
122 lines (121 loc) • 5.72 kB
JavaScript
import { describe, it, expect } from '@jest/globals';
import { formatOutput } from './formatOutput.js';
describe('formatOutput', () => {
it('returns success message when no breaking changes', () => {
const result = {
hasBreakingChanges: false,
packages: [],
summary: 'No breaking changes detected',
semverImpact: { minimumBump: 'patch', reason: 'No changes detected' }
};
const output = formatOutput(result, 'console', false);
expect(output).toMatch(/✅ No changes detected/);
});
it('returns JSON when format is json', () => {
const result = {
hasBreakingChanges: false,
packages: [],
summary: 'No breaking changes detected',
semverImpact: { minimumBump: 'patch', reason: 'No changes detected' }
};
const output = formatOutput(result, 'json', false);
const parsed = JSON.parse(output);
expect(parsed.hasBreakingChanges).toBe(false);
expect(parsed.summary).toBe('No breaking changes detected');
});
it('returns markdown format when requested', () => {
const result = {
hasBreakingChanges: false,
packages: [],
summary: 'No changes',
semverImpact: { minimumBump: 'patch', reason: 'No changes detected' }
};
const output = formatOutput(result, 'md', false);
expect(output).toMatch(/## API Surface Analysis/);
expect(output).toMatch(/✅ No changes detected/);
});
it('console output has proper blank line spacing', () => {
const result = {
hasBreakingChanges: true,
packages: [{
name: '@test/package',
path: 'npm:@test/package',
breakingChanges: [{
type: 'export-removed',
description: "Removed export 'SomeFunction'",
before: 'SomeFunction'
}, {
type: 'type-changed',
description: "Function 'calculateTotal' changed parameter 1 type",
before: 'amount: number',
after: 'amount: string | number'
}],
nonBreakingChanges: [{
type: 'export-added',
description: "Added export 'NewFunction'",
details: 'NewFunction'
}]
}],
summary: 'Breaking changes detected',
semverImpact: { minimumBump: 'major', reason: 'Breaking changes detected' }
};
const output = formatOutput(result, 'console', false);
const lines = output.split('\n');
// Find key sections
const breakingChangesIndex = lines.findIndex(line => line.includes('❌ Breaking changes:'));
const nonBreakingChangesIndex = lines.findIndex(line => line.includes('✅ Non-breaking changes:'));
// Verify blank line before breaking changes section
expect(lines[breakingChangesIndex - 1]).toBe('');
// Verify blank line between breaking and non-breaking changes
expect(lines[nonBreakingChangesIndex - 1]).toBe('');
// Verify no verbose Before/After details are shown
const beforeLines = lines.filter(line => line.includes('Before:'));
const afterLines = lines.filter(line => line.includes('After:'));
expect(beforeLines.length).toBe(0);
expect(afterLines.length).toBe(0);
});
it('verbose mode shows Before/After details', () => {
const result = {
hasBreakingChanges: true,
packages: [{
name: '@test/package',
path: 'npm:@test/package',
breakingChanges: [{
type: 'type-changed',
description: "Function 'calculateTotal' changed parameter 1 type",
before: 'amount: number',
after: 'amount: string | number'
}],
nonBreakingChanges: []
}],
summary: 'Breaking changes detected',
semverImpact: { minimumBump: 'major', reason: 'Breaking changes detected' }
};
const output = formatOutput(result, 'console', true); // verbose = true
// Verify Before/After details are shown in verbose mode
expect(output).toMatch(/Before: amount: number/);
expect(output).toMatch(/After: amount: string \| number/);
});
it('shows breaking changes when detected', () => {
const result = {
hasBreakingChanges: true,
packages: [{
name: 'test-package',
path: './packages/test-package',
breakingChanges: [{
type: 'export-removed',
description: 'Removed export: oldFunction',
before: 'export { oldFunction }',
after: undefined
}],
nonBreakingChanges: []
}],
summary: 'Breaking changes detected',
semverImpact: { minimumBump: 'major', reason: 'Breaking changes detected' }
};
const output = formatOutput(result, 'console', false);
expect(output).toMatch(/❌ Breaking changes:/);
// Package name appears in different context, test the actual change instead
expect(output).toMatch(/Removed export: oldFunction/);
});
});