diff --git a/e2e-tests/playwright/lib/src/server/index.ts b/e2e-tests/playwright/lib/src/server/index.ts index f3561c3805e..5be9379db5a 100644 --- a/e2e-tests/playwright/lib/src/server/index.ts +++ b/e2e-tests/playwright/lib/src/server/index.ts @@ -8,3 +8,5 @@ export {initSetup, getAdminClient} from './init'; export {createRandomPost} from './post'; export {createNewTeam, createRandomTeam} from './team'; export {createNewUserProfile, createRandomUser, getDefaultAdminUser, isOutsideRemoteUserHour} from './user'; +export {installAndEnablePlugin, isPluginActive, getPluginStatus} from './plugin'; +//getPluginStatus diff --git a/e2e-tests/playwright/lib/src/server/plugin.ts b/e2e-tests/playwright/lib/src/server/plugin.ts new file mode 100644 index 00000000000..a15a827415a --- /dev/null +++ b/e2e-tests/playwright/lib/src/server/plugin.ts @@ -0,0 +1,56 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Client4} from '@mattermost/client'; +import {PluginManifest} from '@mattermost/types/plugins'; + +export async function isPluginActive(client: Client4, pluginId: string): Promise { + const plugins = await client.getPlugins(); + return plugins.active.some((plugin: PluginManifest) => plugin.id === pluginId); +} + +export async function getPluginStatus( + client: Client4, + pluginId: string, +): Promise<{isInstalled: boolean; isActive: boolean}> { + const plugins = await client.getPlugins(); + + const isActive = plugins.active.some((plugin: PluginManifest) => plugin.id === pluginId); + const isInactive = plugins.inactive.some((plugin: PluginManifest) => plugin.id === pluginId); + + return { + isInstalled: isActive || isInactive, + isActive, + }; +} + +/** + * Installs and enables a plugin with smart status checking + * - If already active: does nothing + * - If already installed: just enables it + * - Otherwise: installs from URL, then enables + */ +export async function installAndEnablePlugin( + client: Client4, + pluginUrl: string, + pluginId: string, + force = true, +): Promise { + // Check current status + const status = await getPluginStatus(client, pluginId); + + // If already active, nothing to do + if (status.isActive) { + return; + } + + // If already installed but not active, just enable it + if (status.isInstalled) { + await client.enablePlugin(pluginId); + return; + } + + // Not installed - install from URL then enable + await client.installPluginFromUrl(pluginUrl, force); + await client.enablePlugin(pluginId); +} diff --git a/e2e-tests/playwright/lib/src/test_fixture.ts b/e2e-tests/playwright/lib/src/test_fixture.ts index 89b8e869767..c3fb577f2d3 100644 --- a/e2e-tests/playwright/lib/src/test_fixture.ts +++ b/e2e-tests/playwright/lib/src/test_fixture.ts @@ -29,6 +29,8 @@ import { isOutsideRemoteUserHour, makeClient, mergeWithOnPremServerConfig, + installAndEnablePlugin, + isPluginActive, } from './server'; import { toBeFocusedWithFocusVisible, @@ -89,6 +91,8 @@ export class PlaywrightExtended { readonly getAdminClient; readonly mergeWithOnPremServerConfig; readonly initSetup; + readonly installAndEnablePlugin; + readonly isPluginActive; // ./test_action readonly toBeFocusedWithFocusVisible; @@ -149,6 +153,8 @@ export class PlaywrightExtended { this.getAdminClient = getAdminClient; this.mergeWithOnPremServerConfig = mergeWithOnPremServerConfig; this.isOutsideRemoteUserHour = isOutsideRemoteUserHour; + this.installAndEnablePlugin = installAndEnablePlugin; + this.isPluginActive = isPluginActive; // ./test_action this.toBeFocusedWithFocusVisible = toBeFocusedWithFocusVisible; diff --git a/e2e-tests/playwright/specs/functional/plugins/demo_plugin_installation.spec.ts b/e2e-tests/playwright/specs/functional/plugins/demo_plugin_installation.spec.ts new file mode 100644 index 00000000000..8085545b436 --- /dev/null +++ b/e2e-tests/playwright/specs/functional/plugins/demo_plugin_installation.spec.ts @@ -0,0 +1,52 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {test, expect} from '@mattermost/playwright-lib'; + +test('should install and enable demo plugin from URL', async ({pw}) => { + // Create and navigate to channels page + const {adminClient, user} = await pw.initSetup(); + const {channelsPage} = await pw.testBrowser.login(user); + + await channelsPage.goto(); + await channelsPage.toBeVisible(); + + // Enable public links before installing plugin + await adminClient.patchConfig({ + FileSettings: {EnablePublicLink: true}, + ServiceSettings: { + EnableOnboardingFlow: false, + EnableTutorial: false, + }, + }); + + // Install and enable + await pw.installAndEnablePlugin( + adminClient, + 'https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.10.3/mattermost-plugin-demo-v0.10.3.tar.gz', + 'com.mattermost.demo-plugin', + ); + + // Verify it's active (API validation, no UI) + await expect + .poll(async () => { + return await pw.isPluginActive(adminClient, 'com.mattermost.demo-plugin'); + }) + .toBe(true); + + // Optional: Get plugin details + const plugins = await adminClient.getPlugins(); + const demoPlugin = plugins.active.find((p) => p.id === 'com.mattermost.demo-plugin'); + expect(demoPlugin).toBeDefined(); + + // Dismiss overlay again if it reappeared after plugin activation + await channelsPage.page.keyboard.press('Escape'); + await channelsPage.page.waitForTimeout(500); + + // UI Validation: Execute slash command and verify response + await channelsPage.postMessage('/demo_plugin true'); + + const post = await channelsPage.getLastPost(); + await post.toBeVisible(); + await post.toContainText('enabled'); +});