mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
235 lines
7.9 KiB
TypeScript
235 lines
7.9 KiB
TypeScript
import { memo } from 'react';
|
|
|
|
import { PluginExtensionAddedLinkConfig, PluginExtensionLinkConfig, PluginExtensionPoints } from '@grafana/data';
|
|
|
|
import {
|
|
assertConfigureIsValid,
|
|
assertLinkPathIsValid,
|
|
assertStringProps,
|
|
isExtensionPointIdValid,
|
|
isGrafanaCoreExtensionPoint,
|
|
isReactComponent,
|
|
} from './validators';
|
|
|
|
describe('Plugin Extension Validators', () => {
|
|
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('assertConfigureIsValid()', () => {
|
|
it('should NOT throw an error if the configure() function is missing', () => {
|
|
expect(() => {
|
|
assertConfigureIsValid({
|
|
title: 'Title',
|
|
description: 'Description',
|
|
targets: 'grafana/some-page/extension-point-a',
|
|
} as PluginExtensionAddedLinkConfig);
|
|
}).not.toThrowError();
|
|
});
|
|
|
|
it('should NOT throw an error if the configure() function is a valid function', () => {
|
|
expect(() => {
|
|
assertConfigureIsValid({
|
|
title: 'Title',
|
|
description: 'Description',
|
|
targets: 'grafana/some-page/extension-point-a',
|
|
configure: () => {},
|
|
} as PluginExtensionAddedLinkConfig);
|
|
}).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('isReactComponent()', () => {
|
|
it('should return TRUE if we pass in a valid React component', () => {
|
|
expect(isReactComponent(() => <div>Some text</div>)).toBe(true);
|
|
});
|
|
|
|
it('should return TRUE if we pass in a component wrapped with React.memo()', () => {
|
|
const Component = () => <div>Some text</div>;
|
|
const wrapped = memo(() => (
|
|
<div>
|
|
<Component />
|
|
</div>
|
|
));
|
|
wrapped.displayName = 'MyComponent';
|
|
|
|
expect(isReactComponent(wrapped)).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);
|
|
});
|
|
});
|
|
|
|
describe('isGrafanaCoreExtensionPoint()', () => {
|
|
it('should return TRUE if we pass an PluginExtensionPoints value', () => {
|
|
expect(isGrafanaCoreExtensionPoint(PluginExtensionPoints.AlertingAlertingRuleAction)).toBe(true);
|
|
});
|
|
|
|
it('should return TRUE if we pass a string that is not listed under the PluginExtensionPoints enum', () => {
|
|
expect(isGrafanaCoreExtensionPoint('grafana/alerting/alertingrule/action')).toBe(true);
|
|
});
|
|
|
|
it('should return FALSE if we pass a string that is not listed under the PluginExtensionPoints enum', () => {
|
|
expect(isGrafanaCoreExtensionPoint('grafana/dashboard/alertingrule/action')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('isExtensionPointIdValid()', () => {
|
|
test.each([
|
|
// We (for now allow core Grafana extension points to run without a version)
|
|
['grafana/extension-point', ''],
|
|
['grafana/extension-point', 'grafana'],
|
|
['myorg-extensions-app/extension-point', 'myorg-extensions-app'],
|
|
['myorg-extensions-app/extension-point/v1', 'myorg-extensions-app'],
|
|
['plugins/myorg-extensions-app/extension-point/v1', 'myorg-extensions-app'],
|
|
['plugins/myorg-basic-app/start', 'myorg-basic-app'],
|
|
['myorg-extensions-app/extension-point/v1', 'myorg-extensions-app'],
|
|
['plugins/myorg-extensions-app/extension-point/v1', 'myorg-extensions-app'],
|
|
['plugins/grafana-app-observability-app/service/action', 'grafana-app-observability-app'],
|
|
['plugins/grafana-k8s-app/cluster/action', 'grafana-k8s-app'],
|
|
['plugins/grafana-oncall-app/alert-group/action', 'grafana-oncall-app'],
|
|
['plugins/grafana-oncall-app/alert-group/action/v1', 'grafana-oncall-app'],
|
|
['plugins/grafana-oncall-app/alert-group/action/v1.0.0', 'grafana-oncall-app'],
|
|
])('should return TRUE if the extension point id is valid ("%s", "%s")', (extensionPointId, pluginId) => {
|
|
expect(
|
|
isExtensionPointIdValid({
|
|
extensionPointId,
|
|
pluginId,
|
|
})
|
|
).toBe(true);
|
|
});
|
|
|
|
test.each([
|
|
[
|
|
// Plugin id mismatch
|
|
'myorg-extensions-app/extension-point/v1',
|
|
'myorgs-other-app',
|
|
],
|
|
[
|
|
// Missing plugin id prefix
|
|
'extension-point/v1',
|
|
'myorgs-extensions-app',
|
|
],
|
|
])('should return FALSE if the extension point id is invalid ("%s", "%s")', (extensionPointId, pluginId) => {
|
|
expect(
|
|
isExtensionPointIdValid({
|
|
extensionPointId,
|
|
pluginId,
|
|
})
|
|
).toBe(false);
|
|
});
|
|
});
|
|
});
|