73 lines
2.6 KiB
TypeScript
73 lines
2.6 KiB
TypeScript
|
|
import { describe, it, expect, beforeAll } from 'vitest';
|
||
|
|
import type { Guide, Step, Screenshot } from '../src/shared/types';
|
||
|
|
|
||
|
|
class MockFileReader {
|
||
|
|
result: string | ArrayBuffer | null = null;
|
||
|
|
onload: ((ev: ProgressEvent) => void) | null = null;
|
||
|
|
onerror: ((ev: ProgressEvent) => void) | null = null;
|
||
|
|
|
||
|
|
readAsDataURL(_blob: Blob) {
|
||
|
|
setTimeout(() => {
|
||
|
|
this.result = `data:image/jpeg;base64,dGVzdA==`;
|
||
|
|
if (this.onload) this.onload({} as ProgressEvent);
|
||
|
|
}, 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
beforeAll(() => {
|
||
|
|
// @ts-expect-error replace FileReader for test environment
|
||
|
|
globalThis.FileReader = MockFileReader;
|
||
|
|
});
|
||
|
|
|
||
|
|
let exportGuideAsMarkdown: typeof import('../src/export/markdown-export').exportGuideAsMarkdown;
|
||
|
|
|
||
|
|
beforeAll(async () => {
|
||
|
|
const mod = await import('../src/export/markdown-export');
|
||
|
|
exportGuideAsMarkdown = mod.exportGuideAsMarkdown;
|
||
|
|
});
|
||
|
|
|
||
|
|
const guide: Guide = {
|
||
|
|
id: 'g1',
|
||
|
|
title: 'My Workflow Guide',
|
||
|
|
createdAt: 1700000000000,
|
||
|
|
updatedAt: 1700000000000,
|
||
|
|
stepIds: ['s1', 's2'],
|
||
|
|
};
|
||
|
|
|
||
|
|
const steps: Step[] = [
|
||
|
|
{ id: 's1', guideId: 'g1', index: 0, description: 'Open the dashboard', action: 'click', url: 'https://example.com', timestamp: 1700000001000, screenshotId: 'sc1' },
|
||
|
|
{ id: 's2', guideId: 'g1', index: 1, description: 'Click settings', action: 'click', url: 'https://example.com', timestamp: 1700000002000 },
|
||
|
|
];
|
||
|
|
|
||
|
|
const screenshots: Map<string, Screenshot> = new Map([
|
||
|
|
['s1', { id: 'sc1', stepId: 's1', blob: new Blob(['img'], { type: 'image/jpeg' }), mimeType: 'image/jpeg', width: 800, height: 600 }],
|
||
|
|
]);
|
||
|
|
|
||
|
|
describe('exportGuideAsMarkdown', () => {
|
||
|
|
it('returns a string starting with # {guide.title}', async () => {
|
||
|
|
const md = await exportGuideAsMarkdown(guide, steps, screenshots);
|
||
|
|
expect(md.trimStart()).toMatch(/^# My Workflow Guide/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('contains ## Step N: {description} format for each step', async () => {
|
||
|
|
const md = await exportGuideAsMarkdown(guide, steps, screenshots);
|
||
|
|
expect(md).toContain('## Step 1: Open the dashboard');
|
||
|
|
expect(md).toContain('## Step 2: Click settings');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('embeds screenshot as  for steps with screenshots', async () => {
|
||
|
|
const md = await exportGuideAsMarkdown(guide, steps, screenshots);
|
||
|
|
expect(md).toContain(';
|
||
|
|
});
|
||
|
|
|
||
|
|
it('steps without screenshots omit image line', async () => {
|
||
|
|
const md = await exportGuideAsMarkdown(guide, steps, screenshots);
|
||
|
|
expect(md).not.toContain('![Step 2]');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('ends with *Generated by Mimik*', async () => {
|
||
|
|
const md = await exportGuideAsMarkdown(guide, steps, screenshots);
|
||
|
|
expect(md.trimEnd()).toMatch(/\*Generated by Mimik\*$/);
|
||
|
|
});
|
||
|
|
});
|