mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 20:52:10 +08:00

* feat: add a new UI extension type: component * tests: add tests for checking if it is a react component * fix: remove reference to not existing type * chore: update betterer results * review: remove unnecessary override function for components * review: use a single type notation in import * review: stop exporting `PluginExtensionBase` * refactor: make extension config types more explicit By using some repetition now these types are much easier to oversee.
284 lines
8.9 KiB
TypeScript
284 lines
8.9 KiB
TypeScript
import React from 'react';
|
|
|
|
import { PluginExtension, PluginExtensionLinkConfig, PluginExtensionTypes } from '@grafana/data';
|
|
|
|
import {
|
|
assertConfigureIsValid,
|
|
assertLinkPathIsValid,
|
|
assertExtensionPointIdIsValid,
|
|
assertPluginExtensionLink,
|
|
assertStringProps,
|
|
isPluginExtensionConfigValid,
|
|
isReactComponent,
|
|
} from './validators';
|
|
|
|
describe('Plugin Extension Validators', () => {
|
|
describe('assertPluginExtensionLink()', () => {
|
|
it('should NOT throw an error if it is a link extension', () => {
|
|
expect(() => {
|
|
assertPluginExtensionLink({
|
|
id: 'id',
|
|
pluginId: 'myorg-b-app',
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
path: '...',
|
|
} as PluginExtension);
|
|
}).not.toThrowError();
|
|
});
|
|
|
|
it('should throw an error if it is not a link extension', () => {
|
|
expect(() => {
|
|
assertPluginExtensionLink({
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
} as PluginExtension);
|
|
}).toThrowError();
|
|
});
|
|
});
|
|
|
|
describe('assertLinkPathIsValid()', () => {
|
|
it('should not throw an error if the link path is valid', () => {
|
|
expect(() => {
|
|
const pluginId = 'myorg-b-app';
|
|
const extension = {
|
|
path: `/a/${pluginId}/overview`,
|
|
title: 'My Plugin',
|
|
description: 'My Plugin Description',
|
|
extensionPointId: '...',
|
|
};
|
|
|
|
assertLinkPathIsValid(pluginId, extension.path);
|
|
}).not.toThrowError();
|
|
});
|
|
|
|
it('should throw an error if the link path is pointing to a different plugin', () => {
|
|
expect(() => {
|
|
const extension = {
|
|
path: `/a/myorg-b-app/overview`,
|
|
title: 'My Plugin',
|
|
description: 'My Plugin Description',
|
|
extensionPointId: '...',
|
|
};
|
|
|
|
assertLinkPathIsValid('another-plugin-app', extension.path);
|
|
}).toThrowError();
|
|
});
|
|
|
|
it('should throw an error if the link path is not prefixed with "/a/<PLUGIN_ID>"', () => {
|
|
expect(() => {
|
|
const extension = {
|
|
path: `/some-bad-path`,
|
|
title: 'My Plugin',
|
|
description: 'My Plugin Description',
|
|
extensionPointId: '...',
|
|
};
|
|
|
|
assertLinkPathIsValid('myorg-b-app', extension.path);
|
|
}).toThrowError();
|
|
});
|
|
});
|
|
|
|
describe('assertExtensionPointIdIsValid()', () => {
|
|
it('should throw an error if the extensionPointId does not have the right prefix', () => {
|
|
expect(() => {
|
|
assertExtensionPointIdIsValid({
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'wrong-extension-point-id',
|
|
});
|
|
}).toThrowError();
|
|
});
|
|
|
|
it('should NOT throw an error if the extensionPointId is correct', () => {
|
|
expect(() => {
|
|
assertExtensionPointIdIsValid({
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
});
|
|
|
|
assertExtensionPointIdIsValid({
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'plugins/my-super-plugin/some-page/extension-point-a',
|
|
});
|
|
}).not.toThrowError();
|
|
});
|
|
});
|
|
|
|
describe('assertConfigureIsValid()', () => {
|
|
it('should NOT throw an error if the configure() function is missing', () => {
|
|
expect(() => {
|
|
assertConfigureIsValid({
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
} as PluginExtensionLinkConfig);
|
|
}).not.toThrowError();
|
|
});
|
|
|
|
it('should NOT throw an error if the configure() function is a valid function', () => {
|
|
expect(() => {
|
|
assertConfigureIsValid({
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
configure: () => {},
|
|
} as PluginExtensionLinkConfig);
|
|
}).not.toThrowError();
|
|
});
|
|
|
|
it('should throw an error if the configure() function is defined but is not a function', () => {
|
|
expect(() => {
|
|
assertConfigureIsValid(
|
|
// @ts-ignore
|
|
{
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
handler: () => {},
|
|
configure: '() => {}',
|
|
} as PluginExtensionLinkConfig
|
|
);
|
|
}).toThrowError();
|
|
});
|
|
});
|
|
|
|
describe('assertStringProps()', () => {
|
|
it('should throw an error if any of the expected string properties is missing', () => {
|
|
expect(() => {
|
|
assertStringProps(
|
|
{
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
},
|
|
['title', 'description', 'extensionPointId']
|
|
);
|
|
}).toThrowError();
|
|
});
|
|
|
|
it('should throw an error if any of the expected string properties is an empty string', () => {
|
|
expect(() => {
|
|
assertStringProps(
|
|
{
|
|
title: '',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
},
|
|
['title', 'description', 'extensionPointId']
|
|
);
|
|
}).toThrowError();
|
|
});
|
|
|
|
it('should NOT throw an error if the expected string props are present and not empty', () => {
|
|
expect(() => {
|
|
assertStringProps(
|
|
{
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
},
|
|
['title', 'description', 'extensionPointId']
|
|
);
|
|
}).not.toThrowError();
|
|
});
|
|
|
|
it('should NOT throw an error if there are other existing and empty string properties, that we did not specify', () => {
|
|
expect(() => {
|
|
assertStringProps(
|
|
{
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
dontCare: '',
|
|
},
|
|
['title', 'description', 'extensionPointId']
|
|
);
|
|
}).not.toThrowError();
|
|
});
|
|
});
|
|
|
|
describe('isPluginExtensionConfigValid()', () => {
|
|
it('should return TRUE if the plugin extension configuration is valid', () => {
|
|
const pluginId = 'my-super-plugin';
|
|
|
|
expect(
|
|
isPluginExtensionConfigValid(pluginId, {
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
onClick: jest.fn(),
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
} as PluginExtensionLinkConfig)
|
|
).toBe(true);
|
|
|
|
expect(
|
|
isPluginExtensionConfigValid(pluginId, {
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
path: `/a/${pluginId}/page`,
|
|
} as PluginExtensionLinkConfig)
|
|
).toBe(true);
|
|
});
|
|
|
|
it('should return FALSE if the plugin extension configuration is invalid', () => {
|
|
const pluginId = 'my-super-plugin';
|
|
|
|
global.console.warn = jest.fn();
|
|
|
|
// Link (wrong path)
|
|
expect(
|
|
isPluginExtensionConfigValid(pluginId, {
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
path: '/administration/users',
|
|
} as PluginExtensionLinkConfig)
|
|
).toBe(false);
|
|
|
|
// Link (no path and no onClick)
|
|
expect(
|
|
isPluginExtensionConfigValid(pluginId, {
|
|
type: PluginExtensionTypes.link,
|
|
title: 'Title',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
} as PluginExtensionLinkConfig)
|
|
).toBe(false);
|
|
|
|
// Link (missing title)
|
|
expect(
|
|
isPluginExtensionConfigValid(pluginId, {
|
|
type: PluginExtensionTypes.link,
|
|
title: '',
|
|
description: 'Description',
|
|
extensionPointId: 'grafana/some-page/extension-point-a',
|
|
path: `/a/${pluginId}/page`,
|
|
} as PluginExtensionLinkConfig)
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('isReactComponent()', () => {
|
|
it('should return TRUE if we pass in a valid React component', () => {
|
|
expect(isReactComponent(() => <div>Some text</div>)).toBe(true);
|
|
});
|
|
|
|
it('should return FALSE if we pass in a valid React component', () => {
|
|
expect(isReactComponent('Foo bar')).toBe(false);
|
|
expect(isReactComponent(123)).toBe(false);
|
|
expect(isReactComponent(false)).toBe(false);
|
|
expect(isReactComponent(undefined)).toBe(false);
|
|
expect(isReactComponent(null)).toBe(false);
|
|
});
|
|
});
|
|
});
|