import {describe, expect, it} from 'vitest'; import type {PluginImportResult, PluginPermission} from '@xlibrary/plugin-sdk'; import { createBrowserPageCapture, createPluginTestHost, runGameSourceResolve, runGameSourceSearch, runPluginInvocation, } from '@xlibrary/plugin-sdk/testing'; import plugin from '../src/index.js'; const permissions: PluginPermission[] = [ 'library.games.read', 'library.games.write', 'sessions.events.read', 'tracking.providers', 'game.custom-fields', 'game.actions', 'plugin.storage', 'plugin.cache', 'plugin.settings', 'plugin.auth', 'network.http', 'notifications.show', 'jobs.run', 'game-sources.providers', 'browser.page.capture', 'imports.sources', 'exports.targets', 'backup.contribute', 'filters.facets', 'ui.game-details.sidebar', 'ui.game-details.header', 'ui.dashboard.widget', 'ui.library.toolbar', 'ui.settings.section', ]; function createHost() { return createPluginTestHost({ permissions, settings: {endpoint: 'https://example.com'}, storage: {'reference:backup-marker': {before: true}}, importInputs: {'input-1': JSON.stringify({games: [{name: 'Imported Game', tags: ['reference']} ]})}, auth: {'reference-account': {connected: false}}, network: () => ({status: 200, contentType: 'application/json', body: '{}'}), authRequest: () => ({status: 200, contentType: 'application/json', body: '{}'}), }); } describe('XLibrary plugin reference', () => { it('covers provider search, resolve and browser capture', async () => { const host = createHost(); await expect(runGameSourceSearch(plugin, { sourceId: 'reference-catalog', query: 'reference', limit: 20, }, host)).resolves.toMatchObject({candidates: [{title: 'Reference result for reference'}]}); await expect(runGameSourceResolve(plugin, { sourceId: 'reference-catalog', url: 'https://catalog.example/games/reference-game', }, host)).resolves.toMatchObject({name: 'Reference Game (reference-game)'}); await expect(runGameSourceResolve(plugin, { sourceId: 'reference-catalog', page: createBrowserPageCapture({ url: 'https://catalog.example/games/reference-game', title: 'Captured Reference Game', text: 'Captured description', }), }, host)).resolves.toMatchObject({name: 'Captured Reference Game'}); }); it('covers the standard import and export pipeline', async () => { const host = createHost(); const imported = await runPluginInvocation(plugin, { method: 'imports.parse', payload: {sourceId: 'reference-json', input: {id: 'input-1', fileName: 'games.json', size: 64}}, }, host) as PluginImportResult; expect(imported.games).toEqual([{name: 'Imported Game', tags: ['reference']}]); await expect(runPluginInvocation(plugin, { method: 'exports.serialize', payload: {targetId: 'reference-json', games: imported.games}, }, host)).resolves.toEqual({completed: true}); expect(host.exportChunks).toHaveLength(1); }); it('covers UI, filters, actions, tracking and jobs', async () => { const host = createHost(); const game = { id: 'game-1', name: 'Reference Game', tags: ['reference'], customTags: [], providers: [], status: 'playing', }; await expect(runPluginInvocation(plugin, { method: 'filters.evaluate', payload: {facets: [{id: 'has-reference-tag', selection: true}], games: [game]}, }, host)).resolves.toEqual({matches: {'has-reference-tag': ['game-1']}}); await expect(runPluginInvocation(plugin, { method: 'ui.render', payload: {contributionId: 'sidebar', slot: 'ui.game-details.sidebar', context: {gameId: game.id, game}}, }, host)).resolves.toMatchObject({ nodes: expect.arrayContaining([expect.objectContaining({type: 'notice'})]), }); await expect(runPluginInvocation(plugin, { method: 'game-actions.execute', payload: {actionId: 'mark-reference', game, dryRun: true}, }, host)).resolves.toMatchObject({gamePatch: {addTags: ['reference']}}); await expect(runPluginInvocation(plugin, { method: 'tracking.session-event', payload: { providerId: 'reference-tracking', event: {type: 'ended', duration: 120, session: {id: 'session-1', gameId: game.id, startedAt: 1, lastSeen: 2, trackingMode: 'manual'}, game}, }, }, host)).resolves.toEqual({fieldValues: {'reference-score': 2}}); await expect(runPluginInvocation(plugin, { method: 'jobs.run', payload: {jobId: 'reference-job', trigger: 'startup'}, }, host)).resolves.toBeUndefined(); }); it('covers backup, auth-backed UI actions and migrations', async () => { const host = createHost(); await expect(runPluginInvocation(plugin, { method: 'backup.capture', payload: {dataVersion: 1}, }, host)).resolves.toEqual({dataVersion: 1, data: {marker: {before: true}}}); await expect(runPluginInvocation(plugin, { method: 'backup.restore', payload: {dataVersion: 1, data: {marker: {after: true}}, dryRun: false}, }, host)).resolves.toEqual({restored: true}); await expect(runPluginInvocation(plugin, { method: 'ui.action', payload: {contributionId: 'sidebar', actionId: 'check-remote', context: {}}, }, host)).resolves.toMatchObject({panel: {title: 'Action completed'}}); await expect(runPluginInvocation(plugin, { method: 'settings.migrate', payload: {fromDataVersion: 0, values: {}}, }, host)).resolves.toEqual({values: {endpoint: 'https://example.com'}}); await expect(runPluginInvocation(plugin, { method: 'storage.migrate', payload: {fromDataVersion: 0, values: {}}, }, host)).resolves.toEqual({values: {migratedAtVersion: 1}}); await expect(runPluginInvocation(plugin, { method: 'lifecycle.deactivate', payload: {}, }, host)).resolves.toBeUndefined(); }); });