Skip to main content
Glama
version-info.test.js16.5 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { DocumentationService } from '../lib/documentation-service.js'; import { ReleaseNotesParser } from '../lib/release-notes-parser.js'; import { formatVersionInfo } from '../lib/formatters.js'; describe('ReleaseNotesParser', () => { let parser; beforeEach(() => { parser = new ReleaseNotesParser(); }); describe('extractDescription', () => { it('should extract first paragraph from release notes', () => { const body = `This is the first paragraph with important information. This is still part of the first paragraph. # Features - Feature 1 - Feature 2`; const description = parser.extractDescription(body); expect(description).toContain('first paragraph'); expect(description).not.toContain('# Features'); }); it('should stop at headers', () => { const body = `Initial description here. # More Content Additional stuff`; const description = parser.extractDescription(body); expect(description).toBe('Initial description here.'); expect(description).not.toContain('# More Content'); }); it('should stop at list items', () => { const body = `Description text. - List item 1 - List item 2`; const description = parser.extractDescription(body); expect(description).toBe('Description text.'); expect(description).not.toContain('List item'); }); it('should limit to 300 characters', () => { const longText = 'A'.repeat(500); const body = longText; const description = parser.extractDescription(body); expect(description.length).toBeLessThanOrEqual(300); }); it('should return fallback for empty body', () => { const description = parser.extractDescription(''); expect(description).toBe('No description available in release notes.'); }); }); describe('extractFeatures', () => { it('should extract [FEATURE] and [ENHANCEMENT] tagged items from Ember CHANGELOG', () => { const body = `### CHANGELOG - [#20950](https://github.com/emberjs/ember.js/pull/20950) [FEATURE] Add new @cached decorator - [#20951](https://github.com/emberjs/ember.js/pull/20951) [ENHANCEMENT] Improve build times - [#20952](https://github.com/emberjs/ember.js/pull/20952) [BUGFIX] Fix routing issue`; const features = parser.extractFeatures(body); expect(features).toHaveLength(2); expect(features[0]).toContain('@cached decorator'); expect(features[1]).toContain('build times'); expect(features).not.toContain('routing issue'); }); it('should filter out very short items', () => { const body = `### CHANGELOG - [#1](https://github.com/emberjs/ember.js/pull/1) [FEATURE] Short - [#2](https://github.com/emberjs/ember.js/pull/2) [FEATURE] This is a proper feature description`; const features = parser.extractFeatures(body); expect(features).toHaveLength(1); expect(features[0]).toContain('proper feature'); }); it('should filter out very long items', () => { const body = `### CHANGELOG - [#1](https://github.com/emberjs/ember.js/pull/1) [FEATURE] ${'A'.repeat(250)} - [#2](https://github.com/emberjs/ember.js/pull/2) [FEATURE] Normal feature`; const features = parser.extractFeatures(body); expect(features).toHaveLength(1); expect(features[0]).toBe('Normal feature'); }); it('should limit to 10 features', () => { const items = Array.from({ length: 15 }, (_, i) => `- [#${i}](https://github.com/emberjs/ember.js/pull/${i}) [FEATURE] Feature number ${i + 1}` ).join('\n'); const body = `### CHANGELOG\n\n${items}`; const features = parser.extractFeatures(body); expect(features).toHaveLength(10); }); it('should return empty array when no features found', () => { const body = `### CHANGELOG - [#1](https://github.com/emberjs/ember.js/pull/1) [BUGFIX] Fix something - [#2](https://github.com/emberjs/ember.js/pull/2) [BUGFIX] Fix another thing`; const features = parser.extractFeatures(body); expect(features).toHaveLength(0); }); it('should extract from Ember CHANGELOG format with PR links', () => { const body = `### CHANGELOG - [#20950](https://github.com/emberjs/ember.js/pull/20950) / [#20961](https://github.com/emberjs/ember.js/pull/20961) / [#20963](https://github.com/emberjs/ember.js/pull/20963) [FEATURE] Upgrade glimmer-vm to build in Tracked Collections (previously provided by \`tracked-built-ins\`) per [RFC #1068](https://rfcs.emberjs.com/id/1068-tracked-collections). - [#20962](https://github.com/emberjs/ember.js/pull/20962) / [#20966](https://github.com/emberjs/ember.js/pull/20966) / [#20974](https://github.com/emberjs/ember.js/pull/20974) [FEATURE] Add \`renderComponent\` per [RFC #1099](https://rfcs.emberjs.com/id/1099-renderComponent). - [#20957](https://github.com/emberjs/ember.js/pull/20957) / [#20960](https://github.com/emberjs/ember.js/pull/20960) Add TS 5.8, 5.9 to the TS test matrix - [#20988](https://github.com/emberjs/ember.js/pull/20988) [BUGFIX] Drop unnecessary package ember-cli-htmlbars-inline-precompile from component-test blueprint`; const features = parser.extractFeatures(body); expect(features).toHaveLength(2); expect(features[0]).toContain('Upgrade glimmer-vm'); expect(features[0]).toContain('Tracked Collections'); expect(features[1]).toContain('renderComponent'); expect(features).not.toContain('BUGFIX'); }); }); describe('extractBugFixes', () => { it('should extract [BUGFIX] tagged items from Ember CHANGELOG', () => { const body = `### CHANGELOG - [#1](https://github.com/emberjs/ember.js/pull/1) [BUGFIX] Fix component rendering - [#2](https://github.com/emberjs/ember.js/pull/2) [FEATURE] Add new feature - [#3](https://github.com/emberjs/ember.js/pull/3) [BUGFIX] Resolve async issue`; const fixes = parser.extractBugFixes(body); expect(fixes).toHaveLength(2); expect(fixes[0]).toContain('component rendering'); expect(fixes[1]).toContain('async issue'); }); it('should limit to 10 fixes', () => { const items = Array.from({ length: 15 }, (_, i) => `- [#${i}](https://github.com/emberjs/ember.js/pull/${i}) [BUGFIX] Fix issue ${i + 1}` ).join('\n'); const body = `### CHANGELOG\n\n${items}`; const fixes = parser.extractBugFixes(body); expect(fixes).toHaveLength(10); }); it('should return empty array when no fixes found', () => { const body = `### CHANGELOG\n\n- [#1](https://github.com/emberjs/ember.js/pull/1) [FEATURE] New feature`; const fixes = parser.extractBugFixes(body); expect(fixes).toHaveLength(0); }); it('should extract from Ember CHANGELOG format with PR links', () => { const body = `### CHANGELOG - [#20950](https://github.com/emberjs/ember.js/pull/20950) [FEATURE] Upgrade glimmer-vm - [#20988](https://github.com/emberjs/ember.js/pull/20988) [BUGFIX] Drop unnecessary package ember-cli-htmlbars-inline-precompile from component-test blueprint - [#20989](https://github.com/emberjs/ember.js/pull/20989) / [#20990](https://github.com/emberjs/ember.js/pull/20990) [BUGFIX] Fix memory leak in router service`; const fixes = parser.extractBugFixes(body); expect(fixes).toHaveLength(2); expect(fixes[0]).toContain('Drop unnecessary package'); expect(fixes[1]).toContain('Fix memory leak'); }); }); describe('extractBreakingChanges', () => { it('should extract [BREAKING] tagged items from Ember CHANGELOG', () => { const body = `### CHANGELOG - [#1](https://github.com/emberjs/ember.js/pull/1) [BREAKING] Change API interface - [#2](https://github.com/emberjs/ember.js/pull/2) [FEATURE] Add new feature - [#3](https://github.com/emberjs/ember.js/pull/3) [BREAKING] Remove old API`; const breaking = parser.extractBreakingChanges(body); expect(breaking).toHaveLength(2); expect(breaking[0]).toContain('API interface'); expect(breaking[1]).toContain('Remove old API'); }); it('should limit to 10 breaking changes', () => { const items = Array.from({ length: 15 }, (_, i) => `- [#${i}](https://github.com/emberjs/ember.js/pull/${i}) [BREAKING] Breaking change ${i + 1}` ).join('\n'); const body = `### CHANGELOG\n\n${items}`; const breaking = parser.extractBreakingChanges(body); expect(breaking).toHaveLength(10); }); it('should return empty array when no breaking changes found', () => { const body = `### CHANGELOG\n\n- [#1](https://github.com/emberjs/ember.js/pull/1) [FEATURE] New feature`; const breaking = parser.extractBreakingChanges(body); expect(breaking).toHaveLength(0); }); }); }); describe('DocumentationService - Version Methods', () => { let service; beforeEach(() => { service = new DocumentationService(); // Mock parseDocumentation to avoid needing full docs service.parseDocumentation('# api-docs\n\nSome content'); }); describe('formatReleaseInfo', () => { it('should format complete release information', () => { const release = { tag_name: 'v5.4.0', published_at: '2024-01-15T10:00:00Z', body: `### CHANGELOG - [#1](https://github.com/emberjs/ember.js/pull/1) [FEATURE] Add new component API - [#2](https://github.com/emberjs/ember.js/pull/2) [FEATURE] Improve TypeScript support - [#3](https://github.com/emberjs/ember.js/pull/3) [BUGFIX] Fix memory leak - [#4](https://github.com/emberjs/ember.js/pull/4) [BUGFIX] Resolve routing issue - [#5](https://github.com/emberjs/ember.js/pull/5) [BREAKING] Remove deprecated methods`, html_url: 'https://github.com/emberjs/ember.js/releases/tag/v5.4.0' }; const recentReleases = [ { tag_name: 'v5.3.0', published_at: '2024-01-01T10:00:00Z', html_url: 'https://github.com/emberjs/ember.js/releases/tag/v5.3.0' } ]; const info = service.formatReleaseInfo(release, '5.4.0', recentReleases); expect(info.current).toBe('5.4.0'); expect(info.releaseDate).toBe('2024-01-15'); expect(info.features).toHaveLength(2); expect(info.bugFixes).toHaveLength(2); expect(info.breakingChanges).toHaveLength(1); expect(info.releaseNotesUrl).toBe('https://github.com/emberjs/ember.js/releases/tag/v5.4.0'); expect(info.recentReleases).toHaveLength(1); expect(info.recentReleases[0].version).toBe('5.3.0'); }); it('should handle empty release body', () => { const release = { tag_name: 'v5.4.0', published_at: '2024-01-15T10:00:00Z', body: '', html_url: 'https://github.com/emberjs/ember.js/releases/tag/v5.4.0' }; const info = service.formatReleaseInfo(release, '5.4.0'); expect(info.current).toBe('5.4.0'); expect(info.features).toHaveLength(0); expect(info.bugFixes).toHaveLength(0); expect(info.breakingChanges).toHaveLength(0); expect(info.description).toBe('No description available in release notes.'); }); it('should not include recentReleases when not provided', () => { const release = { tag_name: 'v5.4.0', published_at: '2024-01-15T10:00:00Z', body: 'Release notes', html_url: 'https://github.com/emberjs/ember.js/releases/tag/v5.4.0' }; const info = service.formatReleaseInfo(release, '5.4.0'); expect(info.recentReleases).toBeUndefined(); }); }); describe('getFallbackVersionInfo', () => { it('should return fallback info with empty data arrays', () => { const info = service.getFallbackVersionInfo(); expect(info.current).toBeDefined(); expect(info.description).toBe('Unable to fetch release information from GitHub.'); expect(info.features).toEqual([]); expect(info.bugFixes).toEqual([]); expect(info.breakingChanges).toEqual([]); expect(info.note).toContain('currently unavailable'); expect(info.links).toBeDefined(); }); it('should use provided version', () => { const info = service.getFallbackVersionInfo('5.4.0'); expect(info.current).toBe('5.4.0'); expect(info.releaseNotesUrl).toContain('5.4.0'); }); it('should extract version from API docs when available', () => { const mockDoc = `# api-docs { "data": { "id": "ember-5.4.0-Component" } }`; service.parseDocumentation(mockDoc); const info = service.getFallbackVersionInfo(); expect(info.current).toBe('5.4.0'); }); }); }); describe('formatVersionInfo', () => { it('should format complete version info', () => { const versionInfo = { current: '5.4.0', releaseDate: '2024-01-15', description: 'Major release with new features', features: ['New component API', 'TypeScript improvements'], bugFixes: ['Fix memory leak', 'Resolve routing issue'], breakingChanges: ['Remove deprecated methods'], recentReleases: [ { version: '5.3.0', date: '2024-01-01', url: 'https://github.com/emberjs/ember.js/releases/tag/v5.3.0' } ], migrationGuide: 'See migration guide at...', releaseNotesUrl: 'https://github.com/emberjs/ember.js/releases/tag/v5.4.0', blogPost: 'https://blog.emberjs.com/ember-5-4', links: ['https://guides.emberjs.com', 'https://api.emberjs.com'] }; const formatted = formatVersionInfo(versionInfo); expect(formatted).toContain('**Version:** 5.4.0'); expect(formatted).toContain('(Released: 2024-01-15)'); expect(formatted).toContain('Major release'); expect(formatted).toContain('## Features & Enhancements'); expect(formatted).toContain('New component API'); expect(formatted).toContain('## Bug Fixes'); expect(formatted).toContain('Fix memory leak'); expect(formatted).toContain('## ⚠️ Breaking Changes'); expect(formatted).toContain('Remove deprecated methods'); expect(formatted).toContain('## Recent Releases'); expect(formatted).toContain('5.3.0'); expect(formatted).toContain('## Migration Guide'); expect(formatted).toContain('**Release Notes:**'); expect(formatted).toContain('**Blog Posts:**'); }); it('should handle minimal version info', () => { const versionInfo = { current: '5.4.0', description: 'Release description', features: [], bugFixes: [], breakingChanges: [], links: [] }; const formatted = formatVersionInfo(versionInfo); expect(formatted).toContain('**Version:** 5.4.0'); expect(formatted).toContain('Release description'); expect(formatted).not.toContain('## Features & Enhancements'); expect(formatted).not.toContain('## Bug Fixes'); expect(formatted).not.toContain('## Breaking Changes'); }); it('should display note when present', () => { const versionInfo = { current: 'unknown', description: 'Unable to fetch data', features: [], bugFixes: [], breakingChanges: [], note: 'Release information is currently unavailable', links: [] }; const formatted = formatVersionInfo(versionInfo); expect(formatted).toContain('> **Note:**'); expect(formatted).toContain('currently unavailable'); }); it('should handle missing optional fields', () => { const versionInfo = { current: '5.4.0', description: 'Basic release', features: [], bugFixes: [], breakingChanges: [], links: [] }; const formatted = formatVersionInfo(versionInfo); expect(formatted).not.toContain('Released:'); expect(formatted).not.toContain('**Release Notes:**'); expect(formatted).not.toContain('**Blog Posts:**'); expect(formatted).not.toContain('## Recent Releases'); }); it('should format recent releases list', () => { const versionInfo = { current: '5.4.0', description: 'Latest release', features: [], bugFixes: [], breakingChanges: [], recentReleases: [ { version: '5.3.0', date: '2024-01-01', url: 'https://example.com/5.3.0' }, { version: '5.2.0', date: null, url: 'https://example.com/5.2.0' } ], links: [] }; const formatted = formatVersionInfo(versionInfo); expect(formatted).toContain('## Recent Releases'); expect(formatted).toContain('**5.3.0** (2024-01-01)'); expect(formatted).toContain('**5.2.0** - https://example.com/5.2.0'); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/NullVoxPopuli/ember-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server