feat: host file and note actions

This commit is contained in:
2026-06-28 16:29:04 +08:00
parent 3b7258ed3e
commit 48fe942028
4 changed files with 205 additions and 22 deletions
+41
View File
@@ -197,8 +197,10 @@ function loadFilesComponent(document) {
function makeApi() {
const externalCalls = [];
const contributionCalls = [];
return {
externalCalls,
contributionCalls,
files: {
list: async () => [
{
@@ -222,6 +224,34 @@ function makeApi() {
workbench: {
openResource: async () => ({ status: 'opened' }),
},
contributions: {
list: async (point) => {
if (point === 'fileActions') {
return [{
pluginId: 'provider.plugin',
id: 'provider.file.action',
label: 'Provider File Action',
handler: 'provider.command',
}];
}
if (point === 'contextMenuEntries') {
return [{
pluginId: 'provider.plugin',
id: 'provider.file.context',
label: 'Provider Context Action',
context: 'file',
handler: 'provider.context',
}];
}
return [];
},
},
commands: {
executeFor: async (pluginId, commandId, args) => {
contributionCalls.push({ pluginId, commandId, args });
return { status: 'handled' };
},
},
};
}
@@ -267,6 +297,17 @@ async function flush() {
throw new Error(`expected no copied path after successful external API calls, got ${clipboard.written.join(', ')}`);
}
list.dispatchEvent('contextmenu', { target: row, clientX: 20, clientY: 20 });
const providerAction = walk(document.body, (node) => node.getAttribute && node.getAttribute('data-files-menu-action') === 'contribution-provider.file.action');
if (!providerAction) throw new Error('provider file action menu item not found');
const providerContext = walk(document.body, (node) => node.getAttribute && node.getAttribute('data-files-menu-action') === 'contribution-provider.file.context');
if (!providerContext) throw new Error('provider context menu item not found');
providerAction.click();
await flush();
if (!api.contributionCalls.some((call) => call.pluginId === 'provider.plugin' && call.commandId === 'provider.command' && call.args.path === 'Docs/readme.md')) {
throw new Error(`expected provider file action call, got ${JSON.stringify(api.contributionCalls)}`);
}
console.log('files frontend smoke passed');
})().catch((err) => {
console.error(err);
+29
View File
@@ -132,9 +132,11 @@ function loadNotesComponent(document) {
function makeApi(options = {}) {
const entries = new Map();
const opened = [];
const contributionCalls = [];
return {
entries,
opened,
contributionCalls,
files: {
list: async (relativeDir) => {
const prefix = relativeDir ? `${relativeDir}/` : '';
@@ -187,6 +189,25 @@ function makeApi(options = {}) {
return { status: 'opened' };
},
},
contributions: {
list: async (point) => {
if (point === 'noteActions') {
return [{
pluginId: 'provider.plugin',
id: 'provider.note.action',
label: 'Provider Note Action',
handler: 'provider.noteCommand',
}];
}
return [];
},
},
commands: {
executeFor: async (pluginId, commandId, args) => {
contributionCalls.push({ pluginId, commandId, args });
return { status: 'handled' };
},
},
};
}
@@ -239,6 +260,14 @@ async function mountNotes(api) {
throw new Error('create note did not open the newly created file');
}
const providerAction = walk(container, (node) => node.getAttribute && node.getAttribute('data-note-contribution-action') === 'provider.note.action');
if (!providerAction) throw new Error('provider note action button not found');
providerAction.click();
await flush();
if (!createApi.contributionCalls.some((call) => call.pluginId === 'provider.plugin' && call.commandId === 'provider.noteCommand' && call.args.path === 'Project/Notes/First_Note.md')) {
throw new Error(`expected provider note action call, got ${JSON.stringify(createApi.contributionCalls)}`);
}
const trashButton = walk(container, (node) => node.getAttribute && node.getAttribute('data-note-action') === 'trash');
if (!trashButton) throw new Error('trash note button not found');
trashButton.click();