feat: add plugin marketplace renderer
This commit is contained in:
1 parent
8b6824a8ba
commit
97c9ad1aad
27 files changed
+1585
-23
No files matched your search
@@ -0,0 +1,89 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createPluginMarketplaceStore } from '@/stores/plugin-marketplace';
|
||||
import type { MarketplaceLibraryProjection, MarketplaceLibrarySnapshot } from '@/lib/plugin-marketplace';
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
let reject!: (reason?: unknown) => void;
|
||||
const promise = new Promise<T>((yes, no) => { resolve = yes; reject = no; });
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
function library(title: string): MarketplaceLibrarySnapshot {
|
||||
return {
|
||||
total: 1, stale: false, fetchedAt: 1,
|
||||
items: [{
|
||||
pluginId: 'makelore.notes', title, summary: 'Notes', category: 'tools',
|
||||
acquisition: 'free', acquisitionMode: 'user_acquired', catalogStatus: 'active',
|
||||
runtimeStatus: 'enabled', acquiredAt: '2026-08-28T00:00:00Z', removedAt: null,
|
||||
stableVersion: '1.0.0', betaVersion: null,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
const projection = (title: string): MarketplaceLibraryProjection => ({
|
||||
library: library(title), installations: [],
|
||||
});
|
||||
|
||||
describe('plugin Marketplace store', () => {
|
||||
it('prevents stale A → B → A account loads from committing', async () => {
|
||||
const requests = [deferred<MarketplaceLibraryProjection>(), deferred<MarketplaceLibraryProjection>(), deferred<MarketplaceLibraryProjection>()];
|
||||
const readLibrary = vi.fn(() => requests[readLibrary.mock.calls.length - 1].promise);
|
||||
const store = createPluginMarketplaceStore({ readLibrary });
|
||||
|
||||
store.getState().activateAccount('account-a');
|
||||
const a1 = store.getState().loadLibrary();
|
||||
store.getState().activateAccount('account-b');
|
||||
const b = store.getState().loadLibrary();
|
||||
store.getState().activateAccount('account-a');
|
||||
const a2 = store.getState().loadLibrary();
|
||||
|
||||
requests[2].resolve(projection('A newest'));
|
||||
await a2;
|
||||
requests[1].resolve(projection('B stale'));
|
||||
requests[0].resolve(projection('A oldest'));
|
||||
await Promise.all([a1, b]);
|
||||
|
||||
expect(store.getState().accountKey).toBe('account-a');
|
||||
expect(store.getState().library?.items[0].title).toBe('A newest');
|
||||
});
|
||||
|
||||
it('clears account projections immediately on logout', async () => {
|
||||
const store = createPluginMarketplaceStore({ readLibrary: vi.fn().mockResolvedValue(projection('A')) });
|
||||
store.getState().activateAccount('account-a');
|
||||
await store.getState().loadLibrary();
|
||||
store.getState().activateAccount(null);
|
||||
expect(store.getState()).toMatchObject({ accountKey: null, library: null, installations: {} });
|
||||
});
|
||||
|
||||
it('does not let a load started before a mutation overwrite the mutation result', async () => {
|
||||
const stale = deferred<MarketplaceLibraryProjection>();
|
||||
const store = createPluginMarketplaceStore({
|
||||
readLibrary: vi.fn(() => stale.promise),
|
||||
acquire: vi.fn().mockResolvedValue(projection('Acquired')),
|
||||
});
|
||||
store.getState().activateAccount('account-a');
|
||||
const load = store.getState().loadLibrary();
|
||||
await store.getState().acquire('makelore.notes');
|
||||
stale.resolve(projection('Old'));
|
||||
await load;
|
||||
expect(store.getState().library?.items[0].title).toBe('Acquired');
|
||||
});
|
||||
|
||||
it('calls only the selected state mutation', async () => {
|
||||
const acquire = vi.fn().mockResolvedValue(projection('Acquired'));
|
||||
const install = vi.fn().mockResolvedValue({ status: 'installed', pluginId: 'makelore.notes', version: '1.0.0' });
|
||||
const setProjectEnabled = vi.fn();
|
||||
const store = createPluginMarketplaceStore({ acquire, install });
|
||||
store.getState().activateAccount('account-a');
|
||||
|
||||
await store.getState().acquire('makelore.notes');
|
||||
expect(acquire).toHaveBeenCalledOnce();
|
||||
expect(install).not.toHaveBeenCalled();
|
||||
expect(setProjectEnabled).not.toHaveBeenCalled();
|
||||
|
||||
await store.getState().install('makelore.notes');
|
||||
expect(install).toHaveBeenCalledOnce();
|
||||
expect(setProjectEnabled).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user