grafana/public/app/features/datasources/components/EditDataSource.test.tsx

363 lines
11 KiB
TypeScript
Raw Normal View History

DataSources: refactor datasource pages to be reusable (#51874) * refactor: move utility functions out of the redux actions * refactor: move password handlers to the feature root * refactor: move API related functions to an api.ts * refactor: move components under a /components folder * refactor: move page containers under a /pages folder and extract components * refactor: update mocks to be easier to reuse * refactor: move tests into a state/tests/ subfolder * refactor: expose 'initialState' for plugins * refactor: move generic types to the root folder of the feature * refactor: import path fixe * refactor: update import paths for app routes * chore: update betterer * refactor: fix type errors due to changed mock functions * chore: fix mocking context_srv in tests * refactor: udpate imports to be more concise * fix: update failing test because of mocks * refactor: use the new `navId` prop where we can * fix: use UID instead ID in datasource edit links * fix:clean up Redux state when unmounting the edit page * refactor: use `uid` instead of `id` * refactor: always fetch the plugin details when editing a datasource The deleted lines could provide performance benefits, although they also make the implementation more prone to errors. (Mostly because we are storing the information about the currently loaded plugin in a single field, and it was not validating if it is for the latest one). We are planning to introduce some kind of caching, but first we would like to clean up the underlying state a bit (plugins & datasources. * fix: add missing dispatch() wrapper for update datasource callback * refactor: prefer using absolute import paths Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * fix: ESLINT import order issue * refactor: put test files next to their files * refactor: use implicit return types for components * fix: remove caching from datasource fetching I have introduced a cache to only fetch data-sources once, however as we are missing a good logic for updating the instances in the Redux store when they change (create, update, delete), this approach is not keeping the UI in sync. Due to this reason I have removed the caching for now, and will reintroduce it once we have a more robust client-side state logic. Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-07-20 15:25:09 +08:00
import { screen, render } from '@testing-library/react';
import { Provider } from 'react-redux';
import { PluginState } from '@grafana/data';
Frontend: Remove Angular (#99760) * chore(angularsupport): delete feature toggle to disable angular * feat(angular-support): remove config.angularSupportEnabled * chore(jest): remove angular from setup file * chore(angular): delete angular deprecation ui components * refactor(angular): move migration featureflags into migration notice * chore(dashboard): remove angular deprecation notices * chore(annotations): remove angular editor loader * feat(appwrapper): no more angular app loading * feat(pluginscatalog): clean up angular plugin warnings and logic * chore(angular): delete angular app and associated files * feat(plugins): delete old angular graph plugin * feat(plugins): delete old angular table panel * feat(frontend): remove unused appEvent type * feat(dashboards): clean up angular from panel options and menu * feat(plugins): remove graph and table-old from built in plugins and delete sdk * feat(frontend): remove angular related imports in routes and explore graph * feat(theme): remove angular panel styles from global styles * chore(i18n): run make i18n-extract * test(api_plugins_test): refresh snapshot due to deleting old graph and table plugins * chore(angulardeprecation): delete angular migration notice components and usage * test(frontend): clean up tests that assert rendering angular deprecation notices * chore(backend): remove autoMigrateOldPanels feature flag * chore(config): remove angularSupportEnabled from config preventing loading angular plugins * chore(graphpanel): remove autoMigrateGraphPanel from feature toggles * chore(tablepanel): delete autoMigrateTablePanel feature flag * chore(piechart): delete autoMigratePiechartPanel feature flag * chore(worldmappanel): remove autoMigrateWorldmapPanel feature toggle * chore(statpanel): remove autoMigrateStatPanel feature flag * feat(dashboards): remove automigrate feature flags and always auto migrate angular panels * test(pluginsintegration): fix failing loader test * test(frontend): wip: fix failures and skip erroring migration tests * chore(codeowners): remove deleted angular related files and directories * test(graphite): remove angular mock from test file * test(dashboards): skip failing exporter test, remove angularSupportEnabled flags * test(dashbaord): skip another failing panel menu test * Tests: fixes pkg/services/pluginsintegration/loader/loader_test.go (#100505) * Tests: fixes pkg/services/pluginsintegration/plugins_integration_test.go * Trigger Build * chore(dashboards): remove angularComponent from getPanelMenu, update test * feat(dashboards): remove all usage of AngularComponent and getAngularLoader * chore(betterer): refresh results file * feat(plugins): remove PluginAngularBadge component and usage * feat(datasource_srv): remove usage of getLegacyAngularInjector * feat(queryeditor): delete AngularQueryComponentScope type * Chore: removes Angular from plugin_loader * Chore: remove angular from getPlugin * Chore: fix i18n * Trigger Build * Chore: remove more Angular from importPanelPlugin * Chore: remove search options warning * Chore: remove and deprecate Angular related * chore(angular): remove angular dependencies from core and runtime * chore(runtime): delete angular injector * chore(data): delete angular scope from event bus * chore(plugin-catalog): remove code pushing app plugins angular config page * chore(yarn): refresh lock file * chore(frontend): remove ng-loader from webpack configs, remove systemjs cjs plugin * chore(navigation): remove tether-drop cleanup from GrafanaRouter, delete dependency * chore(runtime): delete AngularLoader * chore(betterer): refresh results file * chore(betterer): fix out of sync results file * feat(query): fix type and import errors in QueryEditorRow * test(dashboards): delete skipped angular related tests * Tests: add back tests and fix betterer * Tests: fix broken test * Trigger build * chore(i18n): remove angular deprecation related strings * test: clean up connections and plugins catalog tests * chore(betterer): update results file --------- Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com>
2025-04-04 17:31:35 +08:00
import { setPluginComponentsHook } from '@grafana/runtime';
import { createComponentWithMeta } from 'app/features/plugins/extensions/usePluginComponents';
DataSources: refactor datasource pages to be reusable (#51874) * refactor: move utility functions out of the redux actions * refactor: move password handlers to the feature root * refactor: move API related functions to an api.ts * refactor: move components under a /components folder * refactor: move page containers under a /pages folder and extract components * refactor: update mocks to be easier to reuse * refactor: move tests into a state/tests/ subfolder * refactor: expose 'initialState' for plugins * refactor: move generic types to the root folder of the feature * refactor: import path fixe * refactor: update import paths for app routes * chore: update betterer * refactor: fix type errors due to changed mock functions * chore: fix mocking context_srv in tests * refactor: udpate imports to be more concise * fix: update failing test because of mocks * refactor: use the new `navId` prop where we can * fix: use UID instead ID in datasource edit links * fix:clean up Redux state when unmounting the edit page * refactor: use `uid` instead of `id` * refactor: always fetch the plugin details when editing a datasource The deleted lines could provide performance benefits, although they also make the implementation more prone to errors. (Mostly because we are storing the information about the currently loaded plugin in a single field, and it was not validating if it is for the latest one). We are planning to introduce some kind of caching, but first we would like to clean up the underlying state a bit (plugins & datasources. * fix: add missing dispatch() wrapper for update datasource callback * refactor: prefer using absolute import paths Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * fix: ESLINT import order issue * refactor: put test files next to their files * refactor: use implicit return types for components * fix: remove caching from datasource fetching I have introduced a cache to only fetch data-sources once, however as we are missing a good logic for updating the instances in the Redux store when they change (create, update, delete), this approach is not keeping the UI in sync. Due to this reason I have removed the caching for now, and will reintroduce it once we have a more robust client-side state logic. Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-07-20 15:25:09 +08:00
import { configureStore } from 'app/store/configureStore';
import { getMockDataSource, getMockDataSourceMeta, getMockDataSourceSettingsState } from '../__mocks__';
import { missingRightsMessage } from './DataSourceMissingRightsMessage';
import { readOnlyMessage } from './DataSourceReadOnlyMessage';
import { EditDataSourceView, ViewProps } from './EditDataSource';
jest.mock('@grafana/runtime', () => {
return {
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: jest.fn(() => ({
getInstanceSettings: (uid: string) => ({
uid,
meta: getMockDataSourceMeta(),
}),
})),
};
});
DataSources: refactor datasource pages to be reusable (#51874) * refactor: move utility functions out of the redux actions * refactor: move password handlers to the feature root * refactor: move API related functions to an api.ts * refactor: move components under a /components folder * refactor: move page containers under a /pages folder and extract components * refactor: update mocks to be easier to reuse * refactor: move tests into a state/tests/ subfolder * refactor: expose 'initialState' for plugins * refactor: move generic types to the root folder of the feature * refactor: import path fixe * refactor: update import paths for app routes * chore: update betterer * refactor: fix type errors due to changed mock functions * chore: fix mocking context_srv in tests * refactor: udpate imports to be more concise * fix: update failing test because of mocks * refactor: use the new `navId` prop where we can * fix: use UID instead ID in datasource edit links * fix:clean up Redux state when unmounting the edit page * refactor: use `uid` instead of `id` * refactor: always fetch the plugin details when editing a datasource The deleted lines could provide performance benefits, although they also make the implementation more prone to errors. (Mostly because we are storing the information about the currently loaded plugin in a single field, and it was not validating if it is for the latest one). We are planning to introduce some kind of caching, but first we would like to clean up the underlying state a bit (plugins & datasources. * fix: add missing dispatch() wrapper for update datasource callback * refactor: prefer using absolute import paths Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * fix: ESLINT import order issue * refactor: put test files next to their files * refactor: use implicit return types for components * fix: remove caching from datasource fetching I have introduced a cache to only fetch data-sources once, however as we are missing a good logic for updating the instances in the Redux store when they change (create, update, delete), this approach is not keeping the UI in sync. Due to this reason I have removed the caching for now, and will reintroduce it once we have a more robust client-side state logic. Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-07-20 15:25:09 +08:00
const setup = (props?: Partial<ViewProps>) => {
const store = configureStore();
return render(
<Provider store={store}>
<EditDataSourceView
dataSource={getMockDataSource()}
dataSourceMeta={getMockDataSourceMeta()}
dataSourceSettings={getMockDataSourceSettingsState()}
dataSourceRights={{ readOnly: false, hasWriteRights: true, hasDeleteRights: true }}
exploreUrl={'/explore'}
onDelete={jest.fn()}
onDefaultChange={jest.fn()}
onNameChange={jest.fn()}
onOptionsChange={jest.fn()}
onTest={jest.fn()}
onUpdate={jest.fn()}
{...props}
/>
</Provider>
);
};
describe('<EditDataSource>', () => {
beforeEach(() => {
setPluginComponentsHook(jest.fn().mockReturnValue({ isLoading: false, components: [] }));
});
DataSources: refactor datasource pages to be reusable (#51874) * refactor: move utility functions out of the redux actions * refactor: move password handlers to the feature root * refactor: move API related functions to an api.ts * refactor: move components under a /components folder * refactor: move page containers under a /pages folder and extract components * refactor: update mocks to be easier to reuse * refactor: move tests into a state/tests/ subfolder * refactor: expose 'initialState' for plugins * refactor: move generic types to the root folder of the feature * refactor: import path fixe * refactor: update import paths for app routes * chore: update betterer * refactor: fix type errors due to changed mock functions * chore: fix mocking context_srv in tests * refactor: udpate imports to be more concise * fix: update failing test because of mocks * refactor: use the new `navId` prop where we can * fix: use UID instead ID in datasource edit links * fix:clean up Redux state when unmounting the edit page * refactor: use `uid` instead of `id` * refactor: always fetch the plugin details when editing a datasource The deleted lines could provide performance benefits, although they also make the implementation more prone to errors. (Mostly because we are storing the information about the currently loaded plugin in a single field, and it was not validating if it is for the latest one). We are planning to introduce some kind of caching, but first we would like to clean up the underlying state a bit (plugins & datasources. * fix: add missing dispatch() wrapper for update datasource callback * refactor: prefer using absolute import paths Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * fix: ESLINT import order issue * refactor: put test files next to their files * refactor: use implicit return types for components * fix: remove caching from datasource fetching I have introduced a cache to only fetch data-sources once, however as we are missing a good logic for updating the instances in the Redux store when they change (create, update, delete), this approach is not keeping the UI in sync. Due to this reason I have removed the caching for now, and will reintroduce it once we have a more robust client-side state logic. Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-07-20 15:25:09 +08:00
describe('On loading errors', () => {
it('should render a Back button', () => {
setup({
dataSource: getMockDataSource({ name: 'My Datasource' }),
dataSourceSettings: getMockDataSourceSettingsState({ loadError: 'Some weird error.' }),
});
expect(screen.queryByText('Loading ...')).not.toBeInTheDocument();
expect(screen.queryByText('My Datasource')).not.toBeInTheDocument();
expect(screen.queryByText('Back')).toBeVisible();
});
it('should render a Delete button if the user has rights delete the datasource', () => {
setup({
dataSourceSettings: getMockDataSourceSettingsState({ loadError: 'Some weird error.' }),
dataSourceRights: {
readOnly: false,
hasDeleteRights: true,
hasWriteRights: true,
},
});
expect(screen.queryByText('Delete')).toBeVisible();
});
it('should not render a Delete button if the user has no rights to delete the datasource', () => {
setup({
dataSourceSettings: getMockDataSourceSettingsState({ loadError: 'Some weird error.' }),
dataSourceRights: {
readOnly: false,
hasDeleteRights: false,
hasWriteRights: true,
},
});
expect(screen.queryByText('Delete')).not.toBeInTheDocument();
});
it('should render a message if the datasource is read-only', () => {
setup({
dataSourceSettings: getMockDataSourceSettingsState({ loadError: 'Some weird error.' }),
dataSourceRights: {
readOnly: true,
hasDeleteRights: false,
hasWriteRights: true,
},
});
expect(screen.queryByText(readOnlyMessage)).toBeVisible();
});
});
describe('On loading', () => {
it('should render a loading indicator while the data is being fetched', () => {
setup({
dataSource: getMockDataSource({ name: 'My Datasource' }),
dataSourceSettings: getMockDataSourceSettingsState({ loading: true }),
});
expect(screen.queryByText('Loading ...')).toBeVisible();
expect(screen.queryByText('My Datasource')).not.toBeInTheDocument();
});
it('should not render loading when data is already available', () => {
setup();
expect(screen.queryByText('Loading ...')).not.toBeInTheDocument();
});
});
describe('On editing', () => {
it('should render no messages if the user has write access and if the data-source is not read-only', () => {
setup({
dataSourceRights: {
readOnly: false,
hasDeleteRights: true,
hasWriteRights: true,
},
});
expect(screen.queryByText(readOnlyMessage)).not.toBeInTheDocument();
expect(screen.queryByText(missingRightsMessage)).not.toBeInTheDocument();
});
it('should render a message if the user has no write access', () => {
setup({
dataSourceRights: {
readOnly: false,
hasDeleteRights: false,
hasWriteRights: false,
},
});
expect(screen.queryByText(missingRightsMessage)).toBeVisible();
});
it('should render a message if the data-source is read-only', () => {
setup({
dataSourceRights: {
readOnly: true,
hasDeleteRights: false,
hasWriteRights: false,
},
});
expect(screen.queryByText(readOnlyMessage)).toBeVisible();
});
it('should render a beta info message if the plugin is still in Beta state', () => {
setup({
dataSourceMeta: getMockDataSourceMeta({
state: PluginState.beta,
}),
});
expect(screen.getByTitle('This feature is close to complete but not fully tested')).toBeVisible();
});
it('should render an alpha info message if the plugin is still in Alpha state', () => {
setup({
dataSourceMeta: getMockDataSourceMeta({
state: PluginState.alpha,
}),
});
expect(
screen.getByTitle('This feature is experimental and future updates might not be backward compatible')
).toBeVisible();
});
it('should render testing errors with a detailed error message', () => {
const message = 'message';
const detailsMessage = 'detailed message';
setup({
dataSourceSettings: getMockDataSourceSettingsState({
testingStatus: {
message,
status: 'error',
details: { message: detailsMessage },
},
}),
});
expect(screen.getByText(message)).toBeVisible();
expect(screen.getByText(detailsMessage)).toBeVisible();
});
it('should render testing errors with empty details', () => {
const message = 'message';
setup({
dataSourceSettings: getMockDataSourceSettingsState({
testingStatus: {
message,
status: 'error',
details: {},
},
}),
});
expect(screen.getByText(message)).toBeVisible();
});
it('should render testing errors with no details', () => {
const message = 'message';
setup({
dataSourceSettings: getMockDataSourceSettingsState({
testingStatus: {
message,
status: 'error',
},
}),
});
expect(screen.getByText(message)).toBeVisible();
});
it('should use the verboseMessage property in the error details whenever it is available', () => {
const message = 'message';
const detailsMessage = 'detailed message';
const detailsVerboseMessage = 'even more detailed...';
setup({
dataSourceSettings: getMockDataSourceSettingsState({
testingStatus: {
message,
status: 'error',
details: {
details: detailsMessage,
verboseMessage: detailsVerboseMessage,
},
},
}),
});
expect(screen.queryByText(message)).toBeVisible();
expect(screen.queryByText(detailsMessage)).not.toBeInTheDocument();
expect(screen.queryByText(detailsVerboseMessage)).toBeInTheDocument();
});
});
describe('when extending the datasource config form', () => {
it('should be possible to extend the form with a "component" extension in case the plugin ID is whitelisted', () => {
const message = "I'm a UI extension component!";
setPluginComponentsHook(
jest.fn().mockReturnValue({
isLoading: false,
components: [
createComponentWithMeta(
{
pluginId: 'grafana-pdc-app',
title: 'Example component',
description: 'Example description',
component: () => <div>{message}</div>,
},
'1'
),
],
})
);
setup({
dataSourceRights: {
readOnly: false,
hasDeleteRights: true,
hasWriteRights: true,
},
});
expect(screen.queryByText(message)).toBeVisible();
});
it('should NOT be possible to extend the form with a "component" extension in case the plugin ID is NOT whitelisted', () => {
const message = "I'm a UI extension component!";
setPluginComponentsHook(
jest.fn().mockReturnValue({
isLoading: false,
components: [
createComponentWithMeta(
{
pluginId: 'myorg-basic-app',
title: 'Example component',
description: 'Example description',
component: () => <div>{message}</div>,
},
'1'
),
],
})
);
setup({
dataSourceRights: {
readOnly: false,
hasDeleteRights: true,
hasWriteRights: true,
},
});
expect(screen.queryByText(message)).not.toBeInTheDocument();
});
it('should pass a context prop to the rendered UI extension component', () => {
const message = "I'm a UI extension component!";
const component = jest.fn().mockReturnValue(<div>{message}</div>);
setPluginComponentsHook(
jest.fn().mockReturnValue({
isLoading: false,
components: [
createComponentWithMeta(
{
pluginId: 'grafana-pdc-app',
title: 'Example component',
description: 'Example description',
component,
},
'1'
),
],
})
);
setup({
dataSourceRights: {
readOnly: false,
hasDeleteRights: true,
hasWriteRights: true,
},
});
expect(component).toHaveBeenCalled();
const props = component.mock.calls[0][0];
expect(props.context).toBeDefined();
expect(props.context.dataSource).toBeDefined();
expect(props.context.dataSourceMeta).toBeDefined();
expect(props.context.setJsonData).toBeDefined();
expect(props.context.setSecureJsonData).toBeDefined();
expect(props.context.testingStatus).toBeDefined();
});
});
DataSources: refactor datasource pages to be reusable (#51874) * refactor: move utility functions out of the redux actions * refactor: move password handlers to the feature root * refactor: move API related functions to an api.ts * refactor: move components under a /components folder * refactor: move page containers under a /pages folder and extract components * refactor: update mocks to be easier to reuse * refactor: move tests into a state/tests/ subfolder * refactor: expose 'initialState' for plugins * refactor: move generic types to the root folder of the feature * refactor: import path fixe * refactor: update import paths for app routes * chore: update betterer * refactor: fix type errors due to changed mock functions * chore: fix mocking context_srv in tests * refactor: udpate imports to be more concise * fix: update failing test because of mocks * refactor: use the new `navId` prop where we can * fix: use UID instead ID in datasource edit links * fix:clean up Redux state when unmounting the edit page * refactor: use `uid` instead of `id` * refactor: always fetch the plugin details when editing a datasource The deleted lines could provide performance benefits, although they also make the implementation more prone to errors. (Mostly because we are storing the information about the currently loaded plugin in a single field, and it was not validating if it is for the latest one). We are planning to introduce some kind of caching, but first we would like to clean up the underlying state a bit (plugins & datasources. * fix: add missing dispatch() wrapper for update datasource callback * refactor: prefer using absolute import paths Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * fix: ESLINT import order issue * refactor: put test files next to their files * refactor: use implicit return types for components * fix: remove caching from datasource fetching I have introduced a cache to only fetch data-sources once, however as we are missing a good logic for updating the instances in the Redux store when they change (create, update, delete), this approach is not keeping the UI in sync. Due to this reason I have removed the caching for now, and will reintroduce it once we have a more robust client-side state logic. Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-07-20 15:25:09 +08:00
});