--wip-- [skip ci]

pull/90363/head
Tom Ratcliffe 7 months ago
parent 08bb66ba72
commit 854991adc2
  1. 106
      public/app/features/alerting/unified/RuleEditor.test.tsx
  2. 4
      public/app/features/alerting/unified/components/rule-editor/CloudRulesSourcePicker.tsx
  3. 5
      public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx
  4. 2
      public/app/features/alerting/unified/mocks/server/all-handlers.ts
  5. 4
      public/app/features/alerting/unified/mocks/server/handlers/datasources.ts
  6. 22
      public/app/features/alerting/unified/mocks/server/handlers/mimirRuler.ts
  7. 12
      public/app/features/alerting/unified/mocks/server/handlers/prometheus.ts
  8. 6
      public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.test.tsx

@ -0,0 +1,106 @@
import { Route, Routes } from 'react-router-dom-v5-compat';
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
import { render, screen } from 'test/test-utils';
import { setAppEvents } from '@grafana/runtime';
import appEvents from 'app/core/app_events';
import RuleEditor from 'app/features/alerting/unified/RuleEditor';
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
import { grantUserPermissions, mockDataSource } from 'app/features/alerting/unified/mocks';
import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources';
import { DataSourceType } from 'app/features/alerting/unified/utils/datasource';
import { AccessControlAction } from 'app/types';
setupMswServer();
// Required to make sure that loading the datasource plugin does not fail
// see public/app/plugins/datasource/loki/module.ts
setAppEvents(appEvents);
const lokiDatasource = mockDataSource(
{
type: DataSourceType.Loki,
name: 'loki',
uid: 'oKflPJ2GF38UCq',
id: 1,
isDefault: true,
jsonData: {
manageAlerts: true,
},
},
{
alerting: true,
// needed to make the correct plugin components load
module: 'core:plugin/loki',
id: 'loki',
}
);
beforeEach(() => {
grantUserPermissions([
AccessControlAction.AlertingInstanceCreate,
AccessControlAction.FoldersRead,
AccessControlAction.DataSourcesRead,
AccessControlAction.AlertingRuleCreate,
AccessControlAction.AlertingRuleExternalWrite,
AccessControlAction.AlertingRuleExternalRead,
]);
setupDataSources(lokiDatasource);
});
const renderRecordingRuleEditor = () => {
return render(
<Routes>
<Route path="/alerting/new/:type" element={<RuleEditor />} />
</Routes>,
{
historyOptions: {
initialEntries: [`/alerting/new/recording`],
},
}
);
};
describe('Recording rules', () => {
it('allows user to create loki recording rule', async () => {
const { user } = renderRecordingRuleEditor();
await screen.findByText('New recording rule');
const datasourcePicker = await screen.findByLabelText(/select data source/i);
await user.click(datasourcePicker);
await selectOptionInTest(datasourcePicker, /loki/i);
const editor = await screen.findByPlaceholderText(/text to find/i);
await user.type(editor, '1');
await user.click(screen.getByText(/run query/i));
// screen.debug(await screen.findByTestId('loki-editor'), Infinity);
expect(await screen.findByText(/No data/)).toBeInTheDocument();
});
// it('renders ', async () => {
// const { user } = render(
// <Routes>
// <Route path="/alerting/new/:type" element={<RuleEditor />} />
// </Routes>,
// {
// historyOptions: {
// initialEntries: [`/alerting/new/recording`],
// },
// }
// );
// await screen.findByText('New recording rule');
// const datasourcePicker = await screen.findByLabelText(/select data source/i);
// await user.click(datasourcePicker);
// await selectOptionInTest(datasourcePicker, /loki/i);
// const editor = await screen.findByPlaceholderText(/text to find/i);
// await user.type(editor, '1');
// await user.click(screen.getByText(/run query/i));
// });
});

@ -11,9 +11,10 @@ interface Props {
value: string | null; value: string | null;
onBlur?: () => void; onBlur?: () => void;
name?: string; name?: string;
id?: string;
} }
export function CloudRulesSourcePicker({ value, disabled, ...props }: Props): JSX.Element { export function CloudRulesSourcePicker({ value, disabled, id, ...props }: Props): JSX.Element {
const { rulesSourcesWithRuler: dataSourcesWithRuler, isLoading } = useRulesSourcesWithRuler(); const { rulesSourcesWithRuler: dataSourcesWithRuler, isLoading } = useRulesSourcesWithRuler();
const dataSourceFilter = useCallback( const dataSourceFilter = useCallback(
@ -30,6 +31,7 @@ export function CloudRulesSourcePicker({ value, disabled, ...props }: Props): JS
alerting alerting
filter={dataSourceFilter} filter={dataSourceFilter}
current={value} current={value}
inputId={id}
{...props} {...props}
/> />
); );

@ -22,6 +22,9 @@ export const CloudDataSourceSelector = ({ disabled, onChangeCloudDatasource }: C
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const ruleFormType = watch('type'); const ruleFormType = watch('type');
// Include an ID to make the label + datasource picker linked + properly accessible
const id = 'cloud-data-source-picker';
return ( return (
<> <>
<div className={styles.flexRow}> <div className={styles.flexRow}>
@ -31,11 +34,13 @@ export const CloudDataSourceSelector = ({ disabled, onChangeCloudDatasource }: C
label={disabled ? 'Data source' : 'Select data source'} label={disabled ? 'Data source' : 'Select data source'}
error={errors.dataSourceName?.message} error={errors.dataSourceName?.message}
invalid={!!errors.dataSourceName?.message} invalid={!!errors.dataSourceName?.message}
htmlFor={id}
> >
<Controller <Controller
render={({ field: { onChange, ref, ...field } }) => ( render={({ field: { onChange, ref, ...field } }) => (
<CloudRulesSourcePicker <CloudRulesSourcePicker
{...field} {...field}
id={id}
disabled={disabled} disabled={disabled}
onChange={(ds: DataSourceInstanceSettings) => { onChange={(ds: DataSourceInstanceSettings) => {
// reset expression as they don't need to persist after changing datasources // reset expression as they don't need to persist after changing datasources

@ -17,6 +17,7 @@ import mimirRulerHandlers from 'app/features/alerting/unified/mocks/server/handl
import notificationsHandlers from 'app/features/alerting/unified/mocks/server/handlers/notifications'; import notificationsHandlers from 'app/features/alerting/unified/mocks/server/handlers/notifications';
import pluginsHandlers from 'app/features/alerting/unified/mocks/server/handlers/plugins'; import pluginsHandlers from 'app/features/alerting/unified/mocks/server/handlers/plugins';
import allPluginHandlers from 'app/features/alerting/unified/mocks/server/handlers/plugins/all-plugin-handlers'; import allPluginHandlers from 'app/features/alerting/unified/mocks/server/handlers/plugins/all-plugin-handlers';
import prometheusHandlers from 'app/features/alerting/unified/mocks/server/handlers/prometheus';
import provisioningHandlers from 'app/features/alerting/unified/mocks/server/handlers/provisioning'; import provisioningHandlers from 'app/features/alerting/unified/mocks/server/handlers/provisioning';
import searchHandlers from 'app/features/alerting/unified/mocks/server/handlers/search'; import searchHandlers from 'app/features/alerting/unified/mocks/server/handlers/search';
import silenceHandlers from 'app/features/alerting/unified/mocks/server/handlers/silences'; import silenceHandlers from 'app/features/alerting/unified/mocks/server/handlers/silences';
@ -35,6 +36,7 @@ const allHandlers = [
...folderHandlers, ...folderHandlers,
...pluginsHandlers, ...pluginsHandlers,
...provisioningHandlers, ...provisioningHandlers,
...prometheusHandlers,
...silenceHandlers, ...silenceHandlers,
...searchHandlers, ...searchHandlers,
...notificationsHandlers, ...notificationsHandlers,

@ -46,10 +46,14 @@ const resourcesMetadataHandler = () =>
HttpResponse.json({ status: 'success', data: {} }) HttpResponse.json({ status: 'success', data: {} })
); );
const resourcesLabelsHandler2 = () =>
http.get('/api/datasources/uid/:datasourceUid/resources/labels', () => HttpResponse.json({ status: 'success' }));
const datasourcesHandlers = [ const datasourcesHandlers = [
datasourceBuildInfoHandler(), datasourceBuildInfoHandler(),
labelValuesHandler(), labelValuesHandler(),
resourcesLabelsHandler(), resourcesLabelsHandler(),
resourcesMetadataHandler(), resourcesMetadataHandler(),
resourcesLabelsHandler2(),
]; ];
export default datasourcesHandlers; export default datasourcesHandlers;

@ -39,6 +39,26 @@ export const updateRulerRuleNamespaceHandler = (options?: HandlerOptions) => {
}); });
}; };
const rulerRulesHandler = () => {
return http.get<{ datasourceUid: string }>(`/api/ruler/:datasourceUid/api/v1/rules`, () => {
return HttpResponse.json({});
});
};
const rulerRulesTestHandler = () => {
/**
* This particular response is needed to allow tests to handle the case of testing
* whether a ruler supports rules (see `isCortexErrorResponse` method)
*/
const missingGroupResponse = {
message: 'get rule group user="", namespace="", name="": group does not exist\n',
traceId: '',
};
return http.get<{ datasourceUid: string }>('/api/ruler/:datasourceUid/api/v1/rules/test/test', () => {
return HttpResponse.json(missingGroupResponse, { status: 404 });
});
};
export const rulerRuleGroupHandler = (options?: HandlerOptions) => { export const rulerRuleGroupHandler = (options?: HandlerOptions) => {
return http.get<{ namespaceName: string; groupName: string }>( return http.get<{ namespaceName: string; groupName: string }>(
`/api/ruler/:dataSourceUID/api/v1/rules/:namespaceName/:groupName`, `/api/ruler/:dataSourceUID/api/v1/rules/:namespaceName/:groupName`,
@ -84,6 +104,8 @@ export const deleteRulerRuleGroupHandler = () => {
const handlers = [ const handlers = [
getRulerRulesHandler(), getRulerRulesHandler(),
prometheusRulesHandler(), prometheusRulesHandler(),
rulerRulesHandler(),
rulerRulesTestHandler(),
updateRulerRuleNamespaceHandler(), updateRulerRuleNamespaceHandler(),
rulerRuleGroupHandler(), rulerRuleGroupHandler(),
deleteRulerRuleGroupHandler(), deleteRulerRuleGroupHandler(),

@ -0,0 +1,12 @@
import { http, HttpResponse } from 'msw';
export const getPrometheusRulesHandler = () => {
return http.get(`/api/prometheus/:datasourceUid/api/v1/rules`, () => {
return HttpResponse.json({
data: { groups: [] },
});
});
};
const handlers = [getPrometheusRulesHandler()];
export default handlers;

@ -1,4 +1,5 @@
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
@ -31,8 +32,11 @@ function renderComponent({
describe('MonacoQueryField', () => { describe('MonacoQueryField', () => {
test('Renders with no errors', async () => { test('Renders with no errors', async () => {
renderComponent(); renderComponent();
const user = userEvent.setup();
const monacoEditor = await screen.findByTestId(selectors.components.ReactMonacoEditor.editorLazy); const monacoEditor = await screen.findByTestId(selectors.components.ReactMonacoEditor.editorLazy);
expect(monacoEditor).toBeInTheDocument(); expect(monacoEditor).toBeInTheDocument();
const editor = await screen.findByLabelText(/editor content/i);
await user.type(editor, '1');
}); });
}); });

Loading…
Cancel
Save