一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
236 行
9.8 KiB
TypeScript
236 行
9.8 KiB
TypeScript
|
|
import { test, expect } from './util/test';
|
|
import MainScreen from './models/MainScreen';
|
|
import { msleep, Second } from '@joplin/utils/time';
|
|
import setSettingValue from './util/setSettingValue';
|
|
|
|
test.describe('pluginApi', () => {
|
|
test('the editor.setText command should update the current note (use RTE: false)', async ({ startAppWithPlugins }) => {
|
|
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/execCommand.js']);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
await mainScreen.createNewNote('First note');
|
|
const editor = mainScreen.noteEditor;
|
|
|
|
const markdownEditor = await editor.showMarkdownEditor();
|
|
await markdownEditor.typeText('This content should be overwritten.');
|
|
|
|
await editor.expectToHaveText('This content should be overwritten.');
|
|
await mainScreen.goToAnything.runCommand(app, 'testUpdateEditorText');
|
|
await editor.expectToHaveText('PASS');
|
|
|
|
// Should still have the same text after switching notes:
|
|
await mainScreen.createNewNote('Second note');
|
|
await editor.goBack();
|
|
|
|
await editor.expectToHaveText('PASS');
|
|
});
|
|
|
|
test('should return form data from the dialog API', async ({ startAppWithPlugins }) => {
|
|
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/dialogs.js']);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
await mainScreen.createNewNote('First note');
|
|
|
|
const editor = mainScreen.noteEditor;
|
|
await editor.expectToHaveText('\n');
|
|
|
|
await mainScreen.goToAnything.runCommand(app, 'showTestDialog');
|
|
// Wait for the iframe to load
|
|
const dialogContent = mainScreen.dialog.locator('iframe').contentFrame();
|
|
await dialogContent.locator('form').waitFor();
|
|
|
|
// Submitting the dialog should include form data in the output
|
|
await mainScreen.dialog.getByRole('button', { name: 'Okay' }).click();
|
|
await editor.expectToHaveText(JSON.stringify({
|
|
id: 'ok',
|
|
hasFormData: true,
|
|
}));
|
|
});
|
|
|
|
test('should report the correct visibility state for dialogs', async ({ startAppWithPlugins }) => {
|
|
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/dialogs.js']);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
await mainScreen.createNewNote('Dialog test note');
|
|
|
|
const editor = mainScreen.noteEditor;
|
|
const markdownEditor = await editor.showMarkdownEditor();
|
|
const expectVisible = async (visible: boolean) => {
|
|
// Check UI visibility
|
|
if (visible) {
|
|
await expect(mainScreen.dialog).toBeVisible();
|
|
} else {
|
|
await expect(mainScreen.dialog).not.toBeVisible();
|
|
}
|
|
|
|
// Check visibility reported through the plugin API
|
|
await expect.poll(async () => {
|
|
await mainScreen.goToAnything.runCommand(app, 'getTestDialogVisibility');
|
|
|
|
return markdownEditor.container.textContent();
|
|
}).toBe(JSON.stringify({
|
|
visible: visible,
|
|
active: visible,
|
|
}));
|
|
};
|
|
await expectVisible(false);
|
|
|
|
await mainScreen.goToAnything.runCommand(app, 'showTestDialog');
|
|
await expectVisible(true);
|
|
|
|
// Submitting the dialog should include form data in the output
|
|
await mainScreen.dialog.getByRole('button', { name: 'Okay' }).click();
|
|
await expectVisible(false);
|
|
});
|
|
|
|
// Regression tests for #13718
|
|
for (const method of ['Cancel button', 'Escape key'] as const) {
|
|
test(`should dismiss a plugin dialog via ${method} with isolated iframes`, async ({ startAppWithPlugins }) => {
|
|
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/dialogs.js']);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
await mainScreen.createNewNote('Test note');
|
|
|
|
// WebView isolation is currently behind a feature flag:
|
|
await setSettingValue(app, mainWindow, 'featureFlag.plugins.isolatePluginWebViews', true);
|
|
|
|
await mainScreen.goToAnything.runCommand(app, 'showTestDialogWithDismiss');
|
|
const dialogContent = mainScreen.dialog.locator('iframe').contentFrame();
|
|
await dialogContent.locator('p').waitFor();
|
|
|
|
if (method === 'Cancel button') {
|
|
await mainScreen.dialog.getByRole('button', { name: 'Cancel' }).click();
|
|
} else {
|
|
await mainWindow.keyboard.press('Escape');
|
|
}
|
|
|
|
await expect(mainScreen.dialog).toBeHidden();
|
|
});
|
|
}
|
|
|
|
test('should be possible to create multiple toasts with the same text from a plugin', async ({ startAppWithPlugins }) => {
|
|
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/showToast.js']);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
|
|
await mainScreen.goToAnything.runCommand(app, 'testShowToastNotification');
|
|
const notificationLocator = mainWindow.getByText('Toast: This is a test info message.');
|
|
await expect(notificationLocator).toBeVisible();
|
|
|
|
// Running the command again, there should be two notifications with the same text.
|
|
await mainScreen.goToAnything.runCommand(app, 'testShowToastNotification');
|
|
await expect(notificationLocator.nth(1)).toBeVisible();
|
|
await expect(notificationLocator.nth(0)).toBeVisible();
|
|
|
|
await mainScreen.goToAnything.runCommand(app, 'testShowToastNotification');
|
|
await expect(notificationLocator).toHaveCount(3);
|
|
});
|
|
|
|
test('should be possible to switch between multiple editor plugins', async ({ startAppWithPlugins }) => {
|
|
const { mainWindow } = await startAppWithPlugins(['resources/test-plugins/multipleEditorPlugins.js']);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
|
|
await mainScreen.createNewNote('Test note');
|
|
const toggleButton = mainScreen.noteEditor.toggleEditorPluginButton;
|
|
|
|
// Initially, the toggle button should be visible
|
|
const markdownEditor = await mainScreen.noteEditor.showMarkdownEditor();
|
|
|
|
await toggleButton.click();
|
|
const pluginFrame = mainScreen.noteEditor.editorPluginFrame;
|
|
await expect(pluginFrame).toBeAttached();
|
|
await expect(markdownEditor.container).not.toBeAttached();
|
|
|
|
// Should describe the frame
|
|
const frameViewIdLabel = pluginFrame.contentFrame().locator('#view-id-base');
|
|
await expect(frameViewIdLabel).toHaveText('test-editor-plugin-1');
|
|
|
|
// Clicking toggle again should cycle to the next editor plugin
|
|
await toggleButton.click();
|
|
await expect(frameViewIdLabel).toHaveText('test-editor-plugin-2');
|
|
|
|
// Clicking toggle again should dismiss the editor plugins
|
|
await toggleButton.click();
|
|
await expect(markdownEditor.container).toBeAttached();
|
|
await expect(pluginFrame).not.toBeAttached();
|
|
});
|
|
|
|
test('should be possible to save changes to a note using the editor plugin API', async ({ startAppWithPlugins }) => {
|
|
const { mainWindow, app } = await startAppWithPlugins(['resources/test-plugins/editorPluginSaving.js']);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
await mainScreen.createNewNote('Test note');
|
|
|
|
const noteEditor = mainScreen.noteEditor;
|
|
const markdownEditor = await noteEditor.showMarkdownEditor();
|
|
await markdownEditor.typeText('Initial content.');
|
|
|
|
const toggleButton = noteEditor.toggleEditorPluginButton;
|
|
await toggleButton.click();
|
|
|
|
// Should switch to the editor
|
|
const pluginFrame = noteEditor.editorPluginFrame;
|
|
await expect(pluginFrame).toBeAttached();
|
|
const pluginFrameContent = pluginFrame.contentFrame();
|
|
await expect(pluginFrameContent.getByText('Loaded!')).toBeAttached();
|
|
|
|
// The plugin only knows which note to save after its first onUpdate event.
|
|
await expect(pluginFrameContent.locator('#note-id')).not.toBeEmpty();
|
|
|
|
// Editor plugin tests should pass
|
|
await mainScreen.goToAnything.runCommand(app, 'testEditorPluginSave-test-editor-plugin');
|
|
|
|
// Should have saved
|
|
await toggleButton.click();
|
|
const expectedUpdatedText = 'Changed by test-editor-plugin';
|
|
await noteEditor.expectToHaveText(expectedUpdatedText);
|
|
|
|
// Regression test: Historically the editor's content would very briefly be correct, then
|
|
// almost immediately be replaced with the old content. Doing another check after a brief
|
|
// delay should cause the test to fail if this bug returns:
|
|
await msleep(Second);
|
|
await noteEditor.expectToHaveText(expectedUpdatedText);
|
|
});
|
|
|
|
test('should support hiding and showing panels', async ({ startAppWithPlugins }) => {
|
|
const { mainWindow, app } = await startAppWithPlugins(['resources/test-plugins/panels.js']);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
await mainScreen.createNewNote('Test note (panels)');
|
|
|
|
const panelLocator = await mainScreen.pluginPanelLocator('org.joplinapp.plugins.example.panels');
|
|
await expect(panelLocator).not.toBeVisible();
|
|
|
|
await mainScreen.goToAnything.runCommand(app, 'testShowPanel');
|
|
|
|
// Panel should be visible
|
|
await expect(panelLocator).toBeVisible();
|
|
|
|
// The panel should have the expected content
|
|
const panelContent = panelLocator.contentFrame();
|
|
await expect(
|
|
panelContent.getByRole('heading', { name: 'Panel content' }),
|
|
).toBeAttached();
|
|
|
|
await mainScreen.goToAnything.runCommand(app, 'testHidePanel');
|
|
|
|
await expect(panelLocator).not.toBeVisible();
|
|
});
|
|
|
|
// Regression test for https://github.com/laurent22/joplin/issues/12793
|
|
test('a plugin that crashes before register() should not block other plugins', async ({ startAppWithPlugins }) => {
|
|
// Load a crashing plugin alongside a working plugin. If the fix works,
|
|
// startAppWithPlugins completes because startup-plugins-loaded fires.
|
|
// Without the fix, this test would timeout because allPluginsStarted
|
|
// never becomes true.
|
|
const { app, mainWindow } = await startAppWithPlugins([
|
|
'resources/test-plugins/crashBeforeRegister.js',
|
|
'resources/test-plugins/execCommand.js',
|
|
]);
|
|
const mainScreen = await new MainScreen(mainWindow).setup();
|
|
await mainScreen.createNewNote('Test note');
|
|
|
|
// Verify the working plugin is functional
|
|
const editor = mainScreen.noteEditor;
|
|
const markdownEditor = await editor.showMarkdownEditor();
|
|
await markdownEditor.typeText('Should be overwritten.');
|
|
await mainScreen.goToAnything.runCommand(app, 'testUpdateEditorText');
|
|
await editor.expectToHaveText('PASS');
|
|
});
|
|
});
|
|
|