mirror of
https://github.com/firstsun-dev/git-files-sync.git
synced 2026-07-22 06:54:27 +00:00
Adds a "what's new" modal shown once after the plugin updates to a new version, so users don't miss what changed: - New src/changelog.ts: a hand-curated CHANGELOG array (distinct from the auto-generated CHANGELOG.md, which lists every commit) where entries can be marked `notable` so they're called out separately from minor fixes, per the issue's clarification comment. - New src/utils/version.ts: compareVersions() does numeric per-segment comparison (so "1.10.0" sorts after "1.9.0", unlike a plain string compare). - getUnseenReleases() filters+sorts the changelog to what's newer than a given "last seen" version, extracted as a pure/testable function rather than inlined in main.ts (which isn't unit-tested in this repo). - New settings field lastSeenVersion, persisted the same way as other settings. On a fresh install (empty lastSeenVersion) it's just recorded silently — no modal, since there's nothing to compare against. On an actual version bump, WhatsNewModal shows the unseen releases' highlights, with a "View full changelog" link out to CHANGELOG.md and a "Got it" dismiss; the version is recorded either way so the tip only ever shows once per upgrade. - The whole check is wrapped in try/catch so a malformed version string can never break plugin startup. Closes #39
81 lines
3 KiB
TypeScript
81 lines
3 KiB
TypeScript
import { beforeAll, describe, it, expect, vi, afterEach } from 'vitest';
|
|
import { App } from 'obsidian';
|
|
import { WhatsNewModal } from '../../src/ui/WhatsNewModal';
|
|
import { createContainer, setupObsidianDOM } from './setup-dom';
|
|
import type { ChangelogRelease } from '../../src/changelog';
|
|
|
|
describe('WhatsNewModal', () => {
|
|
beforeAll(() => { setupObsidianDOM(); });
|
|
|
|
afterEach(() => { vi.restoreAllMocks(); });
|
|
|
|
const releases: ChangelogRelease[] = [
|
|
{
|
|
version: '1.3.0',
|
|
entries: [
|
|
{ text: 'Notable highlight', notable: true },
|
|
{ text: 'Minor fix' },
|
|
],
|
|
},
|
|
{
|
|
version: '1.2.1',
|
|
entries: [
|
|
{ text: 'Older release note' },
|
|
],
|
|
},
|
|
];
|
|
|
|
function openModal(rels: ChangelogRelease[]): HTMLElement {
|
|
const modal = new WhatsNewModal(new App(), rels);
|
|
modal.contentEl = createContainer();
|
|
modal.onOpen();
|
|
return modal.contentEl;
|
|
}
|
|
|
|
it('renders a heading for each release version', () => {
|
|
const contentEl = openModal(releases);
|
|
const headings = Array.from(contentEl.querySelectorAll('h4')).map(h => h.textContent);
|
|
expect(headings).toEqual(['v1.3.0', 'v1.2.1']);
|
|
});
|
|
|
|
it('renders every entry as a list item', () => {
|
|
const contentEl = openModal(releases);
|
|
const items = Array.from(contentEl.querySelectorAll('li')).map(li => li.textContent);
|
|
expect(items).toEqual(['Notable highlight', 'Minor fix', 'Older release note']);
|
|
});
|
|
|
|
it('marks notable entries distinctly from non-notable ones', () => {
|
|
const contentEl = openModal(releases);
|
|
const items = Array.from(contentEl.querySelectorAll('li'));
|
|
expect(items[0]?.classList.contains('ssv-whats-new-notable')).toBe(true);
|
|
expect(items[1]?.classList.contains('ssv-whats-new-notable')).toBe(false);
|
|
});
|
|
|
|
it('opens the full changelog URL when "View full changelog" is clicked', () => {
|
|
const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
const contentEl = openModal(releases);
|
|
const buttons = Array.from(contentEl.querySelectorAll('button'));
|
|
const changelogBtn = buttons.find(b => b.textContent?.includes('View full changelog'));
|
|
|
|
changelogBtn?.click();
|
|
|
|
expect(openSpy).toHaveBeenCalledWith(
|
|
'https://github.com/firstsun-dev/git-files-sync/blob/main/CHANGELOG.md',
|
|
'_blank',
|
|
'noopener'
|
|
);
|
|
});
|
|
|
|
it('closes the modal when "Got it" is clicked', () => {
|
|
const modal = new WhatsNewModal(new App(), releases);
|
|
modal.contentEl = createContainer();
|
|
modal.onOpen();
|
|
const closeSpy = vi.spyOn(modal, 'close');
|
|
|
|
const buttons = Array.from(modal.contentEl.querySelectorAll('button'));
|
|
const gotItBtn = buttons.find(b => b.textContent?.includes('Got it'));
|
|
gotItBtn?.click();
|
|
|
|
expect(closeSpy).toHaveBeenCalledOnce();
|
|
});
|
|
});
|