test: cover secret workbench navigation
This commit is contained in:
parent
fc82b3e6b1
commit
fe64e1f822
|
|
@ -36,6 +36,69 @@ test.describe('F: Default Editor Plugin', () => {
|
||||||
await expect(textarea).toHaveValue('Buy groceries\nWrite tests');
|
await expect(textarea).toHaveValue('Buy groceries\nWrite tests');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('secret link opens its exact secret and closing it restores the note preview', async ({ page }) => {
|
||||||
|
const notePath = 'Notes/Secret Link.md';
|
||||||
|
const noteContent = '# Secret link\n\n[Target secret](verstak-secret://target.secret)';
|
||||||
|
|
||||||
|
await page.evaluate(async ({ notePath, noteContent }) => {
|
||||||
|
const writeError = await window.go.api.App.WriteVaultTextFile(
|
||||||
|
'verstak.platform-test',
|
||||||
|
notePath,
|
||||||
|
noteContent,
|
||||||
|
{ createIfMissing: true, overwrite: true },
|
||||||
|
);
|
||||||
|
if (writeError) throw new Error(writeError);
|
||||||
|
const [result, openError] = await window.go.api.App.OpenWorkbenchResource('verstak.platform-test', {
|
||||||
|
kind: 'vault-file',
|
||||||
|
path: notePath,
|
||||||
|
extension: '.md',
|
||||||
|
context: { sourceView: 'notes', isInsideNotesFolder: true, notesMode: true },
|
||||||
|
});
|
||||||
|
if (openError) throw new Error(openError);
|
||||||
|
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
|
||||||
|
}, { notePath, noteContent });
|
||||||
|
|
||||||
|
const note = page.locator('[data-editor-mode="notes-markdown"]');
|
||||||
|
await expect(note).toBeVisible({ timeout: 10000 });
|
||||||
|
await expect(note.locator('[data-preview]')).toContainText('Target secret');
|
||||||
|
await note.locator('.secret-link').click();
|
||||||
|
|
||||||
|
const secrets = page.locator('.secrets-root');
|
||||||
|
await expect(secrets).toBeVisible({ timeout: 10000 });
|
||||||
|
await expect(secrets.locator('.secrets-item.active .secrets-item-title')).toHaveText('Target secret');
|
||||||
|
await expect(secrets.locator('.secrets-card h2')).toHaveText('Target secret');
|
||||||
|
|
||||||
|
await page.locator('.workbench-host .close-btn').click();
|
||||||
|
await expect(note).toBeVisible({ timeout: 10000 });
|
||||||
|
await expect(note.locator('[data-preview]')).toContainText('Target secret');
|
||||||
|
await expect(note.locator('[data-editor-textarea]')).toHaveCount(0);
|
||||||
|
await expect(note.locator('[data-save-state]')).toHaveText('');
|
||||||
|
|
||||||
|
const storedContent = await page.evaluate(async ({ notePath }) => {
|
||||||
|
const [content, readError] = await window.go.api.App.ReadVaultTextFile('verstak.platform-test', notePath);
|
||||||
|
if (readError) throw new Error(readError);
|
||||||
|
return content;
|
||||||
|
}, { notePath });
|
||||||
|
expect(storedContent).toBe(noteContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unavailable secret does not fall back to the first secret', async ({ page }) => {
|
||||||
|
await page.evaluate(async () => {
|
||||||
|
const [result, openError] = await window.go.api.App.OpenWorkbenchResource('verstak.default-editor', {
|
||||||
|
kind: 'secret',
|
||||||
|
path: 'missing.secret',
|
||||||
|
mode: 'view',
|
||||||
|
});
|
||||||
|
if (openError) throw new Error(openError);
|
||||||
|
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
|
||||||
|
});
|
||||||
|
|
||||||
|
const secrets = page.locator('.secrets-root');
|
||||||
|
await expect(secrets).toBeVisible({ timeout: 10000 });
|
||||||
|
await expect(secrets.locator('.secrets-item.active')).toHaveCount(0);
|
||||||
|
await expect(secrets.locator('.secrets-status.error')).toContainText(/unavailable|недоступен/i);
|
||||||
|
});
|
||||||
|
|
||||||
test('editor supports markdown toolbar split save reopen and revert', async ({ page }) => {
|
test('editor supports markdown toolbar split save reopen and revert', async ({ page }) => {
|
||||||
await page.evaluate(async () => {
|
await page.evaluate(async () => {
|
||||||
const err = await window.go.api.App.WriteVaultTextFile(
|
const err = await window.go.api.App.WriteVaultTextFile(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
* Каждый метод возвращает Promise с данными, совместимыми с Wails-контрактом.
|
* Каждый метод возвращает Promise с данными, совместимыми с Wails-контрактом.
|
||||||
* Состояние мутабельно — тесты могут менять его между сценариями.
|
* Состояние мутабельно — тесты могут менять его между сценариями.
|
||||||
*/
|
*/
|
||||||
|
import defaultEditorSource from '../../../../../verstak-official-plugins/plugins/default-editor/frontend/src/index.js?raw';
|
||||||
|
import secretsSource from '../../../../../verstak-official-plugins/plugins/secrets/frontend/src/index.js?raw';
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
if (window.__wailsMockReady) return;
|
if (window.__wailsMockReady) return;
|
||||||
|
|
||||||
|
|
@ -360,6 +363,7 @@
|
||||||
'verstak.platform-test': { savedText: 'initial value' }
|
'verstak.platform-test': { savedText: 'initial value' }
|
||||||
};
|
};
|
||||||
var pluginData = {};
|
var pluginData = {};
|
||||||
|
var secretRecords = makeDefaultSecretRecords();
|
||||||
var vaultFiles = makeDefaultVaultFiles();
|
var vaultFiles = makeDefaultVaultFiles();
|
||||||
var externalOpens = [];
|
var externalOpens = [];
|
||||||
var trashEntries = [];
|
var trashEntries = [];
|
||||||
|
|
@ -404,6 +408,27 @@
|
||||||
return JSON.parse(JSON.stringify(value));
|
return JSON.parse(JSON.stringify(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeDefaultSecretRecords() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'first.secret',
|
||||||
|
title: 'First secret',
|
||||||
|
username: 'first-user',
|
||||||
|
value: 'first-value',
|
||||||
|
scope: { kind: 'global' },
|
||||||
|
updatedAt: '2026-07-14T00:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'target.secret',
|
||||||
|
title: 'Target secret',
|
||||||
|
username: 'target-user',
|
||||||
|
value: 'target-value',
|
||||||
|
scope: { kind: 'global' },
|
||||||
|
updatedAt: '2026-07-14T00:00:00Z'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function builtInWorkspaceTemplates() {
|
function builtInWorkspaceTemplates() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|
@ -573,8 +598,15 @@
|
||||||
icon: 'key-round',
|
icon: 'key-round',
|
||||||
provides: ['secret-store', 'secrets.read-ui', 'secrets.write-ui'],
|
provides: ['secret-store', 'secrets.read-ui', 'secrets.write-ui'],
|
||||||
permissions: ['secrets.read', 'secrets.write', 'ui.register'],
|
permissions: ['secrets.read', 'secrets.write', 'ui.register'],
|
||||||
frontend: { entry: 'frontend/dist/index.js' },
|
frontend: { entry: 'frontend/src/index.js' },
|
||||||
contributes: {
|
contributes: {
|
||||||
|
openProviders: [{
|
||||||
|
id: 'verstak.secrets.secret',
|
||||||
|
title: 'Secrets',
|
||||||
|
priority: 100,
|
||||||
|
component: 'SecretsView',
|
||||||
|
supports: [{ kind: 'secret', modes: ['view'] }]
|
||||||
|
}],
|
||||||
workspaceItems: [{ id: 'verstak.secrets.workspace', title: 'Secrets', icon: 'key-round', component: 'SecretsView' }]
|
workspaceItems: [{ id: 'verstak.secrets.workspace', title: 'Secrets', icon: 'key-round', component: 'SecretsView' }]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -3328,6 +3360,43 @@
|
||||||
GetPluginLocalization: function (pluginId, locale) {
|
GetPluginLocalization: function (pluginId, locale) {
|
||||||
return Promise.resolve([mockPluginCatalog(pluginId, locale), '']);
|
return Promise.resolve([mockPluginCatalog(pluginId, locale), '']);
|
||||||
},
|
},
|
||||||
|
PluginSecretsStatus: function () {
|
||||||
|
return Promise.resolve([{ initialized: true, unlocked: true }, '']);
|
||||||
|
},
|
||||||
|
PluginSecretsUnlock: function () {
|
||||||
|
return Promise.resolve('');
|
||||||
|
},
|
||||||
|
PluginSecretsList: function () {
|
||||||
|
return Promise.resolve([secretRecords.map(function (record) {
|
||||||
|
var listed = cloneJson(record);
|
||||||
|
delete listed.value;
|
||||||
|
return listed;
|
||||||
|
}), '']);
|
||||||
|
},
|
||||||
|
PluginSecretsRead: function (_pluginId, secretID) {
|
||||||
|
var record = secretRecords.find(function (item) { return item.id === secretID; });
|
||||||
|
if (!record) return Promise.resolve([{}, 'not-found: secret ' + secretID]);
|
||||||
|
return Promise.resolve([cloneJson(record), '']);
|
||||||
|
},
|
||||||
|
PluginSecretsWrite: function (_pluginId, nextRecord) {
|
||||||
|
var record = Object.assign({}, nextRecord || {});
|
||||||
|
if (!record.id) return Promise.resolve([{}, 'secret id is required']);
|
||||||
|
record.scope = record.scope || { kind: 'global' };
|
||||||
|
record.updatedAt = new Date().toISOString();
|
||||||
|
var index = secretRecords.findIndex(function (item) { return item.id === record.id; });
|
||||||
|
if (index === -1) secretRecords.push(record);
|
||||||
|
else secretRecords[index] = record;
|
||||||
|
return Promise.resolve([cloneJson(record), '']);
|
||||||
|
},
|
||||||
|
PluginSecretsDelete: function (_pluginId, secretID) {
|
||||||
|
secretRecords = secretRecords.filter(function (record) { return record.id !== secretID; });
|
||||||
|
return Promise.resolve('');
|
||||||
|
},
|
||||||
|
PluginSecretsCopyLink: function (_pluginId, secretID) {
|
||||||
|
var record = secretRecords.find(function (item) { return item.id === secretID; });
|
||||||
|
if (!record) return Promise.resolve(['', 'not-found: secret ' + secretID]);
|
||||||
|
return Promise.resolve(['[' + (record.title || record.id) + '](verstak-secret://' + encodeURIComponent(record.id) + ')', '']);
|
||||||
|
},
|
||||||
ReadPluginSettings: function (pluginId) {
|
ReadPluginSettings: function (pluginId) {
|
||||||
return Promise.resolve([Object.assign({}, pluginSettings[pluginId] || {}), '']);
|
return Promise.resolve([Object.assign({}, pluginSettings[pluginId] || {}), '']);
|
||||||
},
|
},
|
||||||
|
|
@ -3455,8 +3524,8 @@
|
||||||
if (pluginId === 'verstak.platform-test' && assetPath === 'frontend/dist/index.js') {
|
if (pluginId === 'verstak.platform-test' && assetPath === 'frontend/dist/index.js') {
|
||||||
return Promise.resolve(platformTestBundle());
|
return Promise.resolve(platformTestBundle());
|
||||||
}
|
}
|
||||||
if (pluginId === 'verstak.default-editor' && assetPath === 'frontend/dist/index.js') {
|
if (pluginId === 'verstak.default-editor') {
|
||||||
return Promise.resolve(defaultEditorBundle());
|
return Promise.resolve(defaultEditorSource);
|
||||||
}
|
}
|
||||||
if (pluginId === 'verstak.files' && assetPath === 'frontend/dist/index.js') {
|
if (pluginId === 'verstak.files' && assetPath === 'frontend/dist/index.js') {
|
||||||
return Promise.resolve(filesPluginBundle());
|
return Promise.resolve(filesPluginBundle());
|
||||||
|
|
@ -3479,8 +3548,8 @@
|
||||||
if (pluginId === 'verstak.todo' && assetPath === 'frontend/dist/index.js') {
|
if (pluginId === 'verstak.todo' && assetPath === 'frontend/dist/index.js') {
|
||||||
return Promise.resolve(todoBundle());
|
return Promise.resolve(todoBundle());
|
||||||
}
|
}
|
||||||
if (pluginId === 'verstak.secrets' && assetPath === 'frontend/dist/index.js') {
|
if (pluginId === 'verstak.secrets') {
|
||||||
return Promise.resolve(simplePluginBundle('verstak.secrets', 'SecretsView', 'secrets-root', 'Secrets'));
|
return Promise.resolve(secretsSource);
|
||||||
}
|
}
|
||||||
if (pluginId === 'verstak.search' && assetPath === 'frontend/dist/index.js') {
|
if (pluginId === 'verstak.search' && assetPath === 'frontend/dist/index.js') {
|
||||||
return Promise.resolve(searchPluginBundle());
|
return Promise.resolve(searchPluginBundle());
|
||||||
|
|
@ -4164,6 +4233,7 @@
|
||||||
workbenchPreferences = {};
|
workbenchPreferences = {};
|
||||||
openedResources = [];
|
openedResources = [];
|
||||||
pluginSettings = { 'verstak.platform-test': { savedText: 'initial value' } };
|
pluginSettings = { 'verstak.platform-test': { savedText: 'initial value' } };
|
||||||
|
secretRecords = makeDefaultSecretRecords();
|
||||||
vaultFiles = makeDefaultVaultFiles();
|
vaultFiles = makeDefaultVaultFiles();
|
||||||
externalOpens = [];
|
externalOpens = [];
|
||||||
trashEntries = [];
|
trashEntries = [];
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { resolve } from 'node:path';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
|
|
@ -13,6 +14,9 @@ export default defineConfig(({ mode }) => {
|
||||||
server: {
|
server: {
|
||||||
port: isTest ? 5174 : 5173,
|
port: isTest ? 5174 : 5173,
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
|
fs: {
|
||||||
|
allow: [resolve(__dirname, '..', '..')],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
include: [
|
include: [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue