mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Scopes: Reload dashboards when on scopes change (#92804)
This commit is contained in:
parent
ce7533eb3b
commit
2a2813b577
@ -0,0 +1,22 @@
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { sceneGraph } from '@grafana/scenes';
|
||||
import { ScopesFacade } from 'app/features/scopes';
|
||||
|
||||
export interface DashboardScopesFacadeState {
|
||||
reloadOnScopesChange?: boolean;
|
||||
uid?: string;
|
||||
}
|
||||
|
||||
export class DashboardScopesFacade extends ScopesFacade {
|
||||
constructor({ reloadOnScopesChange, uid }: DashboardScopesFacadeState) {
|
||||
super({
|
||||
handler: (facade) => {
|
||||
if (reloadOnScopesChange && uid) {
|
||||
locationService.reload();
|
||||
} else {
|
||||
sceneGraph.getTimeRange(facade).onRefresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
@ -19,10 +19,8 @@ import {
|
||||
SceneDataLayerProvider,
|
||||
SceneDataLayerControls,
|
||||
UserActionEvent,
|
||||
sceneGraph,
|
||||
} from '@grafana/scenes';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { ScopesFacade } from 'app/features/scopes';
|
||||
import { DashboardDTO, DashboardDataDTO } from 'app/types';
|
||||
|
||||
import { AlertStatesDataLayer } from '../scene/AlertStatesDataLayer';
|
||||
@ -32,6 +30,7 @@ import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
|
||||
import { DashboardGridItem, RepeatDirection } from '../scene/DashboardGridItem';
|
||||
import { registerDashboardMacro } from '../scene/DashboardMacro';
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { DashboardScopesFacade } from '../scene/DashboardScopesFacade';
|
||||
import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior';
|
||||
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
|
||||
import { panelLinksBehavior, panelMenuBehavior } from '../scene/PanelMenuBehavior';
|
||||
@ -245,8 +244,9 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel,
|
||||
registerPanelInteractionsReporter,
|
||||
new behaviors.LiveNowTimer({ enabled: oldModel.liveNow }),
|
||||
preserveDashboardSceneStateInLocalStorage,
|
||||
new ScopesFacade({
|
||||
handler: (facade) => sceneGraph.getTimeRange(facade).onRefresh(),
|
||||
new DashboardScopesFacade({
|
||||
reloadOnScopesChange: oldModel.meta.reloadOnScopesChange,
|
||||
uid: oldModel.uid,
|
||||
}),
|
||||
],
|
||||
$data: new DashboardDataLayerSet({ annotationLayers, alertStatesLayer }),
|
||||
|
@ -42,7 +42,7 @@ class LegacyDashboardAPI implements DashboardAPI {
|
||||
}
|
||||
|
||||
getDashboardDTO(uid: string): Promise<DashboardDTO> {
|
||||
const scopes = getSelectedScopesNames();
|
||||
const scopes = config.featureToggles.passScopeToDashboardApi ? getSelectedScopesNames() : [];
|
||||
const queryParams = scopes.length > 0 ? { scopes } : undefined;
|
||||
|
||||
return getBackendSrv().get<DashboardDTO>(`/api/dashboards/uid/${uid}`, queryParams);
|
||||
|
38
public/app/features/scopes/tests/dashboardReload.test.ts
Normal file
38
public/app/features/scopes/tests/dashboardReload.test.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { updateScopes } from './utils/actions';
|
||||
import { expectDashboardReload, expectNotDashboardReload } from './utils/assertions';
|
||||
import { getDatasource, getInstanceSettings, getMock } from './utils/mocks';
|
||||
import { renderDashboard, resetScenes } from './utils/render';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
useChromeHeaderHeight: jest.fn(),
|
||||
getBackendSrv: () => ({ get: getMock }),
|
||||
getDataSourceSrv: () => ({ get: getDatasource, getInstanceSettings }),
|
||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||
}));
|
||||
|
||||
describe('Dashboard reload', () => {
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.groupByVariable = true;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await resetScenes();
|
||||
});
|
||||
|
||||
it('Does not reload the dashboard without UID', async () => {
|
||||
renderDashboard({ uid: undefined }, { reloadOnScopesChange: true });
|
||||
await updateScopes(['grafana']);
|
||||
expectNotDashboardReload();
|
||||
});
|
||||
|
||||
it('Reloads the dashboard with UID', async () => {
|
||||
renderDashboard({}, { reloadOnScopesChange: true });
|
||||
await updateScopes(['grafana']);
|
||||
expectDashboardReload();
|
||||
});
|
||||
});
|
@ -1,84 +1,48 @@
|
||||
import { cleanup } from '@testing-library/react';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { setDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
|
||||
|
||||
import { config, locationService } from '@grafana/runtime';
|
||||
import { getDashboardAPI, setDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
|
||||
|
||||
import { initializeScopes } from '../instance';
|
||||
|
||||
import { getMock } from './utils/mocks';
|
||||
import { resetScenes } from './utils/render';
|
||||
import { getDashboardDTO, updateScopes } from './utils/actions';
|
||||
import { expectNewDashboardDTO, expectOldDashboardDTO } from './utils/assertions';
|
||||
import { getDatasource, getInstanceSettings, getMock } from './utils/mocks';
|
||||
import { renderDashboard, resetScenes } from './utils/render';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getBackendSrv: () => ({
|
||||
get: getMock,
|
||||
}),
|
||||
useChromeHeaderHeight: jest.fn(),
|
||||
getBackendSrv: () => ({ get: getMock }),
|
||||
getDataSourceSrv: () => ({ get: getDatasource, getInstanceSettings }),
|
||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||
}));
|
||||
|
||||
describe('Scopes', () => {
|
||||
describe('Dashboards API', () => {
|
||||
describe('Feature flag off', () => {
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.passScopeToDashboardApi = false;
|
||||
});
|
||||
const runTest = async (passScopes: boolean, kubernetesApi: boolean) => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.passScopeToDashboardApi = passScopes;
|
||||
config.featureToggles.kubernetesDashboards = kubernetesApi;
|
||||
setDashboardAPI(undefined);
|
||||
renderDashboard({}, { reloadOnScopesChange: true });
|
||||
await updateScopes(['grafana', 'mimir']);
|
||||
await getDashboardDTO();
|
||||
|
||||
beforeEach(() => {
|
||||
setDashboardAPI(undefined);
|
||||
locationService.push('/?scopes=scope1&scopes=scope2&scopes=scope3');
|
||||
});
|
||||
if (kubernetesApi) {
|
||||
return expectNewDashboardDTO();
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
resetScenes();
|
||||
cleanup();
|
||||
});
|
||||
if (passScopes) {
|
||||
return expectOldDashboardDTO(['grafana', 'mimir']);
|
||||
}
|
||||
|
||||
it('Legacy API should not pass the scopes', async () => {
|
||||
config.featureToggles.kubernetesDashboards = false;
|
||||
await getDashboardAPI().getDashboardDTO('1');
|
||||
expect(getMock).toHaveBeenCalledWith('/api/dashboards/uid/1', undefined);
|
||||
});
|
||||
return expectOldDashboardDTO();
|
||||
};
|
||||
|
||||
it('K8s API should not pass the scopes', async () => {
|
||||
config.featureToggles.kubernetesDashboards = true;
|
||||
await getDashboardAPI().getDashboardDTO('1');
|
||||
expect(getMock).toHaveBeenCalledWith(
|
||||
'/apis/dashboard.grafana.app/v0alpha1/namespaces/default/dashboards/1/dto'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Feature flag on', () => {
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.passScopeToDashboardApi = true;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
setDashboardAPI(undefined);
|
||||
locationService.push('/?scopes=scope1&scopes=scope2&scopes=scope3');
|
||||
initializeScopes();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetScenes();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('Legacy API should pass the scopes', async () => {
|
||||
config.featureToggles.kubernetesDashboards = false;
|
||||
await getDashboardAPI().getDashboardDTO('1');
|
||||
expect(getMock).toHaveBeenCalledWith('/api/dashboards/uid/1', { scopes: ['scope1', 'scope2', 'scope3'] });
|
||||
});
|
||||
|
||||
it('K8s API should not pass the scopes', async () => {
|
||||
config.featureToggles.kubernetesDashboards = true;
|
||||
await getDashboardAPI().getDashboardDTO('1');
|
||||
expect(getMock).toHaveBeenCalledWith(
|
||||
'/apis/dashboard.grafana.app/v0alpha1/namespaces/default/dashboards/1/dto'
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('Dashboards API', () => {
|
||||
afterEach(async () => {
|
||||
setDashboardAPI(undefined);
|
||||
await resetScenes();
|
||||
});
|
||||
|
||||
it('Legacy API should not pass the scopes with feature flag off', async () => runTest(false, false));
|
||||
it('K8s API should not pass the scopes with feature flag off', async () => runTest(false, true));
|
||||
it('Legacy API should pass the scopes with feature flag on', async () => runTest(true, false));
|
||||
it('K8s API should not pass the scopes with feature flag on', async () => runTest(true, true));
|
||||
});
|
||||
|
250
public/app/features/scopes/tests/dashboardsList.test.ts
Normal file
250
public/app/features/scopes/tests/dashboardsList.test.ts
Normal file
@ -0,0 +1,250 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import {
|
||||
clearNotFound,
|
||||
expandDashboardFolder,
|
||||
searchDashboards,
|
||||
toggleDashboards,
|
||||
updateScopes,
|
||||
} from './utils/actions';
|
||||
import {
|
||||
expectDashboardFolderNotInDocument,
|
||||
expectDashboardInDocument,
|
||||
expectDashboardLength,
|
||||
expectDashboardNotInDocument,
|
||||
expectDashboardSearchValue,
|
||||
expectDashboardsSearch,
|
||||
expectNoDashboardsForFilter,
|
||||
expectNoDashboardsForScope,
|
||||
expectNoDashboardsNoScopes,
|
||||
expectNoDashboardsSearch,
|
||||
} from './utils/assertions';
|
||||
import { fetchDashboardsSpy, getDatasource, getInstanceSettings, getMock } from './utils/mocks';
|
||||
import { renderDashboard, resetScenes } from './utils/render';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
useChromeHeaderHeight: jest.fn(),
|
||||
getBackendSrv: () => ({ get: getMock }),
|
||||
getDataSourceSrv: () => ({ get: getDatasource, getInstanceSettings }),
|
||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||
}));
|
||||
|
||||
describe('Dashboards list', () => {
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.groupByVariable = true;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
renderDashboard();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await resetScenes();
|
||||
});
|
||||
|
||||
it('Does not fetch dashboards list when the list is not expanded', async () => {
|
||||
await updateScopes(['mimir']);
|
||||
expect(fetchDashboardsSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Fetches dashboards list when the list is expanded', async () => {
|
||||
await toggleDashboards();
|
||||
await updateScopes(['mimir']);
|
||||
expect(fetchDashboardsSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Fetches dashboards list when the list is expanded after scope selection', async () => {
|
||||
await updateScopes(['mimir']);
|
||||
await toggleDashboards();
|
||||
expect(fetchDashboardsSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Shows dashboards for multiple scopes', async () => {
|
||||
await toggleDashboards();
|
||||
await updateScopes(['grafana']);
|
||||
await expandDashboardFolder('General');
|
||||
await expandDashboardFolder('Observability');
|
||||
await expandDashboardFolder('Usage');
|
||||
expectDashboardFolderNotInDocument('Components');
|
||||
expectDashboardFolderNotInDocument('Investigations');
|
||||
expectDashboardInDocument('general-data-sources');
|
||||
expectDashboardInDocument('general-usage');
|
||||
expectDashboardInDocument('observability-backend-errors');
|
||||
expectDashboardInDocument('observability-backend-logs');
|
||||
expectDashboardInDocument('observability-frontend-errors');
|
||||
expectDashboardInDocument('observability-frontend-logs');
|
||||
expectDashboardInDocument('usage-data-sources');
|
||||
expectDashboardInDocument('usage-stats');
|
||||
expectDashboardInDocument('usage-usage-overview');
|
||||
expectDashboardInDocument('frontend');
|
||||
expectDashboardInDocument('overview');
|
||||
expectDashboardInDocument('stats');
|
||||
expectDashboardNotInDocument('multiple3-datasource-errors');
|
||||
expectDashboardNotInDocument('multiple4-datasource-logs');
|
||||
expectDashboardNotInDocument('multiple0-ingester');
|
||||
expectDashboardNotInDocument('multiple1-distributor');
|
||||
expectDashboardNotInDocument('multiple2-compacter');
|
||||
expectDashboardNotInDocument('another-stats');
|
||||
|
||||
await updateScopes(['grafana', 'mimir']);
|
||||
await expandDashboardFolder('General');
|
||||
await expandDashboardFolder('Observability');
|
||||
await expandDashboardFolder('Usage');
|
||||
await expandDashboardFolder('Components');
|
||||
await expandDashboardFolder('Investigations');
|
||||
expectDashboardInDocument('general-data-sources');
|
||||
expectDashboardInDocument('general-usage');
|
||||
expectDashboardInDocument('observability-backend-errors');
|
||||
expectDashboardInDocument('observability-backend-logs');
|
||||
expectDashboardInDocument('observability-frontend-errors');
|
||||
expectDashboardInDocument('observability-frontend-logs');
|
||||
expectDashboardInDocument('usage-data-sources');
|
||||
expectDashboardInDocument('usage-stats');
|
||||
expectDashboardInDocument('usage-usage-overview');
|
||||
expectDashboardInDocument('frontend');
|
||||
expectDashboardInDocument('overview');
|
||||
expectDashboardInDocument('stats');
|
||||
expectDashboardLength('multiple3-datasource-errors', 2);
|
||||
expectDashboardLength('multiple4-datasource-logs', 2);
|
||||
expectDashboardLength('multiple0-ingester', 2);
|
||||
expectDashboardLength('multiple1-distributor', 2);
|
||||
expectDashboardLength('multiple2-compacter', 2);
|
||||
expectDashboardInDocument('another-stats');
|
||||
|
||||
await updateScopes(['grafana']);
|
||||
await expandDashboardFolder('General');
|
||||
await expandDashboardFolder('Observability');
|
||||
await expandDashboardFolder('Usage');
|
||||
expectDashboardFolderNotInDocument('Components');
|
||||
expectDashboardFolderNotInDocument('Investigations');
|
||||
expectDashboardInDocument('general-data-sources');
|
||||
expectDashboardInDocument('general-usage');
|
||||
expectDashboardInDocument('observability-backend-errors');
|
||||
expectDashboardInDocument('observability-backend-logs');
|
||||
expectDashboardInDocument('observability-frontend-errors');
|
||||
expectDashboardInDocument('observability-frontend-logs');
|
||||
expectDashboardInDocument('usage-data-sources');
|
||||
expectDashboardInDocument('usage-stats');
|
||||
expectDashboardInDocument('usage-usage-overview');
|
||||
expectDashboardInDocument('frontend');
|
||||
expectDashboardInDocument('overview');
|
||||
expectDashboardInDocument('stats');
|
||||
expectDashboardFolderNotInDocument('multiple3-datasource-errors');
|
||||
expectDashboardFolderNotInDocument('multiple4-datasource-logs');
|
||||
expectDashboardFolderNotInDocument('multiple0-ingester');
|
||||
expectDashboardFolderNotInDocument('multiple1-distributor');
|
||||
expectDashboardFolderNotInDocument('multiple2-compacter');
|
||||
expectDashboardFolderNotInDocument('another-stats');
|
||||
});
|
||||
|
||||
it('Filters the dashboards list for dashboards', async () => {
|
||||
await toggleDashboards();
|
||||
await updateScopes(['grafana']);
|
||||
await expandDashboardFolder('General');
|
||||
await expandDashboardFolder('Observability');
|
||||
await expandDashboardFolder('Usage');
|
||||
expectDashboardInDocument('general-data-sources');
|
||||
expectDashboardInDocument('general-usage');
|
||||
expectDashboardInDocument('observability-backend-errors');
|
||||
expectDashboardInDocument('observability-backend-logs');
|
||||
expectDashboardInDocument('observability-frontend-errors');
|
||||
expectDashboardInDocument('observability-frontend-logs');
|
||||
expectDashboardInDocument('usage-data-sources');
|
||||
expectDashboardInDocument('usage-stats');
|
||||
expectDashboardInDocument('usage-usage-overview');
|
||||
expectDashboardInDocument('frontend');
|
||||
expectDashboardInDocument('overview');
|
||||
expectDashboardInDocument('stats');
|
||||
|
||||
await searchDashboards('Stats');
|
||||
expectDashboardFolderNotInDocument('general-data-sources');
|
||||
expectDashboardFolderNotInDocument('general-usage');
|
||||
expectDashboardFolderNotInDocument('observability-backend-errors');
|
||||
expectDashboardFolderNotInDocument('observability-backend-logs');
|
||||
expectDashboardFolderNotInDocument('observability-frontend-errors');
|
||||
expectDashboardFolderNotInDocument('observability-frontend-logs');
|
||||
expectDashboardFolderNotInDocument('usage-data-sources');
|
||||
expectDashboardInDocument('usage-stats');
|
||||
expectDashboardFolderNotInDocument('usage-usage-overview');
|
||||
expectDashboardFolderNotInDocument('frontend');
|
||||
expectDashboardFolderNotInDocument('overview');
|
||||
expectDashboardInDocument('stats');
|
||||
});
|
||||
|
||||
it('Filters the dashboards list for folders', async () => {
|
||||
await toggleDashboards();
|
||||
await updateScopes(['grafana']);
|
||||
await expandDashboardFolder('General');
|
||||
await expandDashboardFolder('Observability');
|
||||
await expandDashboardFolder('Usage');
|
||||
expectDashboardInDocument('general-data-sources');
|
||||
expectDashboardInDocument('general-usage');
|
||||
expectDashboardInDocument('observability-backend-errors');
|
||||
expectDashboardInDocument('observability-backend-logs');
|
||||
expectDashboardInDocument('observability-frontend-errors');
|
||||
expectDashboardInDocument('observability-frontend-logs');
|
||||
expectDashboardInDocument('usage-data-sources');
|
||||
expectDashboardInDocument('usage-stats');
|
||||
expectDashboardInDocument('usage-usage-overview');
|
||||
expectDashboardInDocument('frontend');
|
||||
expectDashboardInDocument('overview');
|
||||
expectDashboardInDocument('stats');
|
||||
|
||||
await searchDashboards('Usage');
|
||||
expectDashboardFolderNotInDocument('general-data-sources');
|
||||
expectDashboardInDocument('general-usage');
|
||||
expectDashboardFolderNotInDocument('observability-backend-errors');
|
||||
expectDashboardFolderNotInDocument('observability-backend-logs');
|
||||
expectDashboardFolderNotInDocument('observability-frontend-errors');
|
||||
expectDashboardFolderNotInDocument('observability-frontend-logs');
|
||||
expectDashboardInDocument('usage-data-sources');
|
||||
expectDashboardInDocument('usage-stats');
|
||||
expectDashboardInDocument('usage-usage-overview');
|
||||
expectDashboardFolderNotInDocument('frontend');
|
||||
expectDashboardFolderNotInDocument('overview');
|
||||
expectDashboardFolderNotInDocument('stats');
|
||||
});
|
||||
|
||||
it('Deduplicates the dashboards list', async () => {
|
||||
await toggleDashboards();
|
||||
await updateScopes(['dev', 'ops']);
|
||||
await expandDashboardFolder('Cardinality Management');
|
||||
await expandDashboardFolder('Usage Insights');
|
||||
expectDashboardLength('cardinality-management-labels', 1);
|
||||
expectDashboardLength('cardinality-management-metrics', 1);
|
||||
expectDashboardLength('cardinality-management-overview', 1);
|
||||
expectDashboardLength('usage-insights-alertmanager', 1);
|
||||
expectDashboardLength('usage-insights-data-sources', 1);
|
||||
expectDashboardLength('usage-insights-metrics-ingestion', 1);
|
||||
expectDashboardLength('usage-insights-overview', 1);
|
||||
expectDashboardLength('usage-insights-query-errors', 1);
|
||||
expectDashboardLength('billing-usage', 1);
|
||||
});
|
||||
|
||||
it('Shows a proper message when no scopes are selected', async () => {
|
||||
await toggleDashboards();
|
||||
expectNoDashboardsNoScopes();
|
||||
expectNoDashboardsSearch();
|
||||
});
|
||||
|
||||
it('Does not show the input when there are no dashboards found for scope', async () => {
|
||||
await toggleDashboards();
|
||||
await updateScopes(['cloud']);
|
||||
expectNoDashboardsForScope();
|
||||
expectNoDashboardsSearch();
|
||||
});
|
||||
|
||||
it('Shows the input and a message when there are no dashboards found for filter', async () => {
|
||||
await toggleDashboards();
|
||||
await updateScopes(['mimir']);
|
||||
await searchDashboards('unknown');
|
||||
expectDashboardsSearch();
|
||||
expectNoDashboardsForFilter();
|
||||
|
||||
await clearNotFound();
|
||||
expectDashboardSearchValue('');
|
||||
});
|
||||
});
|
27
public/app/features/scopes/tests/featureFlag.test.ts
Normal file
27
public/app/features/scopes/tests/featureFlag.test.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { scopesSelectorScene } from '../instance';
|
||||
|
||||
import { getDatasource, getInstanceSettings, getMock } from './utils/mocks';
|
||||
import { renderDashboard } from './utils/render';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
useChromeHeaderHeight: jest.fn(),
|
||||
getBackendSrv: () => ({ get: getMock }),
|
||||
getDataSourceSrv: () => ({ get: getDatasource, getInstanceSettings }),
|
||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||
}));
|
||||
|
||||
describe('Feature flag off', () => {
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = false;
|
||||
config.featureToggles.groupByVariable = true;
|
||||
});
|
||||
|
||||
it('Does not initialize', () => {
|
||||
renderDashboard();
|
||||
expect(scopesSelectorScene).toBeNull();
|
||||
});
|
||||
});
|
@ -1,687 +0,0 @@
|
||||
import { act, cleanup, waitFor } from '@testing-library/react';
|
||||
import userEvents from '@testing-library/user-event';
|
||||
|
||||
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
|
||||
import { config, setPluginImportUtils } from '@grafana/runtime';
|
||||
import { sceneGraph } from '@grafana/scenes';
|
||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||
|
||||
import { initializeScopes, scopesDashboardsScene, scopesSelectorScene } from '../instance';
|
||||
import { getClosestScopesFacade } from '../utils';
|
||||
|
||||
import {
|
||||
fetchDashboardsSpy,
|
||||
fetchNodesSpy,
|
||||
fetchScopeSpy,
|
||||
fetchSelectedScopesSpy,
|
||||
getMock,
|
||||
mocksScopes,
|
||||
} from './utils/mocks';
|
||||
import { buildTestScene, renderDashboard, resetScenes } from './utils/render';
|
||||
import {
|
||||
getDashboard,
|
||||
getDashboardFolderExpand,
|
||||
getDashboardsExpand,
|
||||
getDashboardsSearch,
|
||||
getNotFoundForFilter,
|
||||
getNotFoundForFilterClear,
|
||||
getNotFoundForScope,
|
||||
getNotFoundNoScopes,
|
||||
getPersistedApplicationsMimirSelect,
|
||||
getPersistedApplicationsMimirTitle,
|
||||
getResultApplicationsCloudDevSelect,
|
||||
getResultApplicationsCloudExpand,
|
||||
getResultApplicationsCloudOpsSelect,
|
||||
getResultApplicationsCloudSelect,
|
||||
getResultApplicationsExpand,
|
||||
getResultApplicationsGrafanaSelect,
|
||||
getResultApplicationsGrafanaTitle,
|
||||
getResultApplicationsMimirSelect,
|
||||
getResultApplicationsMimirTitle,
|
||||
getResultCloudDevRadio,
|
||||
getResultCloudExpand,
|
||||
getResultCloudOpsRadio,
|
||||
getResultCloudSelect,
|
||||
getSelectorApply,
|
||||
getSelectorCancel,
|
||||
getSelectorInput,
|
||||
getTreeHeadline,
|
||||
getTreeSearch,
|
||||
queryAllDashboard,
|
||||
queryDashboard,
|
||||
queryDashboardFolderExpand,
|
||||
queryDashboardsContainer,
|
||||
queryDashboardsSearch,
|
||||
queryPersistedApplicationsGrafanaTitle,
|
||||
queryPersistedApplicationsMimirTitle,
|
||||
queryResultApplicationsCloudTitle,
|
||||
queryResultApplicationsGrafanaTitle,
|
||||
queryResultApplicationsMimirTitle,
|
||||
querySelectorApply,
|
||||
} from './utils/selectors';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
useChromeHeaderHeight: jest.fn(),
|
||||
getBackendSrv: () => ({
|
||||
get: getMock,
|
||||
}),
|
||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||
}));
|
||||
|
||||
const panelPlugin = getPanelPlugin({
|
||||
id: 'table',
|
||||
skipDataQuery: true,
|
||||
});
|
||||
|
||||
config.panels['table'] = panelPlugin.meta;
|
||||
|
||||
setPluginImportUtils({
|
||||
importPanelPlugin: () => Promise.resolve(panelPlugin),
|
||||
getPanelPluginFromCache: () => undefined,
|
||||
});
|
||||
|
||||
describe('Scopes', () => {
|
||||
describe('Feature flag off', () => {
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = false;
|
||||
config.featureToggles.groupByVariable = true;
|
||||
|
||||
initializeScopes();
|
||||
});
|
||||
|
||||
it('Does not initialize', () => {
|
||||
const dashboardScene = buildTestScene();
|
||||
dashboardScene.activate();
|
||||
expect(scopesSelectorScene).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Feature flag on', () => {
|
||||
let dashboardScene: DashboardScene;
|
||||
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.groupByVariable = true;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(console, 'error').mockImplementation(jest.fn());
|
||||
|
||||
fetchNodesSpy.mockClear();
|
||||
fetchScopeSpy.mockClear();
|
||||
fetchSelectedScopesSpy.mockClear();
|
||||
fetchDashboardsSpy.mockClear();
|
||||
getMock.mockClear();
|
||||
|
||||
initializeScopes();
|
||||
|
||||
dashboardScene = buildTestScene();
|
||||
|
||||
renderDashboard(dashboardScene);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetScenes();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe('Tree', () => {
|
||||
it('Navigates through scopes nodes', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsCloudExpand());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
});
|
||||
|
||||
it('Fetches scope details on select', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await waitFor(() => expect(fetchScopeSpy).toHaveBeenCalledTimes(1));
|
||||
});
|
||||
|
||||
it('Selects the proper scopes', async () => {
|
||||
await act(async () =>
|
||||
scopesSelectorScene?.updateScopes([
|
||||
{ scopeName: 'grafana', path: [] },
|
||||
{ scopeName: 'mimir', path: [] },
|
||||
])
|
||||
);
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
expect(getResultApplicationsGrafanaSelect()).toBeChecked();
|
||||
expect(getResultApplicationsMimirSelect()).toBeChecked();
|
||||
});
|
||||
|
||||
it('Can select scopes from same level', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getResultApplicationsCloudSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
expect(getSelectorInput().value).toBe('Grafana, Mimir, Cloud');
|
||||
});
|
||||
|
||||
it('Can select a node from an inner level', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getResultApplicationsCloudExpand());
|
||||
await userEvents.click(getResultApplicationsCloudDevSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
expect(getSelectorInput().value).toBe('Dev');
|
||||
});
|
||||
|
||||
it('Can select a node from an upper level', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultCloudSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
expect(getSelectorInput().value).toBe('Cloud');
|
||||
});
|
||||
|
||||
it('Respects only one select per container', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultCloudExpand());
|
||||
await userEvents.click(getResultCloudDevRadio());
|
||||
expect(getResultCloudDevRadio().checked).toBe(true);
|
||||
expect(getResultCloudOpsRadio().checked).toBe(false);
|
||||
await userEvents.click(getResultCloudOpsRadio());
|
||||
expect(getResultCloudDevRadio().checked).toBe(false);
|
||||
expect(getResultCloudOpsRadio().checked).toBe(true);
|
||||
});
|
||||
|
||||
it('Search works', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.type(getTreeSearch(), 'Cloud');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
expect(queryResultApplicationsGrafanaTitle()).not.toBeInTheDocument();
|
||||
expect(queryResultApplicationsMimirTitle()).not.toBeInTheDocument();
|
||||
expect(getResultApplicationsCloudSelect()).toBeInTheDocument();
|
||||
await userEvents.clear(getTreeSearch());
|
||||
await userEvents.type(getTreeSearch(), 'Grafana');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(4));
|
||||
expect(getResultApplicationsGrafanaSelect()).toBeInTheDocument();
|
||||
expect(queryResultApplicationsCloudTitle()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Opens to a selected scope', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultCloudExpand());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.click(getSelectorInput());
|
||||
expect(queryResultApplicationsMimirTitle()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Persists a scope', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.type(getTreeSearch(), 'grafana');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
expect(getPersistedApplicationsMimirTitle()).toBeInTheDocument();
|
||||
expect(queryPersistedApplicationsGrafanaTitle()).not.toBeInTheDocument();
|
||||
expect(queryResultApplicationsMimirTitle()).not.toBeInTheDocument();
|
||||
expect(getResultApplicationsGrafanaTitle()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not persist a retrieved scope', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.type(getTreeSearch(), 'mimir');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
expect(queryPersistedApplicationsMimirTitle()).not.toBeInTheDocument();
|
||||
expect(getResultApplicationsMimirTitle()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Removes persisted nodes', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.type(getTreeSearch(), 'grafana');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
await userEvents.clear(getTreeSearch());
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(4));
|
||||
expect(queryPersistedApplicationsMimirTitle()).not.toBeInTheDocument();
|
||||
expect(queryPersistedApplicationsGrafanaTitle()).not.toBeInTheDocument();
|
||||
expect(getResultApplicationsMimirTitle()).toBeInTheDocument();
|
||||
expect(getResultApplicationsGrafanaTitle()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Persists nodes from search', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.type(getTreeSearch(), 'mimir');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.type(getTreeSearch(), 'unknown');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(4));
|
||||
expect(getPersistedApplicationsMimirTitle()).toBeInTheDocument();
|
||||
await userEvents.clear(getTreeSearch());
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(5));
|
||||
expect(getResultApplicationsMimirTitle()).toBeInTheDocument();
|
||||
expect(getResultApplicationsGrafanaTitle()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Selects a persisted scope', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.type(getTreeSearch(), 'grafana');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
expect(getSelectorInput().value).toBe('Mimir, Grafana');
|
||||
});
|
||||
|
||||
it('Deselects a persisted scope', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.type(getTreeSearch(), 'grafana');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
expect(getSelectorInput().value).toBe('Mimir, Grafana');
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getPersistedApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
expect(getSelectorInput().value).toBe('Grafana');
|
||||
});
|
||||
|
||||
it('Shows the proper headline', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
expect(getTreeHeadline()).toHaveTextContent('Recommended');
|
||||
await userEvents.type(getTreeSearch(), 'Applications');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(2));
|
||||
expect(getTreeHeadline()).toHaveTextContent('Results');
|
||||
await userEvents.type(getTreeSearch(), 'unknown');
|
||||
await waitFor(() => expect(fetchNodesSpy).toHaveBeenCalledTimes(3));
|
||||
expect(getTreeHeadline()).toHaveTextContent('No results found for your query');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Selector', () => {
|
||||
it('Opens', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
expect(getSelectorApply()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Fetches scope details on save', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultCloudSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => expect(fetchSelectedScopesSpy).toHaveBeenCalled());
|
||||
expect(getClosestScopesFacade(dashboardScene)?.value).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'cloud')
|
||||
);
|
||||
});
|
||||
|
||||
it('Does not save the scopes on close', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultCloudSelect());
|
||||
await userEvents.click(getSelectorCancel());
|
||||
await waitFor(() => expect(fetchSelectedScopesSpy).not.toHaveBeenCalled());
|
||||
expect(getClosestScopesFacade(dashboardScene)?.value).toEqual([]);
|
||||
});
|
||||
|
||||
it('Shows selected scopes', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultCloudSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
expect(getSelectorInput().value).toEqual('Cloud');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dashboards list', () => {
|
||||
it('Toggles expanded state', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
expect(getNotFoundNoScopes()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not fetch dashboards list when the list is not expanded', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => expect(fetchDashboardsSpy).not.toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('Fetches dashboards list when the list is expanded', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => expect(fetchDashboardsSpy).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('Fetches dashboards list when the list is expanded after scope selection', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await waitFor(() => expect(fetchDashboardsSpy).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('Shows dashboards for multiple scopes', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.click(getDashboardFolderExpand('General'));
|
||||
await userEvents.click(getDashboardFolderExpand('Observability'));
|
||||
await userEvents.click(getDashboardFolderExpand('Usage'));
|
||||
expect(queryDashboardFolderExpand('Components')).not.toBeInTheDocument();
|
||||
expect(queryDashboardFolderExpand('Investigations')).not.toBeInTheDocument();
|
||||
expect(getDashboard('general-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('general-usage')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-stats')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-usage-overview')).toBeInTheDocument();
|
||||
expect(getDashboard('frontend')).toBeInTheDocument();
|
||||
expect(getDashboard('overview')).toBeInTheDocument();
|
||||
expect(getDashboard('stats')).toBeInTheDocument();
|
||||
expect(queryDashboard('multiple3-datasource-errors')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('multiple4-datasource-logs')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('multiple0-ingester')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('multiple1-distributor')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('multiple2-compacter')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('another-stats')).not.toBeInTheDocument();
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.click(getDashboardFolderExpand('General'));
|
||||
await userEvents.click(getDashboardFolderExpand('Observability'));
|
||||
await userEvents.click(getDashboardFolderExpand('Usage'));
|
||||
await userEvents.click(getDashboardFolderExpand('Components'));
|
||||
await userEvents.click(getDashboardFolderExpand('Investigations'));
|
||||
expect(getDashboard('general-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('general-usage')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-stats')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-usage-overview')).toBeInTheDocument();
|
||||
expect(getDashboard('frontend')).toBeInTheDocument();
|
||||
expect(getDashboard('overview')).toBeInTheDocument();
|
||||
expect(getDashboard('stats')).toBeInTheDocument();
|
||||
expect(queryAllDashboard('multiple3-datasource-errors')).toHaveLength(2);
|
||||
expect(queryAllDashboard('multiple4-datasource-logs')).toHaveLength(2);
|
||||
expect(queryAllDashboard('multiple0-ingester')).toHaveLength(2);
|
||||
expect(queryAllDashboard('multiple1-distributor')).toHaveLength(2);
|
||||
expect(queryAllDashboard('multiple2-compacter')).toHaveLength(2);
|
||||
expect(getDashboard('another-stats')).toBeInTheDocument();
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.click(getDashboardFolderExpand('General'));
|
||||
await userEvents.click(getDashboardFolderExpand('Observability'));
|
||||
await userEvents.click(getDashboardFolderExpand('Usage'));
|
||||
expect(queryDashboardFolderExpand('Components')).not.toBeInTheDocument();
|
||||
expect(queryDashboardFolderExpand('Investigations')).not.toBeInTheDocument();
|
||||
expect(getDashboard('general-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('general-usage')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-stats')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-usage-overview')).toBeInTheDocument();
|
||||
expect(getDashboard('frontend')).toBeInTheDocument();
|
||||
expect(getDashboard('overview')).toBeInTheDocument();
|
||||
expect(getDashboard('stats')).toBeInTheDocument();
|
||||
expect(queryDashboard('multiple3-datasource-errors')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('multiple4-datasource-logs')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('multiple0-ingester')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('multiple1-distributor')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('multiple2-compacter')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('another-stats')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Filters the dashboards list for dashboards', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.click(getDashboardFolderExpand('General'));
|
||||
await userEvents.click(getDashboardFolderExpand('Observability'));
|
||||
await userEvents.click(getDashboardFolderExpand('Usage'));
|
||||
expect(getDashboard('general-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('general-usage')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-stats')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-usage-overview')).toBeInTheDocument();
|
||||
expect(getDashboard('frontend')).toBeInTheDocument();
|
||||
expect(getDashboard('overview')).toBeInTheDocument();
|
||||
expect(getDashboard('stats')).toBeInTheDocument();
|
||||
await userEvents.type(getDashboardsSearch(), 'Stats');
|
||||
await waitFor(() => {
|
||||
expect(queryDashboard('general-data-sources')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('general-usage')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('observability-backend-errors')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('observability-backend-logs')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('observability-frontend-errors')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('observability-frontend-logs')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('usage-data-sources')).not.toBeInTheDocument();
|
||||
expect(getDashboard('usage-stats')).toBeInTheDocument();
|
||||
expect(queryDashboard('usage-usage-overview')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('frontend')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('overview')).not.toBeInTheDocument();
|
||||
expect(getDashboard('stats')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('Filters the dashboards list for folders', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.click(getDashboardFolderExpand('General'));
|
||||
await userEvents.click(getDashboardFolderExpand('Observability'));
|
||||
await userEvents.click(getDashboardFolderExpand('Usage'));
|
||||
expect(getDashboard('general-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('general-usage')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-backend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-errors')).toBeInTheDocument();
|
||||
expect(getDashboard('observability-frontend-logs')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-stats')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-usage-overview')).toBeInTheDocument();
|
||||
expect(getDashboard('frontend')).toBeInTheDocument();
|
||||
expect(getDashboard('overview')).toBeInTheDocument();
|
||||
expect(getDashboard('stats')).toBeInTheDocument();
|
||||
await userEvents.type(getDashboardsSearch(), 'Usage');
|
||||
await waitFor(() => {
|
||||
expect(queryDashboard('general-data-sources')).not.toBeInTheDocument();
|
||||
expect(getDashboard('general-usage')).toBeInTheDocument();
|
||||
expect(queryDashboard('observability-backend-errors')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('observability-backend-logs')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('observability-frontend-errors')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('observability-frontend-logs')).not.toBeInTheDocument();
|
||||
expect(getDashboard('usage-data-sources')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-stats')).toBeInTheDocument();
|
||||
expect(getDashboard('usage-usage-overview')).toBeInTheDocument();
|
||||
expect(queryDashboard('frontend')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('overview')).not.toBeInTheDocument();
|
||||
expect(queryDashboard('stats')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('Deduplicates the dashboards list', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsCloudExpand());
|
||||
await userEvents.click(getResultApplicationsCloudDevSelect());
|
||||
await userEvents.click(getResultApplicationsCloudOpsSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.click(getDashboardFolderExpand('Cardinality Management'));
|
||||
await userEvents.click(getDashboardFolderExpand('Usage Insights'));
|
||||
expect(queryAllDashboard('cardinality-management-labels')).toHaveLength(1);
|
||||
expect(queryAllDashboard('cardinality-management-metrics')).toHaveLength(1);
|
||||
expect(queryAllDashboard('cardinality-management-overview')).toHaveLength(1);
|
||||
expect(queryAllDashboard('usage-insights-alertmanager')).toHaveLength(1);
|
||||
expect(queryAllDashboard('usage-insights-data-sources')).toHaveLength(1);
|
||||
expect(queryAllDashboard('usage-insights-metrics-ingestion')).toHaveLength(1);
|
||||
expect(queryAllDashboard('usage-insights-overview')).toHaveLength(1);
|
||||
expect(queryAllDashboard('usage-insights-query-errors')).toHaveLength(1);
|
||||
expect(queryAllDashboard('billing-usage')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('Shows a proper message when no scopes are selected', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
expect(getNotFoundNoScopes()).toBeInTheDocument();
|
||||
expect(queryDashboardsSearch()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not show the input when there are no dashboards found for scope', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultCloudSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
expect(getNotFoundForScope()).toBeInTheDocument();
|
||||
expect(queryDashboardsSearch()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Shows the input and a message when there are no dashboards found for filter', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await userEvents.type(getDashboardsSearch(), 'unknown');
|
||||
await waitFor(() => {
|
||||
expect(queryDashboardsSearch()).toBeInTheDocument();
|
||||
expect(getNotFoundForFilter()).toBeInTheDocument();
|
||||
});
|
||||
await userEvents.click(getNotFoundForFilterClear());
|
||||
await waitFor(() => {
|
||||
expect(getDashboardsSearch().value).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('View mode', () => {
|
||||
it('Enters view mode', async () => {
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
expect(scopesSelectorScene?.state?.isReadOnly).toEqual(true);
|
||||
expect(scopesDashboardsScene?.state?.isPanelOpened).toEqual(false);
|
||||
});
|
||||
|
||||
it('Closes selector on enter', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
expect(querySelectorApply()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Closes dashboards list on enter', async () => {
|
||||
await userEvents.click(getDashboardsExpand());
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
expect(queryDashboardsContainer()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not open selector when view mode is active', async () => {
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
await userEvents.click(getSelectorInput());
|
||||
expect(querySelectorApply()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Disables the expand button when view mode is active', async () => {
|
||||
await act(async () => dashboardScene.onEnterEditMode());
|
||||
expect(getDashboardsExpand()).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Enrichers', () => {
|
||||
it('Data requests', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => {
|
||||
const queryRunner = sceneGraph.findObject(dashboardScene, (o) => o.state.key === 'data-query-runner')!;
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'grafana')
|
||||
);
|
||||
});
|
||||
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => {
|
||||
const queryRunner = sceneGraph.findObject(dashboardScene, (o) => o.state.key === 'data-query-runner')!;
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'grafana' || name === 'mimir')
|
||||
);
|
||||
});
|
||||
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => {
|
||||
const queryRunner = sceneGraph.findObject(dashboardScene, (o) => o.state.key === 'data-query-runner')!;
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'mimir')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Filters requests', async () => {
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsExpand());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => {
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'grafana')
|
||||
);
|
||||
});
|
||||
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsMimirSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => {
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'grafana' || name === 'mimir')
|
||||
);
|
||||
});
|
||||
|
||||
await userEvents.click(getSelectorInput());
|
||||
await userEvents.click(getResultApplicationsGrafanaSelect());
|
||||
await userEvents.click(getSelectorApply());
|
||||
await waitFor(() => {
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'mimir')
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
83
public/app/features/scopes/tests/selector.test.ts
Normal file
83
public/app/features/scopes/tests/selector.test.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
import { sceneGraph } from '@grafana/scenes';
|
||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||
|
||||
import { getClosestScopesFacade } from '../utils';
|
||||
|
||||
import { applyScopes, cancelScopes, openSelector, selectResultCloud, updateScopes } from './utils/actions';
|
||||
import { expectNotDashboardReload, expectScopesSelectorValue } from './utils/assertions';
|
||||
import { fetchSelectedScopesSpy, getDatasource, getInstanceSettings, getMock, mocksScopes } from './utils/mocks';
|
||||
import { renderDashboard, resetScenes } from './utils/render';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
useChromeHeaderHeight: jest.fn(),
|
||||
getBackendSrv: () => ({ get: getMock }),
|
||||
getDataSourceSrv: () => ({ get: getDatasource, getInstanceSettings }),
|
||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||
}));
|
||||
|
||||
describe('Selector', () => {
|
||||
let dashboardScene: DashboardScene;
|
||||
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.groupByVariable = true;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
dashboardScene = renderDashboard();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await resetScenes();
|
||||
});
|
||||
|
||||
it('Fetches scope details on save', async () => {
|
||||
await openSelector();
|
||||
await selectResultCloud();
|
||||
await applyScopes();
|
||||
expect(fetchSelectedScopesSpy).toHaveBeenCalled();
|
||||
expect(getClosestScopesFacade(dashboardScene)?.value).toEqual(
|
||||
mocksScopes.filter(({ metadata: { name } }) => name === 'cloud')
|
||||
);
|
||||
});
|
||||
|
||||
it('Does not save the scopes on close', async () => {
|
||||
await openSelector();
|
||||
await selectResultCloud();
|
||||
await cancelScopes();
|
||||
expect(fetchSelectedScopesSpy).not.toHaveBeenCalled();
|
||||
expect(getClosestScopesFacade(dashboardScene)?.value).toEqual([]);
|
||||
});
|
||||
|
||||
it('Shows selected scopes', async () => {
|
||||
await updateScopes(['grafana']);
|
||||
expectScopesSelectorValue('Grafana');
|
||||
});
|
||||
|
||||
it('Does not reload the dashboard on scope change', async () => {
|
||||
await updateScopes(['grafana']);
|
||||
expectNotDashboardReload();
|
||||
});
|
||||
|
||||
it('Adds scopes to enrichers', async () => {
|
||||
const queryRunner = sceneGraph.getQueryController(dashboardScene)!;
|
||||
|
||||
await updateScopes(['grafana']);
|
||||
let scopes = mocksScopes.filter(({ metadata: { name } }) => name === 'grafana');
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(scopes);
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(scopes);
|
||||
|
||||
await updateScopes(['grafana', 'mimir']);
|
||||
scopes = mocksScopes.filter(({ metadata: { name } }) => name === 'grafana' || name === 'mimir');
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(scopes);
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(scopes);
|
||||
|
||||
await updateScopes(['mimir']);
|
||||
scopes = mocksScopes.filter(({ metadata: { name } }) => name === 'mimir');
|
||||
expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(scopes);
|
||||
expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(scopes);
|
||||
});
|
||||
});
|
247
public/app/features/scopes/tests/tree.test.ts
Normal file
247
public/app/features/scopes/tests/tree.test.ts
Normal file
@ -0,0 +1,247 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import {
|
||||
applyScopes,
|
||||
clearScopesSearch,
|
||||
expandResultApplications,
|
||||
expandResultApplicationsCloud,
|
||||
expandResultCloud,
|
||||
openSelector,
|
||||
searchScopes,
|
||||
selectPersistedApplicationsMimir,
|
||||
selectResultApplicationsCloud,
|
||||
selectResultApplicationsCloudDev,
|
||||
selectResultApplicationsGrafana,
|
||||
selectResultApplicationsMimir,
|
||||
selectResultCloud,
|
||||
selectResultCloudDev,
|
||||
selectResultCloudOps,
|
||||
updateScopes,
|
||||
} from './utils/actions';
|
||||
import {
|
||||
expectPersistedApplicationsGrafanaNotPresent,
|
||||
expectPersistedApplicationsMimirNotPresent,
|
||||
expectPersistedApplicationsMimirPresent,
|
||||
expectResultApplicationsCloudNotPresent,
|
||||
expectResultApplicationsCloudPresent,
|
||||
expectResultApplicationsGrafanaNotPresent,
|
||||
expectResultApplicationsGrafanaPresent,
|
||||
expectResultApplicationsGrafanaSelected,
|
||||
expectResultApplicationsMimirNotPresent,
|
||||
expectResultApplicationsMimirPresent,
|
||||
expectResultApplicationsMimirSelected,
|
||||
expectResultCloudDevNotSelected,
|
||||
expectResultCloudDevSelected,
|
||||
expectResultCloudOpsNotSelected,
|
||||
expectResultCloudOpsSelected,
|
||||
expectScopesHeadline,
|
||||
expectScopesSelectorValue,
|
||||
} from './utils/assertions';
|
||||
import { fetchNodesSpy, fetchScopeSpy, getDatasource, getInstanceSettings, getMock } from './utils/mocks';
|
||||
import { renderDashboard, resetScenes } from './utils/render';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
useChromeHeaderHeight: jest.fn(),
|
||||
getBackendSrv: () => ({ get: getMock }),
|
||||
getDataSourceSrv: () => ({ get: getDatasource, getInstanceSettings }),
|
||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||
}));
|
||||
|
||||
describe('Tree', () => {
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.groupByVariable = true;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
renderDashboard();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await resetScenes();
|
||||
});
|
||||
|
||||
it('Fetches scope details on select', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsGrafana();
|
||||
expect(fetchScopeSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Selects the proper scopes', async () => {
|
||||
await updateScopes(['grafana', 'mimir']);
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
expectResultApplicationsGrafanaSelected();
|
||||
expectResultApplicationsMimirSelected();
|
||||
});
|
||||
|
||||
it('Can select scopes from same level', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsGrafana();
|
||||
await selectResultApplicationsMimir();
|
||||
await selectResultApplicationsCloud();
|
||||
await applyScopes();
|
||||
expectScopesSelectorValue('Grafana, Mimir, Cloud');
|
||||
});
|
||||
|
||||
it('Can select a node from an inner level', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsGrafana();
|
||||
await expandResultApplicationsCloud();
|
||||
await selectResultApplicationsCloudDev();
|
||||
await applyScopes();
|
||||
expectScopesSelectorValue('Dev');
|
||||
});
|
||||
|
||||
it('Can select a node from an upper level', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsGrafana();
|
||||
await expandResultApplications();
|
||||
await selectResultCloud();
|
||||
await applyScopes();
|
||||
expectScopesSelectorValue('Cloud');
|
||||
});
|
||||
|
||||
it('Respects only one select per container', async () => {
|
||||
await openSelector();
|
||||
await expandResultCloud();
|
||||
await selectResultCloudDev();
|
||||
expectResultCloudDevSelected();
|
||||
expectResultCloudOpsNotSelected();
|
||||
|
||||
await selectResultCloudOps();
|
||||
expectResultCloudDevNotSelected();
|
||||
expectResultCloudOpsSelected();
|
||||
});
|
||||
|
||||
it('Search works', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await searchScopes('Cloud');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
expectResultApplicationsGrafanaNotPresent();
|
||||
expectResultApplicationsMimirNotPresent();
|
||||
expectResultApplicationsCloudPresent();
|
||||
|
||||
await clearScopesSearch();
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(4);
|
||||
|
||||
await searchScopes('Grafana');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(5);
|
||||
expectResultApplicationsGrafanaPresent();
|
||||
expectResultApplicationsCloudNotPresent();
|
||||
});
|
||||
|
||||
it('Opens to a selected scope', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsMimir();
|
||||
await expandResultApplications();
|
||||
await expandResultCloud();
|
||||
await applyScopes();
|
||||
await openSelector();
|
||||
expectResultApplicationsMimirPresent();
|
||||
});
|
||||
|
||||
it('Persists a scope', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsMimir();
|
||||
await searchScopes('grafana');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
expectPersistedApplicationsMimirPresent();
|
||||
expectPersistedApplicationsGrafanaNotPresent();
|
||||
expectResultApplicationsMimirNotPresent();
|
||||
expectResultApplicationsGrafanaPresent();
|
||||
});
|
||||
|
||||
it('Does not persist a retrieved scope', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsMimir();
|
||||
await searchScopes('mimir');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
expectPersistedApplicationsMimirNotPresent();
|
||||
expectResultApplicationsMimirPresent();
|
||||
});
|
||||
|
||||
it('Removes persisted nodes', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsMimir();
|
||||
await searchScopes('grafana');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
|
||||
await clearScopesSearch();
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(4);
|
||||
expectPersistedApplicationsMimirNotPresent();
|
||||
expectPersistedApplicationsGrafanaNotPresent();
|
||||
expectResultApplicationsMimirPresent();
|
||||
expectResultApplicationsGrafanaPresent();
|
||||
});
|
||||
|
||||
it('Persists nodes from search', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await searchScopes('mimir');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
|
||||
await selectResultApplicationsMimir();
|
||||
await searchScopes('unknown');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(4);
|
||||
expectPersistedApplicationsMimirPresent();
|
||||
|
||||
await clearScopesSearch();
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(5);
|
||||
expectResultApplicationsMimirPresent();
|
||||
expectResultApplicationsGrafanaPresent();
|
||||
});
|
||||
|
||||
it('Selects a persisted scope', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsMimir();
|
||||
await searchScopes('grafana');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
|
||||
await selectResultApplicationsGrafana();
|
||||
await applyScopes();
|
||||
expectScopesSelectorValue('Mimir, Grafana');
|
||||
});
|
||||
|
||||
it('Deselects a persisted scope', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await selectResultApplicationsMimir();
|
||||
await searchScopes('grafana');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
|
||||
await selectResultApplicationsGrafana();
|
||||
await applyScopes();
|
||||
expectScopesSelectorValue('Mimir, Grafana');
|
||||
|
||||
await openSelector();
|
||||
await selectPersistedApplicationsMimir();
|
||||
await applyScopes();
|
||||
expectScopesSelectorValue('Grafana');
|
||||
});
|
||||
|
||||
it('Shows the proper headline', async () => {
|
||||
await openSelector();
|
||||
expectScopesHeadline('Recommended');
|
||||
|
||||
await searchScopes('Applications');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(2);
|
||||
expectScopesHeadline('Results');
|
||||
|
||||
await searchScopes('unknown');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
expectScopesHeadline('No results found for your query');
|
||||
});
|
||||
});
|
@ -10,356 +10,354 @@ import {
|
||||
dashboardWithTwoFolders,
|
||||
} from './utils/mocks';
|
||||
|
||||
describe('Scopes', () => {
|
||||
describe('Utils', () => {
|
||||
describe('groupDashboards', () => {
|
||||
it('Assigns dashboards without groups to root folder', () => {
|
||||
expect(groupDashboards([dashboardWithoutFolder])).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithoutFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithoutFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithoutFolder.status.dashboardTitle,
|
||||
items: [dashboardWithoutFolder],
|
||||
},
|
||||
describe('Utils', () => {
|
||||
describe('groupDashboards', () => {
|
||||
it('Assigns dashboards without groups to root folder', () => {
|
||||
expect(groupDashboards([dashboardWithoutFolder])).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithoutFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithoutFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithoutFolder.status.dashboardTitle,
|
||||
items: [dashboardWithoutFolder],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Assigns dashboards with root group to root folder', () => {
|
||||
expect(groupDashboards([dashboardWithRootFolder])).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithRootFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithRootFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithRootFolder.status.dashboardTitle,
|
||||
items: [dashboardWithRootFolder],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Merges folders from multiple dashboards', () => {
|
||||
expect(groupDashboards([dashboardWithOneFolder, dashboardWithTwoFolders])).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithOneFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithOneFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithOneFolder.status.dashboardTitle,
|
||||
items: [dashboardWithOneFolder],
|
||||
},
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Merges scopes from multiple dashboards', () => {
|
||||
expect(groupDashboards([dashboardWithTwoFolders, alternativeDashboardWithTwoFolders])).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Matches snapshot', () => {
|
||||
expect(
|
||||
groupDashboards([
|
||||
dashboardWithoutFolder,
|
||||
dashboardWithOneFolder,
|
||||
dashboardWithTwoFolders,
|
||||
alternativeDashboardWithTwoFolders,
|
||||
dashboardWithRootFolder,
|
||||
alternativeDashboardWithRootFolder,
|
||||
dashboardWithRootFolderAndOtherFolder,
|
||||
])
|
||||
).toEqual({
|
||||
'': {
|
||||
dashboards: {
|
||||
[dashboardWithRootFolderAndOtherFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithRootFolderAndOtherFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithRootFolderAndOtherFolder.status.dashboardTitle,
|
||||
items: [dashboardWithRootFolderAndOtherFolder],
|
||||
},
|
||||
[dashboardWithRootFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithRootFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithRootFolder.status.dashboardTitle,
|
||||
items: [dashboardWithRootFolder, alternativeDashboardWithRootFolder],
|
||||
},
|
||||
[dashboardWithoutFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithoutFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithoutFolder.status.dashboardTitle,
|
||||
items: [dashboardWithoutFolder],
|
||||
},
|
||||
},
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
dashboards: {
|
||||
[dashboardWithOneFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithOneFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithOneFolder.status.dashboardTitle,
|
||||
items: [dashboardWithOneFolder],
|
||||
},
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
folders: {},
|
||||
isExpanded: false,
|
||||
title: 'Folder 1',
|
||||
},
|
||||
'Folder 2': {
|
||||
dashboards: {
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
folders: {},
|
||||
isExpanded: false,
|
||||
title: 'Folder 2',
|
||||
},
|
||||
'Folder 3': {
|
||||
dashboards: {
|
||||
[dashboardWithRootFolderAndOtherFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithRootFolderAndOtherFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithRootFolderAndOtherFolder.status.dashboardTitle,
|
||||
items: [dashboardWithRootFolderAndOtherFolder],
|
||||
},
|
||||
},
|
||||
folders: {},
|
||||
isExpanded: false,
|
||||
title: 'Folder 3',
|
||||
},
|
||||
},
|
||||
isExpanded: true,
|
||||
title: '',
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterFolders', () => {
|
||||
it('Shows folders matching criteria', () => {
|
||||
expect(
|
||||
filterFolders(
|
||||
{
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
it('Assigns dashboards with root group to root folder', () => {
|
||||
expect(groupDashboards([dashboardWithRootFolder])).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithRootFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithRootFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithRootFolder.status.dashboardTitle,
|
||||
items: [dashboardWithRootFolder],
|
||||
},
|
||||
'Folder'
|
||||
)
|
||||
).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Shows dashboards matching criteria', () => {
|
||||
expect(
|
||||
filterFolders(
|
||||
{
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Random ID': {
|
||||
dashboard: 'Random ID',
|
||||
dashboardTitle: 'Random Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
it('Merges folders from multiple dashboards', () => {
|
||||
expect(groupDashboards([dashboardWithOneFolder, dashboardWithTwoFolders])).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithOneFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithOneFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithOneFolder.status.dashboardTitle,
|
||||
items: [dashboardWithOneFolder],
|
||||
},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
'Random ID': {
|
||||
dashboard: 'Random ID',
|
||||
dashboardTitle: 'Random Title',
|
||||
items: [],
|
||||
},
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
},
|
||||
'dash'
|
||||
)
|
||||
).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
dashboards: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Merges scopes from multiple dashboards', () => {
|
||||
expect(groupDashboards([dashboardWithTwoFolders, alternativeDashboardWithTwoFolders])).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Matches snapshot', () => {
|
||||
expect(
|
||||
groupDashboards([
|
||||
dashboardWithoutFolder,
|
||||
dashboardWithOneFolder,
|
||||
dashboardWithTwoFolders,
|
||||
alternativeDashboardWithTwoFolders,
|
||||
dashboardWithRootFolder,
|
||||
alternativeDashboardWithRootFolder,
|
||||
dashboardWithRootFolderAndOtherFolder,
|
||||
])
|
||||
).toEqual({
|
||||
'': {
|
||||
dashboards: {
|
||||
[dashboardWithRootFolderAndOtherFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithRootFolderAndOtherFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithRootFolderAndOtherFolder.status.dashboardTitle,
|
||||
items: [dashboardWithRootFolderAndOtherFolder],
|
||||
},
|
||||
[dashboardWithRootFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithRootFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithRootFolder.status.dashboardTitle,
|
||||
items: [dashboardWithRootFolder, alternativeDashboardWithRootFolder],
|
||||
},
|
||||
[dashboardWithoutFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithoutFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithoutFolder.status.dashboardTitle,
|
||||
items: [dashboardWithoutFolder],
|
||||
},
|
||||
},
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
dashboards: {
|
||||
[dashboardWithOneFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithOneFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithOneFolder.status.dashboardTitle,
|
||||
items: [dashboardWithOneFolder],
|
||||
},
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
folders: {},
|
||||
isExpanded: false,
|
||||
title: 'Folder 1',
|
||||
},
|
||||
'Folder 2': {
|
||||
dashboards: {
|
||||
[dashboardWithTwoFolders.spec.dashboard]: {
|
||||
dashboard: dashboardWithTwoFolders.spec.dashboard,
|
||||
dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle,
|
||||
items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders],
|
||||
},
|
||||
},
|
||||
folders: {},
|
||||
isExpanded: false,
|
||||
title: 'Folder 2',
|
||||
},
|
||||
'Folder 3': {
|
||||
dashboards: {
|
||||
[dashboardWithRootFolderAndOtherFolder.spec.dashboard]: {
|
||||
dashboard: dashboardWithRootFolderAndOtherFolder.spec.dashboard,
|
||||
dashboardTitle: dashboardWithRootFolderAndOtherFolder.status.dashboardTitle,
|
||||
items: [dashboardWithRootFolderAndOtherFolder],
|
||||
},
|
||||
},
|
||||
folders: {},
|
||||
isExpanded: false,
|
||||
title: 'Folder 3',
|
||||
},
|
||||
},
|
||||
isExpanded: true,
|
||||
title: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterFolders', () => {
|
||||
it('Shows folders matching criteria', () => {
|
||||
expect(
|
||||
filterFolders(
|
||||
{
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder'
|
||||
)
|
||||
).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Shows dashboards matching criteria', () => {
|
||||
expect(
|
||||
filterFolders(
|
||||
{
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: false,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
'Folder 2': {
|
||||
title: 'Folder 2',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Random ID': {
|
||||
dashboard: 'Random ID',
|
||||
dashboardTitle: 'Random Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
'Random ID': {
|
||||
dashboard: 'Random ID',
|
||||
dashboardTitle: 'Random Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'dash'
|
||||
)
|
||||
).toEqual({
|
||||
'': {
|
||||
title: '',
|
||||
isExpanded: true,
|
||||
folders: {
|
||||
'Folder 1': {
|
||||
title: 'Folder 1',
|
||||
isExpanded: true,
|
||||
folders: {},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboards: {
|
||||
'Dashboard ID': {
|
||||
dashboard: 'Dashboard ID',
|
||||
dashboardTitle: 'Dashboard Title',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
93
public/app/features/scopes/tests/utils/actions.ts
Normal file
93
public/app/features/scopes/tests/utils/actions.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { act, fireEvent } from '@testing-library/react';
|
||||
|
||||
import { getDashboardAPI, setDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
|
||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||
|
||||
import { scopesSelectorScene } from '../../instance';
|
||||
|
||||
import {
|
||||
fetchDashboardsSpy,
|
||||
fetchNodesSpy,
|
||||
fetchScopeSpy,
|
||||
fetchSelectedScopesSpy,
|
||||
getMock,
|
||||
locationReloadSpy,
|
||||
} from './mocks';
|
||||
import {
|
||||
getDashboardFolderExpand,
|
||||
getDashboardsExpand,
|
||||
getDashboardsSearch,
|
||||
getNotFoundForFilterClear,
|
||||
getPersistedApplicationsMimirSelect,
|
||||
getResultApplicationsCloudDevSelect,
|
||||
getResultApplicationsCloudExpand,
|
||||
getResultApplicationsCloudSelect,
|
||||
getResultApplicationsExpand,
|
||||
getResultApplicationsGrafanaSelect,
|
||||
getResultApplicationsMimirSelect,
|
||||
getResultCloudDevRadio,
|
||||
getResultCloudExpand,
|
||||
getResultCloudOpsRadio,
|
||||
getResultCloudSelect,
|
||||
getSelectorApply,
|
||||
getSelectorCancel,
|
||||
getSelectorInput,
|
||||
getTreeSearch,
|
||||
} from './selectors';
|
||||
|
||||
export const clearMocks = () => {
|
||||
fetchNodesSpy.mockClear();
|
||||
fetchScopeSpy.mockClear();
|
||||
fetchSelectedScopesSpy.mockClear();
|
||||
fetchDashboardsSpy.mockClear();
|
||||
locationReloadSpy.mockClear();
|
||||
getMock.mockClear();
|
||||
};
|
||||
|
||||
const click = async (selector: () => HTMLElement) => act(() => fireEvent.click(selector()));
|
||||
const type = async (selector: () => HTMLInputElement, value: string) => {
|
||||
await act(() => fireEvent.input(selector(), { target: { value } }));
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
};
|
||||
|
||||
export const updateScopes = async (scopes: string[]) =>
|
||||
act(async () =>
|
||||
scopesSelectorScene?.updateScopes(
|
||||
scopes.map((scopeName) => ({
|
||||
scopeName,
|
||||
path: [],
|
||||
}))
|
||||
)
|
||||
);
|
||||
export const openSelector = async () => click(getSelectorInput);
|
||||
export const applyScopes = async () => {
|
||||
await click(getSelectorApply);
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
};
|
||||
export const cancelScopes = async () => click(getSelectorCancel);
|
||||
export const searchScopes = async (value: string) => type(getTreeSearch, value);
|
||||
export const clearScopesSearch = async () => type(getTreeSearch, '');
|
||||
export const expandResultApplications = async () => click(getResultApplicationsExpand);
|
||||
export const expandResultApplicationsCloud = async () => click(getResultApplicationsCloudExpand);
|
||||
export const expandResultCloud = async () => click(getResultCloudExpand);
|
||||
export const selectResultApplicationsGrafana = async () => click(getResultApplicationsGrafanaSelect);
|
||||
export const selectPersistedApplicationsMimir = async () => click(getPersistedApplicationsMimirSelect);
|
||||
export const selectResultApplicationsMimir = async () => click(getResultApplicationsMimirSelect);
|
||||
export const selectResultApplicationsCloud = async () => click(getResultApplicationsCloudSelect);
|
||||
export const selectResultApplicationsCloudDev = async () => click(getResultApplicationsCloudDevSelect);
|
||||
export const selectResultCloud = async () => click(getResultCloudSelect);
|
||||
export const selectResultCloudDev = async () => click(getResultCloudDevRadio);
|
||||
export const selectResultCloudOps = async () => click(getResultCloudOpsRadio);
|
||||
|
||||
export const toggleDashboards = async () => click(getDashboardsExpand);
|
||||
export const searchDashboards = async (value: string) => type(getDashboardsSearch, value);
|
||||
export const clearNotFound = async () => click(getNotFoundForFilterClear);
|
||||
export const expandDashboardFolder = (folder: string) => click(() => getDashboardFolderExpand(folder));
|
||||
|
||||
export const enterEditMode = async (dashboardScene: DashboardScene) =>
|
||||
act(async () => dashboardScene.onEnterEditMode());
|
||||
|
||||
export const getDashboardDTO = async () => {
|
||||
setDashboardAPI(undefined);
|
||||
await getDashboardAPI().getDashboardDTO('1');
|
||||
};
|
82
public/app/features/scopes/tests/utils/assertions.ts
Normal file
82
public/app/features/scopes/tests/utils/assertions.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { getMock, locationReloadSpy } from './mocks';
|
||||
import {
|
||||
getDashboard,
|
||||
getDashboardsExpand,
|
||||
getDashboardsSearch,
|
||||
getNotFoundForFilter,
|
||||
getNotFoundForScope,
|
||||
getNotFoundNoScopes,
|
||||
getPersistedApplicationsMimirSelect,
|
||||
getResultApplicationsCloudSelect,
|
||||
getResultApplicationsGrafanaSelect,
|
||||
getResultApplicationsMimirSelect,
|
||||
getResultCloudDevRadio,
|
||||
getResultCloudOpsRadio,
|
||||
getSelectorInput,
|
||||
getTreeHeadline,
|
||||
queryAllDashboard,
|
||||
queryDashboard,
|
||||
queryDashboardFolderExpand,
|
||||
queryDashboardsContainer,
|
||||
queryDashboardsSearch,
|
||||
queryPersistedApplicationsGrafanaSelect,
|
||||
queryPersistedApplicationsMimirSelect,
|
||||
queryResultApplicationsCloudSelect,
|
||||
queryResultApplicationsGrafanaSelect,
|
||||
queryResultApplicationsMimirSelect,
|
||||
querySelectorApply,
|
||||
} from './selectors';
|
||||
|
||||
const expectInDocument = (selector: () => HTMLElement) => expect(selector()).toBeInTheDocument();
|
||||
const expectNotInDocument = (selector: () => HTMLElement | null) => expect(selector()).not.toBeInTheDocument();
|
||||
const expectChecked = (selector: () => HTMLInputElement) => expect(selector()).toBeChecked();
|
||||
const expectRadioChecked = (selector: () => HTMLInputElement) => expect(selector().checked).toBe(true);
|
||||
const expectRadioNotChecked = (selector: () => HTMLInputElement) => expect(selector().checked).toBe(false);
|
||||
const expectValue = (selector: () => HTMLInputElement, value: string) => expect(selector().value).toBe(value);
|
||||
const expectTextContent = (selector: () => HTMLElement, text: string) => expect(selector()).toHaveTextContent(text);
|
||||
const expectDisabled = (selector: () => HTMLElement) => expect(selector()).toBeDisabled();
|
||||
|
||||
export const expectScopesSelectorClosed = () => expectNotInDocument(querySelectorApply);
|
||||
export const expectScopesSelectorValue = (value: string) => expectValue(getSelectorInput, value);
|
||||
export const expectScopesHeadline = (value: string) => expectTextContent(getTreeHeadline, value);
|
||||
export const expectPersistedApplicationsGrafanaNotPresent = () =>
|
||||
expectNotInDocument(queryPersistedApplicationsGrafanaSelect);
|
||||
export const expectResultApplicationsGrafanaSelected = () => expectChecked(getResultApplicationsGrafanaSelect);
|
||||
export const expectResultApplicationsGrafanaPresent = () => expectInDocument(getResultApplicationsGrafanaSelect);
|
||||
export const expectResultApplicationsGrafanaNotPresent = () =>
|
||||
expectNotInDocument(queryResultApplicationsGrafanaSelect);
|
||||
export const expectPersistedApplicationsMimirPresent = () => expectInDocument(getPersistedApplicationsMimirSelect);
|
||||
export const expectPersistedApplicationsMimirNotPresent = () =>
|
||||
expectNotInDocument(queryPersistedApplicationsMimirSelect);
|
||||
export const expectResultApplicationsMimirSelected = () => expectChecked(getResultApplicationsMimirSelect);
|
||||
export const expectResultApplicationsMimirPresent = () => expectInDocument(getResultApplicationsMimirSelect);
|
||||
export const expectResultApplicationsMimirNotPresent = () => expectNotInDocument(queryResultApplicationsMimirSelect);
|
||||
export const expectResultApplicationsCloudPresent = () => expectInDocument(getResultApplicationsCloudSelect);
|
||||
export const expectResultApplicationsCloudNotPresent = () => expectNotInDocument(queryResultApplicationsCloudSelect);
|
||||
export const expectResultCloudDevSelected = () => expectRadioChecked(getResultCloudDevRadio);
|
||||
export const expectResultCloudDevNotSelected = () => expectRadioNotChecked(getResultCloudDevRadio);
|
||||
export const expectResultCloudOpsSelected = () => expectRadioChecked(getResultCloudOpsRadio);
|
||||
export const expectResultCloudOpsNotSelected = () => expectRadioNotChecked(getResultCloudOpsRadio);
|
||||
|
||||
export const expectDashboardsDisabled = () => expectDisabled(getDashboardsExpand);
|
||||
export const expectDashboardsClosed = () => expectNotInDocument(queryDashboardsContainer);
|
||||
export const expectNoDashboardsSearch = () => expectNotInDocument(queryDashboardsSearch);
|
||||
export const expectDashboardsSearch = () => expectInDocument(getDashboardsSearch);
|
||||
export const expectNoDashboardsNoScopes = () => expectInDocument(getNotFoundNoScopes);
|
||||
export const expectNoDashboardsForScope = () => expectInDocument(getNotFoundForScope);
|
||||
export const expectNoDashboardsForFilter = () => expectInDocument(getNotFoundForFilter);
|
||||
export const expectDashboardSearchValue = (value: string) => expectValue(getDashboardsSearch, value);
|
||||
export const expectDashboardFolderNotInDocument = (uid: string) =>
|
||||
expectNotInDocument(() => queryDashboardFolderExpand(uid));
|
||||
export const expectDashboardInDocument = (uid: string) => expectInDocument(() => getDashboard(uid));
|
||||
export const expectDashboardNotInDocument = (uid: string) => expectNotInDocument(() => queryDashboard(uid));
|
||||
export const expectDashboardLength = (uid: string, length: number) =>
|
||||
expect(queryAllDashboard(uid)).toHaveLength(length);
|
||||
|
||||
export const expectNotDashboardReload = () => expect(locationReloadSpy).not.toHaveBeenCalled();
|
||||
export const expectDashboardReload = () => expect(locationReloadSpy).toHaveBeenCalled();
|
||||
|
||||
export const expectOldDashboardDTO = (scopes?: string[]) =>
|
||||
expect(getMock).toHaveBeenCalledWith('/api/dashboards/uid/1', scopes ? { scopes } : undefined);
|
||||
export const expectNewDashboardDTO = () =>
|
||||
expect(getMock).toHaveBeenCalledWith('/apis/dashboard.grafana.app/v0alpha1/namespaces/default/dashboards/1/dto');
|
@ -1,4 +1,6 @@
|
||||
import { Scope, ScopeDashboardBinding, ScopeNode } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { DataSourceRef } from '@grafana/schema/dist/esm/common/common.gen';
|
||||
|
||||
import * as api from '../../internal/api';
|
||||
|
||||
@ -371,6 +373,7 @@ export const fetchNodesSpy = jest.spyOn(api, 'fetchNodes');
|
||||
export const fetchScopeSpy = jest.spyOn(api, 'fetchScope');
|
||||
export const fetchSelectedScopesSpy = jest.spyOn(api, 'fetchSelectedScopes');
|
||||
export const fetchDashboardsSpy = jest.spyOn(api, 'fetchDashboards');
|
||||
export const locationReloadSpy = jest.spyOn(locationService, 'reload');
|
||||
|
||||
export const getMock = jest
|
||||
.fn()
|
||||
@ -448,3 +451,39 @@ export const dashboardWithRootFolderAndOtherFolder: ScopeDashboardBinding = gene
|
||||
'With root folder and other folder',
|
||||
['', 'Folder 3']
|
||||
);
|
||||
|
||||
export const getDatasource = async (ref: DataSourceRef) => {
|
||||
if (ref.uid === '-- Grafana --') {
|
||||
return {
|
||||
id: 1,
|
||||
uid: '-- Grafana --',
|
||||
name: 'grafana',
|
||||
type: 'grafana',
|
||||
meta: {
|
||||
id: 'grafana',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
meta: {
|
||||
id: 'grafana-testdata-datasource',
|
||||
},
|
||||
name: 'grafana-testdata-datasource',
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'gdev-testdata',
|
||||
getRef: () => {
|
||||
return { type: 'grafana-testdata-datasource', uid: 'gdev-testdata' };
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getInstanceSettings = () => ({
|
||||
id: 1,
|
||||
uid: 'gdev-testdata',
|
||||
name: 'testDs1',
|
||||
type: 'grafana-testdata-datasource',
|
||||
meta: {
|
||||
id: 'grafana-testdata-datasource',
|
||||
},
|
||||
});
|
||||
|
@ -1,92 +1,166 @@
|
||||
import { cleanup } from '@testing-library/react';
|
||||
import { KBarProvider } from 'kbar';
|
||||
import { render } from 'test/test-utils';
|
||||
|
||||
import {
|
||||
AdHocFiltersVariable,
|
||||
behaviors,
|
||||
GroupByVariable,
|
||||
sceneGraph,
|
||||
SceneGridItem,
|
||||
SceneGridLayout,
|
||||
SceneQueryRunner,
|
||||
SceneTimeRange,
|
||||
SceneVariableSet,
|
||||
VizPanel,
|
||||
} from '@grafana/scenes';
|
||||
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
|
||||
import { config, setPluginImportUtils } from '@grafana/runtime';
|
||||
import { defaultDashboard } from '@grafana/schema';
|
||||
import { AppChrome } from 'app/core/components/AppChrome/AppChrome';
|
||||
import { DashboardControls } from 'app/features/dashboard-scene/scene//DashboardControls';
|
||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||
import { transformSaveModelToScene } from 'app/features/dashboard-scene/serialization/transformSaveModelToScene';
|
||||
import { DashboardDataDTO, DashboardDTO, DashboardMeta } from 'app/types';
|
||||
|
||||
import { ScopesFacade } from '../../ScopesFacadeScene';
|
||||
import { scopesDashboardsScene, scopesSelectorScene } from '../../instance';
|
||||
import { initializeScopes, scopesDashboardsScene, scopesSelectorScene } from '../../instance';
|
||||
import { getInitialDashboardsState } from '../../internal/ScopesDashboardsScene';
|
||||
import { initialSelectorState } from '../../internal/ScopesSelectorScene';
|
||||
import { DASHBOARDS_OPENED_KEY } from '../../internal/const';
|
||||
|
||||
export function buildTestScene(overrides: Partial<DashboardScene> = {}) {
|
||||
return new DashboardScene({
|
||||
import { clearMocks } from './actions';
|
||||
|
||||
const getDashboardDTO: (
|
||||
overrideDashboard: Partial<DashboardDataDTO>,
|
||||
overrideMeta: Partial<DashboardMeta>
|
||||
) => DashboardDTO = (overrideDashboard, overrideMeta) => ({
|
||||
dashboard: {
|
||||
...defaultDashboard,
|
||||
title: 'hello',
|
||||
uid: 'dash-1',
|
||||
description: 'hello description',
|
||||
tags: ['tag1', 'tag2'],
|
||||
editable: true,
|
||||
$timeRange: new SceneTimeRange({
|
||||
timeZone: 'browser',
|
||||
}),
|
||||
controls: new DashboardControls({}),
|
||||
$behaviors: [
|
||||
new behaviors.CursorSync({}),
|
||||
new ScopesFacade({
|
||||
handler: (facade) => sceneGraph.getTimeRange(facade).onRefresh(),
|
||||
}),
|
||||
],
|
||||
$variables: new SceneVariableSet({
|
||||
variables: [
|
||||
new AdHocFiltersVariable({
|
||||
name: 'adhoc',
|
||||
datasource: { uid: 'my-ds-uid' },
|
||||
}),
|
||||
new GroupByVariable({
|
||||
name: 'groupby',
|
||||
datasource: { uid: 'my-ds-uid' },
|
||||
}),
|
||||
templating: {
|
||||
list: [
|
||||
{
|
||||
datasource: {
|
||||
type: 'datasource',
|
||||
uid: 'grafana',
|
||||
},
|
||||
filters: [],
|
||||
name: 'Filters',
|
||||
type: 'adhoc',
|
||||
},
|
||||
{
|
||||
current: {
|
||||
text: [],
|
||||
value: [],
|
||||
},
|
||||
datasource: {
|
||||
type: 'datasource',
|
||||
uid: 'grafana',
|
||||
},
|
||||
description: '',
|
||||
label: 'Group By',
|
||||
name: 'groupBy',
|
||||
type: 'groupby',
|
||||
},
|
||||
],
|
||||
}),
|
||||
body: new SceneGridLayout({
|
||||
children: [
|
||||
new SceneGridItem({
|
||||
key: 'griditem-1',
|
||||
},
|
||||
panels: [
|
||||
{
|
||||
datasource: {
|
||||
type: 'datasource',
|
||||
uid: 'grafana',
|
||||
},
|
||||
fieldConfig: {
|
||||
defaults: {
|
||||
color: {
|
||||
mode: 'thresholds',
|
||||
},
|
||||
custom: {
|
||||
align: 'auto',
|
||||
cellOptions: {
|
||||
type: 'auto',
|
||||
},
|
||||
inspect: false,
|
||||
},
|
||||
mappings: [],
|
||||
thresholds: {
|
||||
mode: 'absolute',
|
||||
steps: [
|
||||
{
|
||||
color: 'green',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
color: 'red',
|
||||
value: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
overrides: [],
|
||||
},
|
||||
gridPos: {
|
||||
h: 8,
|
||||
w: 12,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 300,
|
||||
height: 300,
|
||||
body: new VizPanel({
|
||||
title: 'Panel A',
|
||||
key: 'panel-1',
|
||||
pluginId: 'table',
|
||||
$data: new SceneQueryRunner({ key: 'data-query-runner', queries: [{ refId: 'A' }] }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
},
|
||||
id: 1,
|
||||
options: {
|
||||
cellHeight: 'sm',
|
||||
footer: {
|
||||
countRows: false,
|
||||
fields: '',
|
||||
reducer: ['sum'],
|
||||
show: false,
|
||||
},
|
||||
showHeader: true,
|
||||
},
|
||||
pluginVersion: '11.3.0-pre',
|
||||
targets: [
|
||||
{
|
||||
refId: 'A',
|
||||
},
|
||||
],
|
||||
title: 'Panel Title',
|
||||
type: 'table',
|
||||
},
|
||||
],
|
||||
...overrideDashboard,
|
||||
},
|
||||
meta: {
|
||||
...overrideMeta,
|
||||
},
|
||||
});
|
||||
|
||||
export function renderDashboard(dashboardScene: DashboardScene) {
|
||||
return render(
|
||||
const panelPlugin = getPanelPlugin({
|
||||
id: 'table',
|
||||
skipDataQuery: true,
|
||||
});
|
||||
|
||||
config.panels['table'] = panelPlugin.meta;
|
||||
|
||||
setPluginImportUtils({
|
||||
importPanelPlugin: () => Promise.resolve(panelPlugin),
|
||||
getPanelPluginFromCache: () => undefined,
|
||||
});
|
||||
|
||||
export function renderDashboard(
|
||||
overrideDashboard: Partial<DashboardDataDTO> = {},
|
||||
overrideMeta: Partial<DashboardMeta> = {}
|
||||
) {
|
||||
jest.useFakeTimers({ advanceTimers: true });
|
||||
jest.spyOn(console, 'error').mockImplementation(jest.fn());
|
||||
clearMocks();
|
||||
initializeScopes();
|
||||
|
||||
const dto: DashboardDTO = getDashboardDTO(overrideDashboard, overrideMeta);
|
||||
const scene = transformSaveModelToScene(dto);
|
||||
|
||||
render(
|
||||
<KBarProvider>
|
||||
<AppChrome>
|
||||
<dashboardScene.Component model={dashboardScene} />
|
||||
<scene.Component model={scene} />
|
||||
</AppChrome>
|
||||
</KBarProvider>
|
||||
);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
export function resetScenes() {
|
||||
export async function resetScenes() {
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
jest.useRealTimers();
|
||||
scopesSelectorScene?.setState(initialSelectorState);
|
||||
|
||||
localStorage.removeItem(DASHBOARDS_OPENED_KEY);
|
||||
|
||||
scopesDashboardsScene?.setState(getInitialDashboardsState());
|
||||
cleanup();
|
||||
}
|
||||
|
@ -53,36 +53,28 @@ export const getNotFoundForFilterClear = () => screen.getByTestId(selectors.dash
|
||||
export const getTreeSearch = () => screen.getByTestId<HTMLInputElement>(selectors.tree.search);
|
||||
export const getTreeHeadline = () => screen.getByTestId(selectors.tree.headline);
|
||||
export const getResultApplicationsExpand = () => screen.getByTestId(selectors.tree.expand('applications', 'result'));
|
||||
export const queryResultApplicationsGrafanaTitle = () =>
|
||||
screen.queryByTestId(selectors.tree.title('applications-grafana', 'result'));
|
||||
export const getResultApplicationsGrafanaTitle = () =>
|
||||
screen.getByTestId(selectors.tree.title('applications-grafana', 'result'));
|
||||
export const queryResultApplicationsGrafanaSelect = () =>
|
||||
screen.queryByTestId<HTMLInputElement>(selectors.tree.select('applications-grafana', 'result'));
|
||||
export const getResultApplicationsGrafanaSelect = () =>
|
||||
screen.getByTestId(selectors.tree.select('applications-grafana', 'result'));
|
||||
export const queryPersistedApplicationsGrafanaTitle = () =>
|
||||
screen.queryByTestId(selectors.tree.title('applications-grafana', 'persisted'));
|
||||
export const queryResultApplicationsMimirTitle = () =>
|
||||
screen.queryByTestId(selectors.tree.title('applications-mimir', 'result'));
|
||||
export const getResultApplicationsMimirTitle = () =>
|
||||
screen.getByTestId(selectors.tree.title('applications-mimir', 'result'));
|
||||
screen.getByTestId<HTMLInputElement>(selectors.tree.select('applications-grafana', 'result'));
|
||||
export const queryPersistedApplicationsGrafanaSelect = () =>
|
||||
screen.queryByTestId<HTMLInputElement>(selectors.tree.select('applications-grafana', 'persisted'));
|
||||
export const queryResultApplicationsMimirSelect = () =>
|
||||
screen.queryByTestId(selectors.tree.select('applications-mimir', 'result'));
|
||||
export const getResultApplicationsMimirSelect = () =>
|
||||
screen.getByTestId(selectors.tree.select('applications-mimir', 'result'));
|
||||
export const queryPersistedApplicationsMimirTitle = () =>
|
||||
screen.queryByTestId(selectors.tree.title('applications-mimir', 'persisted'));
|
||||
export const getPersistedApplicationsMimirTitle = () =>
|
||||
screen.getByTestId(selectors.tree.title('applications-mimir', 'persisted'));
|
||||
screen.getByTestId<HTMLInputElement>(selectors.tree.select('applications-mimir', 'result'));
|
||||
export const queryPersistedApplicationsMimirSelect = () =>
|
||||
screen.queryByTestId(selectors.tree.select('applications-mimir', 'persisted'));
|
||||
export const getPersistedApplicationsMimirSelect = () =>
|
||||
screen.getByTestId(selectors.tree.select('applications-mimir', 'persisted'));
|
||||
export const queryResultApplicationsCloudTitle = () =>
|
||||
screen.queryByTestId(selectors.tree.title('applications-cloud', 'result'));
|
||||
export const queryResultApplicationsCloudSelect = () =>
|
||||
screen.queryByTestId(selectors.tree.select('applications-cloud', 'result'));
|
||||
export const getResultApplicationsCloudSelect = () =>
|
||||
screen.getByTestId(selectors.tree.select('applications-cloud', 'result'));
|
||||
export const getResultApplicationsCloudExpand = () =>
|
||||
screen.getByTestId(selectors.tree.expand('applications-cloud', 'result'));
|
||||
export const getResultApplicationsCloudDevSelect = () =>
|
||||
screen.getByTestId(selectors.tree.select('applications-cloud-dev', 'result'));
|
||||
export const getResultApplicationsCloudOpsSelect = () =>
|
||||
screen.getByTestId(selectors.tree.select('applications-cloud-ops', 'result'));
|
||||
|
||||
export const getResultCloudSelect = () => screen.getByTestId(selectors.tree.select('cloud', 'result'));
|
||||
export const getResultCloudExpand = () => screen.getByTestId(selectors.tree.expand('cloud', 'result'));
|
||||
|
64
public/app/features/scopes/tests/viewMode.test.ts
Normal file
64
public/app/features/scopes/tests/viewMode.test.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||
|
||||
import { scopesDashboardsScene, scopesSelectorScene } from '../instance';
|
||||
|
||||
import { enterEditMode, openSelector, toggleDashboards } from './utils/actions';
|
||||
import { expectDashboardsClosed, expectDashboardsDisabled, expectScopesSelectorClosed } from './utils/assertions';
|
||||
import { getDatasource, getInstanceSettings, getMock } from './utils/mocks';
|
||||
import { renderDashboard, resetScenes } from './utils/render';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
useChromeHeaderHeight: jest.fn(),
|
||||
getBackendSrv: () => ({ get: getMock }),
|
||||
getDataSourceSrv: () => ({ get: getDatasource, getInstanceSettings }),
|
||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||
}));
|
||||
|
||||
describe('View mode', () => {
|
||||
let dashboardScene: DashboardScene;
|
||||
|
||||
beforeAll(() => {
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.groupByVariable = true;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
dashboardScene = renderDashboard();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await resetScenes();
|
||||
});
|
||||
|
||||
it('Enters view mode', async () => {
|
||||
await enterEditMode(dashboardScene);
|
||||
expect(scopesSelectorScene?.state?.isReadOnly).toEqual(true);
|
||||
expect(scopesDashboardsScene?.state?.isPanelOpened).toEqual(false);
|
||||
});
|
||||
|
||||
it('Closes selector on enter', async () => {
|
||||
await openSelector();
|
||||
await enterEditMode(dashboardScene);
|
||||
expectScopesSelectorClosed();
|
||||
});
|
||||
|
||||
it('Closes dashboards list on enter', async () => {
|
||||
await toggleDashboards();
|
||||
await enterEditMode(dashboardScene);
|
||||
expectDashboardsClosed();
|
||||
});
|
||||
|
||||
it('Does not open selector when view mode is active', async () => {
|
||||
await enterEditMode(dashboardScene);
|
||||
await openSelector();
|
||||
expectScopesSelectorClosed();
|
||||
});
|
||||
|
||||
it('Disables the expand button when view mode is active', async () => {
|
||||
await enterEditMode(dashboardScene);
|
||||
expectDashboardsDisabled();
|
||||
});
|
||||
});
|
@ -73,6 +73,10 @@ export interface DashboardMeta {
|
||||
// yes weird, but this means all the editor structures can exist unchanged
|
||||
// until we use the resource as the main container
|
||||
k8s?: Partial<ObjectMeta>;
|
||||
|
||||
// This is a property added specifically for edge cases where dashboards should be reloaded on scopes changes
|
||||
// This property is not persisted in the DB but its existence is controlled by the API
|
||||
reloadOnScopesChange?: boolean;
|
||||
}
|
||||
|
||||
export interface AnnotationActions {
|
||||
|
Loading…
Reference in New Issue
Block a user