firstsun-dev_git-files-sync/tests/ui/WhatsNewModal.test.ts
ClaudiaFang 4eebebc765 feat: show new feature tips after update
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
2026-07-13 13:56:38 +00:00

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();
});
});