release: v0.17.0 (#677)
See CHANGELOG.md, section [0.17.0] — 2026-09-23.
This commit is contained in:
141
lib/__tests__/journal-album.test.ts
Normal file
141
lib/__tests__/journal-album.test.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { parseJournalMarkdown, serializeJournalMarkdown, collectAssetIds } from '../journal';
|
||||
import type { JournalBlock } from '../journal';
|
||||
import { expandAlbumBlocks, orderAlbumAssets } from '../journalAlbum';
|
||||
|
||||
const block = (md: string) => parseJournalMarkdown(md).blocks[0];
|
||||
|
||||
describe('album block syntax', () => {
|
||||
it('parses the id and every option', () => {
|
||||
expect(
|
||||
block(
|
||||
['::album abc-123', 'count: 12', 'skip: 2', 'layout: pairs', 'caption: The **day**'].join(
|
||||
'\n',
|
||||
),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'album',
|
||||
albumId: 'abc-123',
|
||||
count: 12,
|
||||
skip: 2,
|
||||
layout: 'pairs',
|
||||
caption: 'The <strong>day</strong>',
|
||||
});
|
||||
});
|
||||
|
||||
it('defaults to grid, all photos, no skip — and allows an empty id placeholder', () => {
|
||||
expect(block('::album')).toEqual({ type: 'album', albumId: '', layout: 'grid' });
|
||||
expect(block('::album abc')).toEqual({ type: 'album', albumId: 'abc', layout: 'grid' });
|
||||
});
|
||||
|
||||
it('ignores malformed options', () => {
|
||||
expect(block('::album abc\ncount: many\nlayout: mosaic\nskip: -1')).toEqual({
|
||||
type: 'album',
|
||||
albumId: 'abc',
|
||||
layout: 'grid',
|
||||
});
|
||||
});
|
||||
|
||||
it('round-trips byte-stable and stays out of the asset ids', () => {
|
||||
const md = ['::album abc-123', 'count: 12', 'skip: 2', 'layout: wide', 'caption: Twelve'].join(
|
||||
'\n',
|
||||
);
|
||||
const parsed = parseJournalMarkdown(md);
|
||||
expect(serializeJournalMarkdown(parsed)).toBe(md);
|
||||
expect(serializeJournalMarkdown(parseJournalMarkdown('::album'))).toBe('::album');
|
||||
expect(collectAssetIds(parsed.blocks)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
const assets = (n: number, type = 'IMAGE') =>
|
||||
Array.from({ length: n }, (_, i) => ({ id: `p${i + 1}`, type }));
|
||||
|
||||
describe('expandAlbumBlocks', () => {
|
||||
const lookup = (id: string) => (id === 'alb' ? assets(7) : undefined);
|
||||
const albumBlock = (
|
||||
extra: Partial<Extract<JournalBlock, { type: 'album' }>> = {},
|
||||
): JournalBlock => ({
|
||||
type: 'album',
|
||||
albumId: 'alb',
|
||||
layout: 'grid',
|
||||
...extra,
|
||||
});
|
||||
|
||||
it('turns an album into one grid, keeping the surrounding blocks', () => {
|
||||
const before: JournalBlock = { type: 'heading', level: 2, text: 'Party' };
|
||||
const { blocks, albumAssetIds } = expandAlbumBlocks(
|
||||
[before, albumBlock({ caption: 'All of it' })],
|
||||
lookup,
|
||||
);
|
||||
expect(blocks).toEqual([
|
||||
before,
|
||||
{
|
||||
type: 'photo-grid',
|
||||
assetIds: ['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7'],
|
||||
caption: 'All of it',
|
||||
},
|
||||
]);
|
||||
expect(albumAssetIds).toEqual(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7']);
|
||||
});
|
||||
|
||||
it('slices with count and skip', () => {
|
||||
const { blocks } = expandAlbumBlocks([albumBlock({ count: 3, skip: 2 })], lookup);
|
||||
expect(blocks).toEqual([{ type: 'photo-grid', assetIds: ['p3', 'p4', 'p5'] }]);
|
||||
});
|
||||
|
||||
it('lays out pairs with an odd tail and puts the caption last', () => {
|
||||
const { blocks } = expandAlbumBlocks(
|
||||
[albumBlock({ count: 5, layout: 'pairs', caption: 'c' })],
|
||||
lookup,
|
||||
);
|
||||
expect(blocks).toEqual([
|
||||
{ type: 'photo-pair', assetIds: ['p1', 'p2'] },
|
||||
{ type: 'photo-pair', assetIds: ['p3', 'p4'] },
|
||||
{ type: 'photo', assetId: 'p5', layout: 'contained', caption: 'c' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('lays out wide as one photo per row', () => {
|
||||
const { blocks } = expandAlbumBlocks([albumBlock({ count: 2, layout: 'wide' })], lookup);
|
||||
expect(blocks).toEqual([
|
||||
{ type: 'photo', assetId: 'p1', layout: 'wide' },
|
||||
{ type: 'photo', assetId: 'p2', layout: 'wide' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('falls back to a pair or a single photo when a grid has too few', () => {
|
||||
expect(expandAlbumBlocks([albumBlock({ count: 2 })], lookup).blocks).toEqual([
|
||||
{ type: 'photo-pair', assetIds: ['p1', 'p2'] },
|
||||
]);
|
||||
expect(expandAlbumBlocks([albumBlock({ count: 1 })], lookup).blocks).toEqual([
|
||||
{ type: 'photo', assetId: 'p1', layout: 'contained' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('drops an empty id, an unknown album and an empty slice', () => {
|
||||
expect(expandAlbumBlocks([albumBlock({ albumId: '' })], lookup).blocks).toEqual([]);
|
||||
expect(expandAlbumBlocks([albumBlock({ albumId: 'nope' })], lookup).blocks).toEqual([]);
|
||||
expect(expandAlbumBlocks([albumBlock({ skip: 99 })], lookup).blocks).toEqual([]);
|
||||
});
|
||||
|
||||
it('puts the gallery manual order first, then the album order', () => {
|
||||
const { blocks } = expandAlbumBlocks([albumBlock({ count: 4 })], lookup, {
|
||||
alb: ['p6', 'p2', 'ghost'],
|
||||
});
|
||||
expect(blocks).toEqual([{ type: 'photo-grid', assetIds: ['p6', 'p2', 'p1', 'p3'] }]);
|
||||
});
|
||||
|
||||
it('keeps only images and videos', () => {
|
||||
const mixed = [...assets(2), { id: 'x', type: 'OTHER' }, { id: 'v', type: 'VIDEO' }];
|
||||
const { blocks } = expandAlbumBlocks([albumBlock()], () => mixed);
|
||||
expect(blocks).toEqual([{ type: 'photo-grid', assetIds: ['p1', 'p2', 'v'] }]);
|
||||
});
|
||||
|
||||
it('orderAlbumAssets pins first occurrences only', () => {
|
||||
expect(orderAlbumAssets(assets(3), ['p3', 'p3', 'p1']).map((a) => a.id)).toEqual([
|
||||
'p3',
|
||||
'p1',
|
||||
'p2',
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user