import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { enableAutoUnmount } from '@vue/test-utils' import PluginsView from '../../src/views/PluginsView.vue' import { settingsPath } from '../../src/state/useSettings' import type { InstalledPlugin, PluginsOverview, SettingsEntry } from '../../src/api/types' import { API, mountApp, requestsTo, resetState, type Route } from '../helpers' const PLUGINS = `${API}/plugins` function installed(overrides: Partial & { id: string }): InstalledPlugin { return { name: '', description: '', version: '', source: '', module: '', package: '', listed: false, latest: '', latestFramework: '', updateAvailable: false, updateCommand: '', ...overrides, } } function overview(overrides: Partial = {}): PluginsOverview { return { framework: 'v0.6.0', registry: { enabled: false, status: 'disabled' }, plugins: [ installed({ id: 'acme.blog', name: 'Blog', description: 'Posts and categories.', version: 'v0.1.0', source: 'module', module: 'example.test/acme-blog', package: 'example.test/acme-blog', }), installed({ id: 'acme.local', source: 'local' }), ], ...overrides, } } const envelope = (data: unknown) => ({ body: { data, meta: {} } }) async function openPlugins(data: PluginsOverview = overview(), routes: Record = {}) { return mountApp('/system/plugins', { [`GET ${PLUGINS}`]: envelope(data), ...routes }, { attach: true }) } beforeEach(() => { resetState() }) afterEach(() => { document.body.innerHTML = '' }) enableAutoUnmount(afterEach) describe('PluginsView', () => { it('renders the installed plugins from GET /plugins', async () => { const { wrapper, calls } = await openPlugins() expect(wrapper.findComponent(PluginsView).exists()).toBe(true) expect(requestsTo(calls, 'GET', PLUGINS)).toHaveLength(1) expect(wrapper.find('h1').text()).toBe('Wtyczki') expect(wrapper.find('[data-plugins-framework]').text()).toBe('Framework v0.6.0') const rows = wrapper.findAll('[data-plugin]') expect(rows.map((row) => row.attributes('data-plugin'))).toEqual(['acme.blog', 'acme.local']) const blog = rows[0]! expect(blog.find('[data-plugin-name]').text()).toBe('Blog') expect(blog.find('[data-plugin-version]').text()).toBe('v0.1.0') expect(blog.find('[data-plugin-source]').text()).toBe('ModuĊ‚ Go') // Registry and plugin text is text, never HTML. expect(blog.find('[data-plugin-description]').text()).toBe('Posts and categories.') expect(blog.find('b').exists()).toBe(false) const local = rows[1]! expect(local.find('[data-plugin-name]').text()).toBe('acme.local') expect(local.find('[data-plugin-version]').text()).toBe('Brak wersji') expect(local.find('[data-plugin-source]').text()).toBe('W aplikacji') }) it('shows an error when the list cannot be loaded', async () => { const { wrapper } = await openPlugins(overview(), { [`GET ${PLUGINS}`]: { status: 403, body: { error: { code: 'forbidden', message: 'Forbidden', details: {} } } }, }) expect(wrapper.find('[data-plugins-failed]').exists()).toBe(true) expect(wrapper.findAll('[data-plugin]')).toHaveLength(0) }) it('opens from the settings entry path', () => { const entry: SettingsEntry = { code: 'plugins', label: 'Wtyczki', description: '', category: 'System', icon: 'puzzle', order: 120, keywords: [], model: '', controller: '', path: '/system/plugins', } expect(settingsPath(entry)).toBe('/system/plugins') }) })