Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169// src/plugin/sidebar/__tests__/sort-mode.test.tsimport { describe, it, expect } from 'vitest'import { SORT_MODES, DEFAULT_SORT_MODE, compareTAbstractFiles, type SortMode, getSortModeMetadata,} from '../sort-mode'import { TFolder, type TFile, createTFile, createTFolder,} from '../../files'
function file(name: string, ctime: number, mtime: number): TFile { return createTFile(name, null, { ctime, mtime, size: 0 })}function folder(name: string): TFolder { return createTFolder(name, null)}
describe('SORT_MODES', () => { it('exposes exactly six modes in the documented order', () => { expect(SORT_MODES).toEqual([ 'name-asc', 'name-desc', 'created-desc', 'created-asc', 'modified-desc', 'modified-asc', ]) })
it('default is name-asc', () => { const expected: SortMode = 'name-asc' expect(DEFAULT_SORT_MODE).toBe(expected) })
it('every mode has metadata with label + lucide icon name + Obsidian icon id', () => { for (const m of SORT_MODES) { const meta = getSortModeMetadata(m) expect(meta.label).toBeTruthy() expect(meta.lucideIcon).toBeTruthy() expect(meta.obsidianIcon).toBeTruthy() } })})
describe('compareTAbstractFiles', () => { const a = file('apple.md', 100, 200) const b = file('banana.md', 50, 300) const c = file('cherry.md', 200, 100)
it('name-asc sorts by basename, case-insensitive', () => { const out = [c, a, b].sort(compareTAbstractFiles('name-asc', false)) expect(out.map((f) => f.name)).toEqual(['apple.md', 'banana.md', 'cherry.md']) })
it('name-desc reverses name-asc', () => { const out = [c, a, b].sort(compareTAbstractFiles('name-desc', false)) expect(out.map((f) => f.name)).toEqual(['cherry.md', 'banana.md', 'apple.md']) })
it('created-desc puts newest-created first; ties broken by basename asc', () => { const out = [a, b, c].sort(compareTAbstractFiles('created-desc', false)) expect(out.map((f) => f.name)).toEqual(['cherry.md', 'apple.md', 'banana.md']) })
it('created-asc inverts created-desc', () => { const out = [a, b, c].sort(compareTAbstractFiles('created-asc', false)) expect(out.map((f) => f.name)).toEqual(['banana.md', 'apple.md', 'cherry.md']) })
it('modified-desc puts most-recently-modified first', () => { const out = [a, b, c].sort(compareTAbstractFiles('modified-desc', false)) expect(out.map((f) => f.name)).toEqual(['banana.md', 'apple.md', 'cherry.md']) })
it('modified-asc inverts modified-desc', () => { const out = [a, b, c].sort(compareTAbstractFiles('modified-asc', false)) expect(out.map((f) => f.name)).toEqual(['cherry.md', 'apple.md', 'banana.md']) })
it('folders-first places folders before files regardless of mode and direction', () => { const f = folder('Notes') const out = [a, f, b].sort(compareTAbstractFiles('name-desc', true)) expect(out[0]).toBe(f) expect(out.slice(1).map((x) => x.name)).toEqual(['banana.md', 'apple.md']) })
it.each([ ['name-asc', false] as const, ['name-desc', false] as const, ['created-desc', true] as const, ['created-asc', true] as const, ['modified-desc', true] as const, ['modified-asc', true] as const, ])( 'folders sort by basename ascending under mode=%s (folders-first invariant, both directions)', (mode, _expectFoldersFirst) => { const f1 = folder('Zeta') const f2 = folder('Alpha') const f3 = folder('Mango') // Vary file stats so the date axis would have an opinion if folders // leaked into it; folders must still appear first, name-ascending. const fa = file('apple.md', 100, 200) const fb = file('banana.md', 50, 300) const out = [fa, f1, fb, f2, f3].sort( compareTAbstractFiles(mode, /*foldersFirst*/ true), ) expect(out.slice(0, 3).map((x) => x.name)).toEqual(['Alpha', 'Mango', 'Zeta']) }, )
it('comparator is stable on equal keys', () => { const a1 = file('same.md', 100, 100) const a2 = file('same.md', 100, 100) const out = [a1, a2].sort(compareTAbstractFiles('name-asc', false)) expect(out[0]).toBe(a1) expect(out[1]).toBe(a2) })
describe('missing-stat fallback (date modes)', () => { // A TAbstractFile-shaped object lacking a `stat` field. Folders carry // no stat in production, but here we use a bare object with just `name` // to stand in for a file whose stat hasn't been hydrated yet. function noStat(name: string): TFile { return { name } as unknown as TFile }
it.each([ ['modified-desc'] as const, ['modified-asc'] as const, ['created-desc'] as const, ['created-asc'] as const, ])( 'puts the with-stat file before the no-stat file under mode=%s', (mode) => { const withStat = file('aaa.md', 100, 100) const ns = noStat('zzz.md') // Implementation contract: aStat missing → returns 1 (a after b); // bStat missing → returns -1 (a before b). So a file WITH stat // always sorts before a file WITHOUT stat, regardless of direction. const out1 = [ns, withStat].sort(compareTAbstractFiles(mode, false)) expect(out1.map((x) => x.name)).toEqual(['aaa.md', 'zzz.md']) const out2 = [withStat, ns].sort(compareTAbstractFiles(mode, false)) expect(out2.map((x) => x.name)).toEqual(['aaa.md', 'zzz.md']) }, )
it.each([ ['modified-desc'] as const, ['modified-asc'] as const, ['created-desc'] as const, ['created-asc'] as const, ])( 'falls back to basename (collator) when BOTH files lack stat under mode=%s', (mode) => { const a = noStat('aaa.md') const z = noStat('zzz.md') const out = [z, a].sort(compareTAbstractFiles(mode, false)) expect(out.map((x) => x.name)).toEqual(['aaa.md', 'zzz.md']) }, ) })})