mirror of https://github.com/grafana/grafana
Plugin Extensions: E2E test addLink and legacy APIs (#92394)
* cleanup tests * more cleanup * added links * test legacy hooks * test legacy hooks * update codeowners * revert package changes * add project specfic example script * remove console log * Update .github/CODEOWNERS Co-authored-by: Timur Olzhabayev <timur.olzhabayev@grafana.com> * Update CODEOWNERS * use correct file names * cleanup tests --------- Co-authored-by: Timur Olzhabayev <timur.olzhabayev@grafana.com>pull/91738/head^2
parent
a2de893ab3
commit
1373b37166
@ -1,51 +0,0 @@ |
||||
import { test, expect } from '@grafana/plugin-e2e'; |
||||
|
||||
import { ensureExtensionRegistryIsPopulated } from './utils'; |
||||
|
||||
const testIds = { |
||||
container: 'main-app-body', |
||||
actions: { |
||||
button: 'action-button', |
||||
}, |
||||
modal: { |
||||
container: 'container', |
||||
open: 'open-link', |
||||
}, |
||||
appA: { |
||||
container: 'a-app-body', |
||||
}, |
||||
appB: { |
||||
modal: 'b-app-modal', |
||||
reusableComponent: 'b-app-configure-extension-component', |
||||
}, |
||||
legacyAPIPage: { |
||||
container: 'data-testid pg-two-container', |
||||
}, |
||||
}; |
||||
|
||||
const pluginId = 'grafana-extensionstest-app'; |
||||
|
||||
test('should extend the actions menu with a link to a-app plugin', async ({ page }) => { |
||||
await page.goto(`/a/${pluginId}/legacy-apis`); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
await page.getByTestId(testIds.actions.button).click(); |
||||
await page.getByTestId(testIds.container).getByText('Go to A').click(); |
||||
await page.getByTestId(testIds.modal.open).click(); |
||||
await expect(page.getByTestId(testIds.appA.container)).toBeVisible(); |
||||
}); |
||||
|
||||
test('should extend the actions menu with a command triggered from b-app plugin', async ({ page }) => { |
||||
await page.goto(`/a/${pluginId}/legacy-apis`); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
await expect( |
||||
page.getByTestId(testIds.legacyAPIPage.container).getByTestId(testIds.appB.reusableComponent) |
||||
).toHaveText('Hello World!'); |
||||
}); |
||||
|
||||
test('should extend main app with component extension from app B', async ({ page }) => { |
||||
await page.goto(`/a/${pluginId}/legacy-apis`); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
await page.getByTestId(testIds.actions.button).click(); |
||||
await page.getByTestId(testIds.container).getByText('Open from B').click(); |
||||
await expect(page.getByTestId(testIds.appB.modal)).toBeVisible(); |
||||
}); |
@ -1,44 +0,0 @@ |
||||
import { selectors } from '@grafana/e2e-selectors'; |
||||
import { expect, test } from '@grafana/plugin-e2e'; |
||||
|
||||
import { ensureExtensionRegistryIsPopulated } from './utils'; |
||||
|
||||
const panelTitle = 'Link with defaults'; |
||||
const extensionTitle = 'Open from time series...'; |
||||
const testIds = { |
||||
modal: { |
||||
container: 'ape-modal-body', |
||||
}, |
||||
mainPage: { |
||||
container: 'main-app-body', |
||||
}, |
||||
}; |
||||
|
||||
const linkOnClickDashboardUid = 'dbfb47c5-e5e5-4d28-8ac7-35f349b95946'; |
||||
const linkPathDashboardUid = 'd1fbb077-cd44-4738-8c8a-d4e66748b719'; |
||||
|
||||
test('should add link extension (path) with defaults to time series panel', async ({ gotoDashboardPage, page }) => { |
||||
const dashboardPage = await gotoDashboardPage({ uid: linkPathDashboardUid }); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle); |
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' }); |
||||
await expect(page.getByTestId(testIds.mainPage.container)).toBeVisible(); |
||||
}); |
||||
|
||||
test('should add link extension (onclick) with defaults to time series panel', async ({ gotoDashboardPage, page }) => { |
||||
const dashboardPage = await gotoDashboardPage({ uid: linkOnClickDashboardUid }); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle); |
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' }); |
||||
await expect(page.getByRole('dialog')).toContainText('Select query from "Link with defaults"'); |
||||
}); |
||||
|
||||
test('should add link extension (onclick) with new title to pie chart panel', async ({ gotoDashboardPage, page }) => { |
||||
const panelTitle = 'Link with new name'; |
||||
const extensionTitle = 'Open from piechart'; |
||||
const dashboardPage = await gotoDashboardPage({ uid: linkOnClickDashboardUid }); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle); |
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' }); |
||||
await expect(page.getByRole('dialog')).toContainText('Select query from "Link with new name"'); |
||||
}); |
@ -1,9 +0,0 @@ |
||||
import { test, expect } from '@grafana/plugin-e2e'; |
||||
|
||||
const pluginId = 'grafana-extensionstest-app'; |
||||
const exposedComponentTestId = 'exposed-component'; |
||||
|
||||
test('should display component exposed by another app', async ({ page }) => { |
||||
await page.goto(`/a/${pluginId}/exposed-components`); |
||||
await expect(await page.getByTestId(exposedComponentTestId)).toHaveText('Hello World!'); |
||||
}); |
@ -1,11 +0,0 @@ |
||||
import { test, expect } from '@grafana/plugin-e2e'; |
||||
|
||||
const pluginId = 'grafana-extensionstest-app'; |
||||
const exposedComponentTestId = 'exposed-component'; |
||||
|
||||
test('should render component with usePluginComponents hook', async ({ page }) => { |
||||
await page.goto(`/a/${pluginId}/added-components`); |
||||
await expect( |
||||
page.getByTestId('data-testid pg-added-components-container').getByTestId('b-app-add-component') |
||||
).toHaveText('Hello World!'); |
||||
}); |
@ -1,135 +0,0 @@ |
||||
import { ChangeEvent, useState } from 'react'; |
||||
import { lastValueFrom } from 'rxjs'; |
||||
import { css } from '@emotion/css'; |
||||
import { AppPluginMeta, GrafanaTheme2, PluginConfigPageProps, PluginMeta } from '@grafana/data'; |
||||
import { getBackendSrv } from '@grafana/runtime'; |
||||
import { Button, Field, FieldSet, Input, SecretInput, useStyles2 } from '@grafana/ui'; |
||||
import { testIds } from '../testIds'; |
||||
|
||||
export type AppPluginSettings = { |
||||
apiUrl?: string; |
||||
}; |
||||
|
||||
type State = { |
||||
// The URL to reach our custom API.
|
||||
apiUrl: string; |
||||
// Tells us if the API key secret is set.
|
||||
isApiKeySet: boolean; |
||||
// A secret key for our custom API.
|
||||
apiKey: string; |
||||
}; |
||||
|
||||
export interface AppConfigProps extends PluginConfigPageProps<AppPluginMeta<AppPluginSettings>> {} |
||||
|
||||
export const AppConfig = ({ plugin }: AppConfigProps) => { |
||||
const s = useStyles2(getStyles); |
||||
const { enabled, pinned, jsonData, secureJsonFields } = plugin.meta; |
||||
const [state, setState] = useState<State>({ |
||||
apiUrl: jsonData?.apiUrl || '', |
||||
apiKey: '', |
||||
isApiKeySet: Boolean(secureJsonFields?.apiKey), |
||||
}); |
||||
|
||||
const onResetApiKey = () => |
||||
setState({ |
||||
...state, |
||||
apiKey: '', |
||||
isApiKeySet: false, |
||||
}); |
||||
|
||||
const onChange = (event: ChangeEvent<HTMLInputElement>) => { |
||||
setState({ |
||||
...state, |
||||
[event.target.name]: event.target.value.trim(), |
||||
}); |
||||
}; |
||||
|
||||
return ( |
||||
<div data-testid={testIds.appConfig.container}> |
||||
<FieldSet label="API Settings"> |
||||
<Field label="API Key" description="A secret key for authenticating to our custom API"> |
||||
<SecretInput |
||||
width={60} |
||||
id="config-api-key" |
||||
data-testid={testIds.appConfig.apiKey} |
||||
name="apiKey" |
||||
value={state.apiKey} |
||||
isConfigured={state.isApiKeySet} |
||||
placeholder={'Your secret API key'} |
||||
onChange={onChange} |
||||
onReset={onResetApiKey} |
||||
/> |
||||
</Field> |
||||
|
||||
<Field label="API Url" description="" className={s.marginTop}> |
||||
<Input |
||||
width={60} |
||||
name="apiUrl" |
||||
id="config-api-url" |
||||
data-testid={testIds.appConfig.apiUrl} |
||||
value={state.apiUrl} |
||||
placeholder={`E.g.: http://mywebsite.com/api/v1`} |
||||
onChange={onChange} |
||||
/> |
||||
</Field> |
||||
|
||||
<div className={s.marginTop}> |
||||
<Button |
||||
type="submit" |
||||
data-testid={testIds.appConfig.submit} |
||||
onClick={() => |
||||
updatePluginAndReload(plugin.meta.id, { |
||||
enabled, |
||||
pinned, |
||||
jsonData: { |
||||
apiUrl: state.apiUrl, |
||||
}, |
||||
// This cannot be queried later by the frontend.
|
||||
// We don't want to override it in case it was set previously and left untouched now.
|
||||
secureJsonData: state.isApiKeySet |
||||
? undefined |
||||
: { |
||||
apiKey: state.apiKey, |
||||
}, |
||||
}) |
||||
} |
||||
disabled={Boolean(!state.apiUrl || (!state.isApiKeySet && !state.apiKey))} |
||||
> |
||||
Save API settings |
||||
</Button> |
||||
</div> |
||||
</FieldSet> |
||||
</div> |
||||
); |
||||
}; |
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({ |
||||
colorWeak: css` |
||||
color: ${theme.colors.text.secondary}; |
||||
`,
|
||||
marginTop: css` |
||||
margin-top: ${theme.spacing(3)}; |
||||
`,
|
||||
}); |
||||
|
||||
const updatePluginAndReload = async (pluginId: string, data: Partial<PluginMeta<AppPluginSettings>>) => { |
||||
try { |
||||
await updatePlugin(pluginId, data); |
||||
|
||||
// Reloading the page as the changes made here wouldn't be propagated to the actual plugin otherwise.
|
||||
// This is not ideal, however unfortunately currently there is no supported way for updating the plugin state.
|
||||
window.location.reload(); |
||||
} catch (e) { |
||||
console.error('Error while updating the plugin', e); |
||||
} |
||||
}; |
||||
|
||||
export const updatePlugin = async (pluginId: string, data: Partial<PluginMeta>) => { |
||||
const response = await getBackendSrv().fetch({ |
||||
url: `/api/plugins/${pluginId}/settings`, |
||||
method: 'POST', |
||||
data, |
||||
}); |
||||
|
||||
return lastValueFrom(response); |
||||
}; |
@ -1 +0,0 @@ |
||||
export * from './AppConfig'; |
@ -1,36 +0,0 @@ |
||||
export const testIds = { |
||||
container: 'main-app-body', |
||||
actions: { |
||||
button: 'action-button', |
||||
}, |
||||
modal: { |
||||
container: 'container', |
||||
open: 'open-link', |
||||
}, |
||||
appA: { |
||||
container: 'a-app-body', |
||||
}, |
||||
appB: { |
||||
modal: 'b-app-modal', |
||||
}, |
||||
appConfig: { |
||||
container: 'data-testid ac-container', |
||||
apiKey: 'data-testid ac-api-key', |
||||
apiUrl: 'data-testid ac-api-url', |
||||
submit: 'data-testid ac-submit-form', |
||||
}, |
||||
pageOne: { |
||||
container: 'data-testid pg-one-container', |
||||
navigateToFour: 'data-testid navigate-to-four', |
||||
}, |
||||
pageTwo: { |
||||
container: 'data-testid pg-two-container', |
||||
}, |
||||
addedComponentsPage: { |
||||
container: 'data-testid pg-added-components-container', |
||||
}, |
||||
pageFour: { |
||||
container: 'data-testid pg-four-container', |
||||
navigateBack: 'data-testid navigate-back', |
||||
}, |
||||
}; |
@ -0,0 +1,25 @@ |
||||
import { PluginPage, usePluginLinks } from '@grafana/runtime'; |
||||
|
||||
import { testIds } from '../testIds'; |
||||
|
||||
export const LINKS_EXTENSION_POINT_ID = 'plugins/grafana-extensionstest-app/use-plugin-links/v1'; |
||||
|
||||
export function AddedLinks() { |
||||
const { links, isLoading } = usePluginLinks({ extensionPointId: LINKS_EXTENSION_POINT_ID }); |
||||
|
||||
return ( |
||||
<PluginPage> |
||||
<div data-testid={testIds.addedLinksPage.container}> |
||||
{isLoading ? ( |
||||
<div>Loading...</div> |
||||
) : ( |
||||
links.map(({ id, title, path, onClick }) => ( |
||||
<a href={path} title={title} key={id} onClick={onClick}> |
||||
{title} |
||||
</a> |
||||
)) |
||||
)} |
||||
</div> |
||||
</PluginPage> |
||||
); |
||||
} |
@ -1,44 +0,0 @@ |
||||
import { testIds } from '../components/testIds'; |
||||
import { PluginPage, getPluginComponentExtensions, getPluginExtensions } from '@grafana/runtime'; |
||||
import { ActionButton } from '../components/ActionButton'; |
||||
import { Stack } from '@grafana/ui'; |
||||
|
||||
type AppExtensionContext = {}; |
||||
type ReusableComponentProps = { |
||||
name: string; |
||||
}; |
||||
|
||||
export function LegacyAPIs() { |
||||
const extensionPointId = 'plugins/grafana-extensionstest-app/actions'; |
||||
const context: AppExtensionContext = {}; |
||||
|
||||
const { extensions } = getPluginExtensions({ |
||||
extensionPointId, |
||||
context, |
||||
}); |
||||
|
||||
const { extensions: componentExtensions } = getPluginComponentExtensions<ReusableComponentProps>({ |
||||
extensionPointId: 'plugins/grafana-extensionexample2-app/configure-extension-component/v1', |
||||
}); |
||||
|
||||
return ( |
||||
<PluginPage> |
||||
<Stack direction={'column'} gap={4} data-testid={testIds.pageTwo.container}> |
||||
<article> |
||||
<h3>Link extensions defined with configureExtensionLink and retrived using getPluginExtensions</h3> |
||||
<ActionButton extensions={extensions} /> |
||||
</article> |
||||
<article> |
||||
<h3> |
||||
Component extensions defined with configureExtensionComponent and retrived using |
||||
getPluginComponentExtensions |
||||
</h3> |
||||
{componentExtensions.map((extension) => { |
||||
const Component = extension.component; |
||||
return <Component key={extension.id} name="World" />; |
||||
})} |
||||
</article> |
||||
</Stack> |
||||
</PluginPage> |
||||
); |
||||
} |
@ -0,0 +1,62 @@ |
||||
import { |
||||
PluginPage, |
||||
getPluginComponentExtensions, |
||||
getPluginExtensions, |
||||
getPluginLinkExtensions, |
||||
} from '@grafana/runtime'; |
||||
import { Stack } from '@grafana/ui'; |
||||
|
||||
import { ActionButton } from '../components/ActionButton'; |
||||
import { testIds } from '../testIds'; |
||||
|
||||
type AppExtensionContext = {}; |
||||
type ReusableComponentProps = { |
||||
name: string; |
||||
}; |
||||
|
||||
export function LegacyGetters() { |
||||
const extensionPointId1 = 'plugins/grafana-extensionstest-app/actions'; |
||||
const extensionPointId2 = 'plugins/grafana-extensionexample2-app/configure-extension-component/v1'; |
||||
const context: AppExtensionContext = {}; |
||||
|
||||
const { extensions } = getPluginExtensions({ |
||||
extensionPointId: extensionPointId1, |
||||
context, |
||||
}); |
||||
|
||||
const { extensions: linkExtensions } = getPluginLinkExtensions({ |
||||
extensionPointId: extensionPointId1, |
||||
}); |
||||
|
||||
const { extensions: componentExtensions } = getPluginComponentExtensions<ReusableComponentProps>({ |
||||
extensionPointId: extensionPointId2, |
||||
}); |
||||
|
||||
return ( |
||||
<PluginPage> |
||||
<Stack direction={'column'} gap={4} data-testid={testIds.legacyGettersPage.container}> |
||||
<section data-testid={testIds.legacyGettersPage.section1}> |
||||
<h3> |
||||
Link extensions defined with configureExtensionLink or configureExtensionComponent and retrived using |
||||
getPluginExtensions |
||||
</h3> |
||||
<ActionButton extensions={extensions} /> |
||||
</section> |
||||
<section data-testid={testIds.legacyGettersPage.section2}> |
||||
<h3>Link extensions defined with configureExtensionLink and retrived using getPluginLinkExtensions</h3> |
||||
<ActionButton extensions={linkExtensions} /> |
||||
</section> |
||||
<section data-testid={testIds.legacyGettersPage.section3}> |
||||
<h3> |
||||
Component extensions defined with configureExtensionComponent and retrived using |
||||
getPluginComponentExtensions |
||||
</h3> |
||||
{componentExtensions.map((extension) => { |
||||
const Component = extension.component; |
||||
return <Component key={extension.id} name="World" />; |
||||
})} |
||||
</section> |
||||
</Stack> |
||||
</PluginPage> |
||||
); |
||||
} |
@ -0,0 +1,62 @@ |
||||
import { |
||||
PluginPage, |
||||
usePluginComponentExtensions, |
||||
usePluginExtensions, |
||||
usePluginLinkExtensions, |
||||
} from '@grafana/runtime'; |
||||
import { Stack } from '@grafana/ui'; |
||||
|
||||
import { ActionButton } from '../components/ActionButton'; |
||||
import { testIds } from '../testIds'; |
||||
|
||||
type AppExtensionContext = {}; |
||||
type ReusableComponentProps = { |
||||
name: string; |
||||
}; |
||||
|
||||
export function LegacyHooks() { |
||||
const extensionPointId1 = 'plugins/grafana-extensionstest-app/actions'; |
||||
const extensionPointId2 = 'plugins/grafana-extensionexample2-app/configure-extension-component/v1'; |
||||
const context: AppExtensionContext = {}; |
||||
|
||||
const { extensions } = usePluginExtensions({ |
||||
extensionPointId: extensionPointId1, |
||||
context, |
||||
}); |
||||
|
||||
const { extensions: linkExtensions } = usePluginLinkExtensions({ |
||||
extensionPointId: extensionPointId1, |
||||
}); |
||||
|
||||
const { extensions: componentExtensions } = usePluginComponentExtensions<ReusableComponentProps>({ |
||||
extensionPointId: extensionPointId2, |
||||
}); |
||||
|
||||
return ( |
||||
<PluginPage> |
||||
<Stack direction={'column'} gap={4} data-testid={testIds.legacyHooksPage.container}> |
||||
<section data-testid={testIds.legacyHooksPage.section1}> |
||||
<h3> |
||||
Link extensions defined with configureExtensionLink or configureExtensionComponent and retrived using |
||||
usePluginExtensions |
||||
</h3> |
||||
<ActionButton extensions={extensions} /> |
||||
</section> |
||||
<section data-testid={testIds.legacyHooksPage.section2}> |
||||
<h3>Link extensions defined with configureExtensionLink and retrived using usePluginLinkExtensions</h3> |
||||
<ActionButton extensions={linkExtensions} /> |
||||
</section> |
||||
<section data-testid={testIds.legacyHooksPage.section3}> |
||||
<h3> |
||||
Component extensions defined with configureExtensionComponent and retrived using |
||||
usePluginComponentExtensions |
||||
</h3> |
||||
{componentExtensions.map((extension) => { |
||||
const Component = extension.component; |
||||
return <Component key={extension.id} name="World" />; |
||||
})} |
||||
</section> |
||||
</Stack> |
||||
</PluginPage> |
||||
); |
||||
} |
@ -1,3 +1,5 @@ |
||||
export { ExposedComponents } from './ExposedComponents'; |
||||
export { LegacyAPIs } from './LegacyAPIs'; |
||||
export { LegacyGetters } from './LegacyGetters'; |
||||
export { LegacyHooks } from './LegacyHooks'; |
||||
export { AddedComponents } from './AddedComponents'; |
||||
export { AddedLinks } from './AddedLinks'; |
||||
|
@ -1,16 +0,0 @@ |
||||
export const testIds = { |
||||
container: 'main-app-body', |
||||
actions: { |
||||
button: 'action-button', |
||||
}, |
||||
modal: { |
||||
container: 'container', |
||||
open: 'open-link', |
||||
}, |
||||
appA: { |
||||
container: 'a-app-body', |
||||
}, |
||||
appB: { |
||||
modal: 'b-app-modal', |
||||
}, |
||||
}; |
@ -1,18 +0,0 @@ |
||||
export const testIds = { |
||||
container: 'main-app-body', |
||||
actions: { |
||||
button: 'action-button', |
||||
}, |
||||
modal: { |
||||
container: 'container', |
||||
open: 'open-link', |
||||
}, |
||||
appA: { |
||||
container: 'a-app-body', |
||||
}, |
||||
appB: { |
||||
modal: 'b-app-modal', |
||||
reusableComponent: 'b-app-configure-extension-component', |
||||
reusableAddedComponent: 'b-app-add-component', |
||||
}, |
||||
}; |
@ -0,0 +1,40 @@ |
||||
export const testIds = { |
||||
container: 'main-app-body', |
||||
actions: { |
||||
button: 'action-button', |
||||
}, |
||||
modal: { |
||||
container: 'container', |
||||
open: 'open-link', |
||||
}, |
||||
appA: { |
||||
container: 'a-app-body', |
||||
}, |
||||
appB: { |
||||
modal: 'b-app-modal', |
||||
reusableComponent: 'b-app-configure-extension-component', |
||||
reusableAddedComponent: 'b-app-add-component', |
||||
exposedComponent: 'b-app-exposed-component', |
||||
}, |
||||
legacyGettersPage: { |
||||
container: 'data-testid pg-legacy-getters-container', |
||||
section1: 'get-plugin-extensions', |
||||
section2: 'configure-extension-link-get-plugin-link-extensions', |
||||
section3: 'configure-extension-component-get-plugin-component-extensions', |
||||
}, |
||||
legacyHooksPage: { |
||||
container: 'data-testid pg-legacy-hooks-container', |
||||
section1: 'use-plugin-extensions', |
||||
section2: 'configure-extension-link-use-plugin-link-extensions', |
||||
section3: 'configure-extension-component-use-plugin-component-extensions', |
||||
}, |
||||
exposedComponentsPage: { |
||||
container: 'data-testid pg-exposed-components-container', |
||||
}, |
||||
addedComponentsPage: { |
||||
container: 'data-testid pg-added-components-container', |
||||
}, |
||||
addedLinksPage: { |
||||
container: 'data-testid pg-added-links-container', |
||||
}, |
||||
}; |
@ -0,0 +1,52 @@ |
||||
import { test, expect } from '@grafana/plugin-e2e'; |
||||
|
||||
import { ensureExtensionRegistryIsPopulated } from '../utils'; |
||||
import { testIds } from '../../testIds'; |
||||
import pluginJson from '../../plugin.json'; |
||||
|
||||
test.describe('getPluginExtensions + configureExtensionLink', () => { |
||||
test('should extend the actions menu with a link to a-app plugin', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/legacy-getters`); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const section = await page.getByTestId(testIds.legacyGettersPage.section1); |
||||
await section.getByTestId(testIds.actions.button).click(); |
||||
await page.getByTestId(testIds.container).getByText('Go to A').click(); |
||||
await page.getByTestId(testIds.modal.open).click(); |
||||
await expect(page.getByTestId(testIds.appA.container)).toBeVisible(); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('getPluginExtensions + configureExtensionComponent', () => { |
||||
test('should extend main app with component extension from app B', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/legacy-getters`); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const section = await page.getByTestId(testIds.legacyGettersPage.section1); |
||||
await section.getByTestId(testIds.actions.button).click(); |
||||
await page.getByTestId(testIds.container).getByText('Open from B').click(); |
||||
await expect(page.getByTestId(testIds.appB.modal)).toBeVisible(); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('getPluginLinkExtensions + configureExtensionLink', () => { |
||||
test('should extend the actions menu with a link to a-app plugin', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/legacy-getters`); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const section = await page.getByTestId(testIds.legacyGettersPage.section2); |
||||
await section.getByTestId(testIds.actions.button).click(); |
||||
await page.getByTestId(testIds.container).getByText('Go to A').click(); |
||||
await page.getByTestId(testIds.modal.open).click(); |
||||
await expect(page.getByTestId(testIds.appA.container)).toBeVisible(); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('getPluginComponentExtensions + configureExtensionComponent', () => { |
||||
test('should extend the actions menu with a command triggered from b-app plugin', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/legacy-getters`); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
await expect( |
||||
page |
||||
.getByTestId('configure-extension-component-get-plugin-component-extensions') |
||||
.getByTestId(testIds.appB.reusableComponent) |
||||
).toHaveText('Hello World!'); |
||||
}); |
||||
}); |
@ -0,0 +1,45 @@ |
||||
import { test, expect } from '@grafana/plugin-e2e'; |
||||
|
||||
import { testIds } from '../../testIds'; |
||||
import pluginJson from '../../plugin.json'; |
||||
|
||||
test.describe('usePluginExtensions + configureExtensionLink', () => { |
||||
test('should extend the actions menu with a link to a-app plugin', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/legacy-hooks`); |
||||
const section = await page.getByTestId(testIds.legacyHooksPage.section1); |
||||
await section.getByTestId(testIds.actions.button).click(); |
||||
await page.getByTestId(testIds.container).getByText('Go to A').click(); |
||||
await page.getByTestId(testIds.modal.open).click(); |
||||
await expect(page.getByTestId(testIds.appA.container)).toBeVisible(); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('usePluginExtensions + configureExtensionComponent', () => { |
||||
test('should extend main app with component extension from app B', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/legacy-hooks`); |
||||
const section = await page.getByTestId(testIds.legacyHooksPage.section1); |
||||
await section.getByTestId(testIds.actions.button).click(); |
||||
await page.getByTestId(testIds.container).getByText('Open from B').click(); |
||||
await expect(page.getByTestId(testIds.appB.modal)).toBeVisible(); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('usePluginLinkExtensions + configureExtensionLink', () => { |
||||
test('should extend the actions menu with a link to a-app plugin', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/legacy-hooks`); |
||||
const section = await page.getByTestId(testIds.legacyHooksPage.section2); |
||||
await section.getByTestId(testIds.actions.button).click(); |
||||
await page.getByTestId(testIds.container).getByText('Go to A').click(); |
||||
await page.getByTestId(testIds.modal.open).click(); |
||||
await expect(page.getByTestId(testIds.appA.container)).toBeVisible(); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('usePluginComponentExtensions + configureExtensionComponent', () => { |
||||
test('should extend the actions menu with a command triggered from b-app plugin', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/legacy-hooks`); |
||||
await expect( |
||||
page.getByTestId(testIds.legacyHooksPage.section3).getByTestId(testIds.appB.reusableComponent) |
||||
).toHaveText('Hello World!'); |
||||
}); |
||||
}); |
@ -0,0 +1,42 @@ |
||||
import { expect, test } from '@grafana/plugin-e2e'; |
||||
import { ensureExtensionRegistryIsPopulated } from '../utils'; |
||||
|
||||
const panelTitle = 'Link with defaults'; |
||||
const extensionTitle = 'Open from time series...'; |
||||
|
||||
const linkOnClickDashboardUid = 'dbfb47c5-e5e5-4d28-8ac7-35f349b95946'; |
||||
const linkPathDashboardUid = 'd1fbb077-cd44-4738-8c8a-d4e66748b719'; |
||||
|
||||
test.describe('configureExtensionLink targeting core extension points', () => { |
||||
test('configureExtensionLink - should add link extension (path) with defaults to time series panel.', async ({ |
||||
gotoDashboardPage, |
||||
page, |
||||
}) => { |
||||
const dashboardPage = await gotoDashboardPage({ uid: linkPathDashboardUid }); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle); |
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' }); |
||||
await expect(page.getByRole('heading', { name: 'Extensions test app' })).toBeVisible(); |
||||
}); |
||||
|
||||
test('should add link extension (onclick) with defaults to time series panel', async ({ |
||||
gotoDashboardPage, |
||||
page, |
||||
}) => { |
||||
const dashboardPage = await gotoDashboardPage({ uid: linkOnClickDashboardUid }); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle); |
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' }); |
||||
await expect(page.getByRole('dialog')).toContainText('Select query from "Link with defaults"'); |
||||
}); |
||||
|
||||
test('should add link extension (onclick) with new title to pie chart panel', async ({ gotoDashboardPage, page }) => { |
||||
const panelTitle = 'Link with new name'; |
||||
const extensionTitle = 'Open from piechart'; |
||||
const dashboardPage = await gotoDashboardPage({ uid: linkOnClickDashboardUid }); |
||||
await ensureExtensionRegistryIsPopulated(page); |
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle); |
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' }); |
||||
await expect(page.getByRole('dialog')).toContainText('Select query from "Link with new name"'); |
||||
}); |
||||
}); |
@ -0,0 +1,8 @@ |
||||
import { test, expect } from '@grafana/plugin-e2e'; |
||||
import { testIds } from '../testIds'; |
||||
import pluginJson from '../plugin.json'; |
||||
|
||||
test('should display component exposed by another app', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/exposed-components`); |
||||
await expect(page.getByTestId(testIds.appB.exposedComponent)).toHaveText('Hello World!'); |
||||
}); |
@ -0,0 +1,11 @@ |
||||
import { test, expect } from '@grafana/plugin-e2e'; |
||||
|
||||
import pluginJson from '../plugin.json'; |
||||
import { testIds } from '../testIds'; |
||||
|
||||
test('should render component with usePluginComponents hook', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/added-components`); |
||||
await expect( |
||||
page.getByTestId(testIds.addedComponentsPage.container).getByTestId(testIds.appB.reusableAddedComponent) |
||||
).toHaveText('Hello World!'); |
||||
}); |
@ -0,0 +1,10 @@ |
||||
import { test, expect } from '@grafana/plugin-e2e'; |
||||
|
||||
import pluginJson from '../plugin.json'; |
||||
import { testIds } from '../testIds'; |
||||
|
||||
test('path link', async ({ page }) => { |
||||
await page.goto(`/a/${pluginJson.id}/added-links`); |
||||
await page.getByTestId(testIds.addedLinksPage.container).getByText('Basic link').click(); |
||||
await expect(page.getByTestId(testIds.appA.container)).toHaveText('Hello Grafana!'); |
||||
}); |
Loading…
Reference in new issue