mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-05 05:05:08 +02:00
test: gallery generation — sessions, dates, corruption, empty state
7 tests: empty dir, nonexistent dir, single session with approved variant, multiple sessions sorted newest-first, corrupted approved.json handled gracefully, session without approved.json, self-contained HTML (no external dependencies). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Tests for the $D gallery command — design history timeline generation.
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
||||
import { generateGalleryHtml } from '../src/gallery';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
let tmpDir: string;
|
||||
|
||||
function createTestPng(filePath: string): void {
|
||||
const png = Buffer.from(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/58BAwAI/AL+hc2rNAAAAABJRU5ErkJggg==',
|
||||
'base64'
|
||||
);
|
||||
fs.writeFileSync(filePath, png);
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
tmpDir = '/tmp/gallery-test-' + Date.now();
|
||||
fs.mkdirSync(tmpDir, { recursive: true });
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe('Gallery generation', () => {
|
||||
test('empty directory returns "No history" page', () => {
|
||||
const emptyDir = path.join(tmpDir, 'empty');
|
||||
fs.mkdirSync(emptyDir, { recursive: true });
|
||||
|
||||
const html = generateGalleryHtml(emptyDir);
|
||||
expect(html).toContain('No design history yet');
|
||||
expect(html).toContain('/design-shotgun');
|
||||
});
|
||||
|
||||
test('nonexistent directory returns "No history" page', () => {
|
||||
const html = generateGalleryHtml('/nonexistent/path');
|
||||
expect(html).toContain('No design history yet');
|
||||
});
|
||||
|
||||
test('single session with approved variant', () => {
|
||||
const sessionDir = path.join(tmpDir, 'designs', 'homepage-20260327');
|
||||
fs.mkdirSync(sessionDir, { recursive: true });
|
||||
|
||||
createTestPng(path.join(sessionDir, 'variant-A.png'));
|
||||
createTestPng(path.join(sessionDir, 'variant-B.png'));
|
||||
createTestPng(path.join(sessionDir, 'variant-C.png'));
|
||||
|
||||
fs.writeFileSync(path.join(sessionDir, 'approved.json'), JSON.stringify({
|
||||
approved_variant: 'B',
|
||||
feedback: 'Great spacing and colors',
|
||||
date: '2026-03-27T12:00:00Z',
|
||||
screen: 'homepage',
|
||||
}));
|
||||
|
||||
const html = generateGalleryHtml(path.join(tmpDir, 'designs'));
|
||||
expect(html).toContain('Design History');
|
||||
expect(html).toContain('1 exploration');
|
||||
expect(html).toContain('homepage');
|
||||
expect(html).toContain('2026-03-27');
|
||||
expect(html).toContain('approved');
|
||||
expect(html).toContain('Great spacing and colors');
|
||||
// Should have 3 variant images (base64)
|
||||
expect(html).toContain('data:image/png;base64,');
|
||||
});
|
||||
|
||||
test('multiple sessions sorted by date (newest first)', () => {
|
||||
const dir = path.join(tmpDir, 'multi');
|
||||
const session1 = path.join(dir, 'settings-20260301');
|
||||
const session2 = path.join(dir, 'dashboard-20260315');
|
||||
fs.mkdirSync(session1, { recursive: true });
|
||||
fs.mkdirSync(session2, { recursive: true });
|
||||
|
||||
createTestPng(path.join(session1, 'variant-A.png'));
|
||||
createTestPng(path.join(session2, 'variant-A.png'));
|
||||
|
||||
fs.writeFileSync(path.join(session1, 'approved.json'), JSON.stringify({
|
||||
approved_variant: 'A', date: '2026-03-01T12:00:00Z',
|
||||
}));
|
||||
fs.writeFileSync(path.join(session2, 'approved.json'), JSON.stringify({
|
||||
approved_variant: 'A', date: '2026-03-15T12:00:00Z',
|
||||
}));
|
||||
|
||||
const html = generateGalleryHtml(dir);
|
||||
expect(html).toContain('2 explorations');
|
||||
// Dashboard (Mar 15) should appear before settings (Mar 1)
|
||||
const dashIdx = html.indexOf('dashboard');
|
||||
const settingsIdx = html.indexOf('settings');
|
||||
expect(dashIdx).toBeLessThan(settingsIdx);
|
||||
});
|
||||
|
||||
test('corrupted approved.json is handled gracefully', () => {
|
||||
const dir = path.join(tmpDir, 'corrupt');
|
||||
const session = path.join(dir, 'broken-20260327');
|
||||
fs.mkdirSync(session, { recursive: true });
|
||||
|
||||
createTestPng(path.join(session, 'variant-A.png'));
|
||||
fs.writeFileSync(path.join(session, 'approved.json'), 'NOT VALID JSON {{{');
|
||||
|
||||
const html = generateGalleryHtml(dir);
|
||||
// Should still render the session, just without any variant marked as approved
|
||||
expect(html).toContain('Design History');
|
||||
expect(html).toContain('broken');
|
||||
// The class "approved" should not appear on any variant div (only in CSS definition)
|
||||
expect(html).not.toContain('class="gallery-variant approved"');
|
||||
});
|
||||
|
||||
test('session without approved.json still renders', () => {
|
||||
const dir = path.join(tmpDir, 'no-approved');
|
||||
const session = path.join(dir, 'draft-20260327');
|
||||
fs.mkdirSync(session, { recursive: true });
|
||||
|
||||
createTestPng(path.join(session, 'variant-A.png'));
|
||||
createTestPng(path.join(session, 'variant-B.png'));
|
||||
|
||||
const html = generateGalleryHtml(dir);
|
||||
expect(html).toContain('draft');
|
||||
// No variant should be marked as approved
|
||||
expect(html).not.toContain('class="gallery-variant approved"');
|
||||
});
|
||||
|
||||
test('HTML is self-contained (no external dependencies)', () => {
|
||||
const dir = path.join(tmpDir, 'self-contained');
|
||||
const session = path.join(dir, 'test-20260327');
|
||||
fs.mkdirSync(session, { recursive: true });
|
||||
createTestPng(path.join(session, 'variant-A.png'));
|
||||
|
||||
const html = generateGalleryHtml(dir);
|
||||
// No external CSS/JS/image links
|
||||
expect(html).not.toContain('href="http');
|
||||
expect(html).not.toContain('src="http');
|
||||
expect(html).not.toContain('<link');
|
||||
// All images are base64
|
||||
expect(html).toContain('data:image/png;base64,');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user