diff --git a/frontend/e2e/secrets-global.spec.js b/frontend/e2e/secrets-global.spec.js new file mode 100644 index 0000000..551b909 --- /dev/null +++ b/frontend/e2e/secrets-global.spec.js @@ -0,0 +1,50 @@ +import { test, expect } from '@playwright/test'; +import { waitForAppReady, setupConsoleCollector, resetMockState } from './helpers.js'; + +test('global Secrets lists, filters, and creates Deal-scoped secrets', async ({ page }) => { + const consoleCollector = setupConsoleCollector(page); + await resetMockState(page); + await page.goto('/'); + await waitForAppReady(page); + + await page.evaluate(async () => { + const [record, error] = await window.go.api.App.PluginSecretsWrite('verstak.secrets', { + id: 'project.global-secret', + title: 'Project API', + username: 'project-user', + value: 'project-value', + scope: { kind: 'workspace', workspaceRootPath: 'Project' }, + }); + if (error || !record?.id) throw new Error(error || 'could not create a project secret'); + }); + + await page.locator('.sidebar .plugin-item').filter({ hasText: 'Secrets' }).click(); + const secrets = page.locator('.secrets-root'); + await expect(secrets).toBeVisible({ timeout: 10000 }); + await expect(secrets).toContainText('First secret'); + await expect(secrets).toContainText('Project API'); + + await secrets.locator('[data-secret-scope-filter]').selectOption('workspace:Project'); + await expect(secrets).toContainText('Project API'); + await expect(secrets).not.toContainText('First secret'); + + await secrets.locator('[data-secret-search]').fill('no matching secret'); + await expect(secrets).toContainText('No secrets'); + await secrets.locator('[data-secret-search]').fill(''); + + await secrets.locator('[data-secret-scope-filter]').selectOption('all'); + await secrets.getByRole('button', { name: 'New' }).click(); + await secrets.locator('[data-secret-title]').fill('Project deployment token'); + await secrets.locator('[data-secret-value]').fill('deployment-token-value'); + await secrets.locator('[data-secret-scope]').selectOption('workspace'); + await secrets.locator('[data-secret-workspace]').selectOption('Project'); + await secrets.locator('[data-secret-save]').click(); + + await expect.poll(async () => page.evaluate(async () => { + const [records, error] = await window.go.api.App.PluginSecretsList('verstak.secrets'); + if (error) throw new Error(error); + const record = records.find((item) => item.title === 'Project deployment token'); + return record && [record.scope?.kind, record.scope?.workspaceRootPath].join('|'); + })).toBe('workspace|Project'); + consoleCollector.assertNoErrors(); +}); diff --git a/frontend/src/lib/test/wails-mock.js b/frontend/src/lib/test/wails-mock.js index 9a82a7e..e979de2 100644 --- a/frontend/src/lib/test/wails-mock.js +++ b/frontend/src/lib/test/wails-mock.js @@ -602,9 +602,11 @@ import journalSource from '../../../../../verstak-official-plugins/plugins/journ source: 'official', icon: 'key-round', provides: ['secret-store', 'secrets.read-ui', 'secrets.write-ui'], - permissions: ['secrets.read', 'secrets.write', 'ui.register'], + permissions: ['files.read', 'secrets.read', 'secrets.write', 'ui.register'], frontend: { entry: 'frontend/src/index.js' }, contributes: { + views: [{ id: 'verstak.secrets.view', title: 'Secrets', icon: 'key-round', component: 'SecretsView' }], + sidebarItems: [{ id: 'verstak.secrets.sidebar', title: 'Secrets', icon: 'key-round', view: 'verstak.secrets.view', position: 45 }], openProviders: [{ id: 'verstak.secrets.secret', title: 'Secrets',