Every document-scoped key now goes through docKey(), which hangs it off the tab's document id; the first document keeps the empty id, so existing browser copies read back unchanged. A fresh tab takes up the most recently used document no live tab holds, else starts a new one — one signed timestamp per document (edentext-live@id) says which, written only by the tab holding it. Snapshots and embedded fonts get a database per document. The picture store stays shared and content-addressed, so its sweep now drops only keys no stored document names any more, instead of the open document's own.
63 lines
2.7 KiB
TypeScript
63 lines
2.7 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
import { stashImages, putImages, IDB_SRC } from '../../src/lib/storage/imageStore';
|
|
import { fakeIndexedDb } from '../fakeIdb';
|
|
|
|
const big = (fill: string) => `data:image/png;base64,${fill.repeat(5000)}`;
|
|
|
|
const doc = (...content: any[]) => ({ type: 'doc', content });
|
|
const img = (src: string) => ({ type: 'image', attrs: { src, width: 100 } });
|
|
const para = (...content: any[]) => ({ type: 'paragraph', content });
|
|
|
|
describe('stashImages', () => {
|
|
it('replaces a large picture and keeps its other attrs', () => {
|
|
const src = big('A');
|
|
const json: any = doc(para(img(src)));
|
|
const { json: slim, blobs } = stashImages(json);
|
|
const out = (slim as any).content[0].content[0];
|
|
expect(out.attrs.src.startsWith(IDB_SRC)).toBe(true);
|
|
expect(out.attrs.width).toBe(100);
|
|
expect([...blobs.values()]).toEqual([src]);
|
|
// The caller's JSON is what the editor is still holding.
|
|
expect(json.content[0].content[0].attrs.src).toBe(src);
|
|
});
|
|
|
|
it('leaves an icon and a non-data src inline', () => {
|
|
const { json: slim, blobs } = stashImages(doc(para(img('data:image/png;base64,AAAA'), img('https://x/y.png'))));
|
|
expect(blobs.size).toBe(0);
|
|
expect((slim as any).content[0].content[0].attrs.src).toBe('data:image/png;base64,AAAA');
|
|
expect((slim as any).content[0].content[1].attrs.src).toBe('https://x/y.png');
|
|
});
|
|
|
|
it('reaches a picture at any depth and dedupes by content', () => {
|
|
const src = big('B');
|
|
const cell = (...c: any[]) => ({ type: 'tableCell', content: c });
|
|
const { json: slim, blobs } = stashImages(doc(
|
|
{ type: 'table', content: [{ type: 'tableRow', content: [cell(para(img(src)))] }] },
|
|
{ type: 'textBox', content: [para(img(src))] },
|
|
));
|
|
expect(blobs.size).toBe(1);
|
|
const keys = JSON.stringify(slim).match(new RegExp(`${IDB_SRC}[a-z0-9]+`, 'g'));
|
|
expect(keys?.length).toBe(2);
|
|
expect(new Set(keys).size).toBe(1);
|
|
});
|
|
|
|
it('gives two different pictures two keys', () => {
|
|
const { blobs } = stashImages(doc(para(img(big('C')), img(big('D')))));
|
|
expect(blobs.size).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('putImages', () => {
|
|
it('sweeps what no document names any more, and nothing else', async () => {
|
|
const idb = fakeIndexedDb();
|
|
vi.stubGlobal('indexedDB', { open: idb.open });
|
|
localStorage.clear();
|
|
// A second document, stored by another tab and not open here.
|
|
localStorage.setItem('edentext-doc@d2', JSON.stringify({ attrs: { src: `${IDB_SRC}theirs` } }));
|
|
idb.data.set('theirs', big('B'));
|
|
idb.data.set('orphan', big('C'));
|
|
await putImages(new Map([['mine', big('A')]]));
|
|
expect([...idb.data.keys()].sort()).toEqual(['mine', 'theirs']);
|
|
});
|
|
});
|