Expressions: Add tests for the QueryEditorRows and QueryGroup components (#63394)

* add QueryGroup onAddQueryClick and onExpressionClick tests

* add test's for the query group component

- new query/expression should be expanded
- adds query/expression onclick

* add open data source help modal test

* add disabled expression test

* add query editor rows test

* rm unused attributes, rm renderScenario required override
This commit is contained in:
Abdul 2023-02-22 12:26:41 -03:00 committed by GitHub
parent 33277fccde
commit 5ec8e370f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 314 additions and 6 deletions

View File

@ -2,5 +2,5 @@
# Manual changes might be lost!
integrations:
- vim
- vscode
- vim

View File

@ -490,7 +490,7 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
const DatasourceCheatsheet = datasource.components?.QueryEditorHelp;
return (
<div aria-label={selectors.components.QueryEditorRows.rows}>
<div data-testid="query-editor-row" aria-label={selectors.components.QueryEditorRows.rows}>
<QueryOperationRow
id={this.id}
draggable={true}

View File

@ -0,0 +1,156 @@
import { fireEvent, queryByLabelText, render, screen } from '@testing-library/react';
import React from 'react';
import { type DataQuery } from '@grafana/schema';
import { mockDataSource } from 'app/features/alerting/unified/mocks';
import { DataSourceType } from 'app/features/alerting/unified/utils/datasource';
import createMockPanelData from 'app/plugins/datasource/azuremonitor/__mocks__/panelData';
import { QueryEditorRows, Props } from './QueryEditorRows';
const mockDS = mockDataSource({
name: 'CloudManager',
type: DataSourceType.Alertmanager,
});
const mockVariable = mockDataSource({
name: '${dsVariable}',
type: 'datasource',
});
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
return {
getDataSourceSrv: () => ({
get: () => Promise.resolve({ ...mockDS, getRef: () => {} }),
getList: ({ variables }: { variables: boolean }) => (variables ? [mockDS, mockVariable] : [mockDS]),
getInstanceSettings: () => ({
...mockDS,
meta: {
...mockDS.meta,
alerting: true,
mixed: true,
},
}),
}),
};
});
const props: Props = {
queries: [
{
datasource: mockDS,
refId: 'A',
},
{
datasource: mockDS,
refId: 'B',
},
],
dsSettings: mockDataSource(),
onQueriesChange: function (queries: DataQuery[]): void {
throw new Error('Function not implemented.');
},
onAddQuery: function (query: DataQuery): void {
throw new Error('Function not implemented.');
},
onRunQueries: function (): void {
throw new Error('Function not implemented.');
},
data: createMockPanelData(),
};
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
return {
getDataSourceSrv: () => ({
get: () => Promise.resolve(mockDS),
getList: ({ variables }: { variables: boolean }) => (variables ? [mockDS, mockVariable] : [mockDS]),
getInstanceSettings: () => mockDS,
}),
};
});
describe('QueryEditorRows', () => {
it('Should render queries', async () => {
const {
renderResult: { rerender },
} = renderScenario();
expect((await screen.findByTestId('query-editor-rows')).children.length).toBe(2);
rerender(
<QueryEditorRows
{...props}
queries={[
{
datasource: mockDS,
refId: 'A',
},
]}
/>
);
expect((await screen.findByTestId('query-editor-rows')).children.length).toBe(1);
});
it('Should be able to expand and collapse queries', async () => {
renderScenario();
const queryEditorRows = await screen.findAllByTestId('query-editor-row');
for (const childQuery of queryEditorRows) {
const toggleExpandButton = queryByLabelText(childQuery, 'toggle collapse and expand query row') as HTMLElement;
expect(toggleExpandButton).toBeInTheDocument();
expect(toggleExpandButton.getAttribute('aria-expanded')).toBe('true');
fireEvent.click(toggleExpandButton);
expect(toggleExpandButton.getAttribute('aria-expanded')).toBe('false');
}
});
it('Should be able to duplicate queries', async () => {
const onAddQuery = jest.fn();
const onQueryCopied = jest.fn();
renderScenario({ onAddQuery, onQueryCopied });
const queryEditorRows = await screen.findAllByTestId('query-editor-row');
queryEditorRows.map(async (childQuery) => {
const duplicateQueryButton = queryByLabelText(
childQuery,
'Duplicate query query operation action'
) as HTMLElement;
expect(duplicateQueryButton).toBeInTheDocument();
fireEvent.click(duplicateQueryButton);
});
expect(onAddQuery).toHaveBeenCalledTimes(queryEditorRows.length);
expect(onQueryCopied).toHaveBeenCalledTimes(queryEditorRows.length);
});
it('Should be able to delete queries', async () => {
const onQueriesChange = jest.fn();
const onQueryRemoved = jest.fn();
renderScenario({ onQueriesChange, onQueryRemoved });
const queryEditorRows = await screen.findAllByTestId('query-editor-row');
queryEditorRows.map(async (childQuery) => {
const deleteQueryButton = queryByLabelText(childQuery, 'Remove query query operation action') as HTMLElement;
expect(deleteQueryButton).toBeInTheDocument();
fireEvent.click(deleteQueryButton);
});
expect(onQueriesChange).toHaveBeenCalledTimes(queryEditorRows.length);
expect(onQueryRemoved).toHaveBeenCalledTimes(queryEditorRows.length);
});
});
function renderScenario(overrides?: Partial<Props>) {
Object.assign(props, overrides);
return {
renderResult: render(<QueryEditorRows {...props} />),
};
}

View File

@ -14,7 +14,7 @@ import { getDataSourceSrv, reportInteraction } from '@grafana/runtime';
import { QueryEditorRow } from './QueryEditorRow';
interface Props {
export interface Props {
// The query configuration
queries: DataQuery[];
dsSettings: DataSourceInstanceSettings;
@ -151,7 +151,7 @@ export class QueryEditorRows extends PureComponent<Props> {
<Droppable droppableId="transformations-list" direction="vertical">
{(provided) => {
return (
<div ref={provided.innerRef} {...provided.droppableProps}>
<div data-testid="query-editor-rows" ref={provided.innerRef} {...provided.droppableProps}>
{queries.map((query, index) => {
const dataSourceSettings = getDataSourceSettings(query, dsSettings);
const onChangeDataSourceSettings = dsSettings.meta.mixed

View File

@ -0,0 +1,149 @@
import { act, render, screen } from '@testing-library/react';
import React from 'react';
import config from 'app/core/config';
import { mockDataSource } from 'app/features/alerting/unified/mocks';
import { DataSourceType } from 'app/features/alerting/unified/utils/datasource';
import { PanelQueryRunner } from '../state/PanelQueryRunner';
import { Props, QueryGroup } from './QueryGroup';
const mockDS = mockDataSource({
name: 'CloudManager',
type: DataSourceType.Alertmanager,
});
const mockVariable = mockDataSource({
name: '${dsVariable}',
type: 'datasource',
});
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
return {
getDataSourceSrv: () => ({
get: () => Promise.resolve({ ...mockDS, getRef: () => {} }),
getList: ({ variables }: { variables: boolean }) => (variables ? [mockDS, mockVariable] : [mockDS]),
getInstanceSettings: () => ({
...mockDS,
meta: {
...mockDS.meta,
alerting: true,
mixed: true,
},
}),
}),
};
});
describe('QueryGroup', () => {
beforeEach(() => {
jest.useFakeTimers();
config.expressionsEnabled = true;
});
it('Should add expression on click', async () => {
renderScenario({});
const addExpressionButton = await screen.findByTestId('query-tab-add-expression');
const queryRowsContainer = await screen.findByTestId('query-editor-rows');
expect(queryRowsContainer.children.length).toBe(2);
await addExpressionButton.click();
expect(queryRowsContainer.children.length).toBe(3);
});
it('Should add query on click', async () => {
renderScenario({});
const addQueryButton = await screen.findByTestId('query-tab-add-query');
const queryRowsContainer = await screen.findByTestId('query-editor-rows');
expect(queryRowsContainer.children.length).toBe(2);
await addQueryButton.click();
expect(queryRowsContainer.children.length).toBe(3);
});
it('New expression should be expanded', async () => {
renderScenario({});
const addExpressionButton = await screen.findByTestId('query-tab-add-expression');
const queryRowsContainer = await screen.findByTestId('query-editor-rows');
await addExpressionButton.click();
const lastQueryEditorRow = (await screen.findAllByTestId('query-editor-row')).at(-1);
const lastEditorToggleRow = (await screen.findAllByLabelText('toggle collapse and expand query row')).at(-1);
expect(lastEditorToggleRow?.getAttribute('aria-expanded')).toBe('true');
expect(lastQueryEditorRow?.firstElementChild?.children.length).toBe(2);
expect(queryRowsContainer.children.length).toBe(3);
});
it('New query should be expanded', async () => {
renderScenario({});
const addQueryButton = await screen.findByTestId('query-tab-add-query');
const queryRowsContainer = await screen.findByTestId('query-editor-rows');
await addQueryButton.click();
const lastQueryEditorRow = (await screen.findAllByTestId('query-editor-row')).at(-1);
const lastEditorToggleRow = (await screen.findAllByLabelText('toggle collapse and expand query row')).at(-1);
expect(lastEditorToggleRow?.getAttribute('aria-expanded')).toBe('true');
expect(lastQueryEditorRow?.firstElementChild?.children.length).toBe(2);
expect(queryRowsContainer.children.length).toBe(3);
});
it('Should open data source help modal', async () => {
renderScenario({});
const openHelpButton = await screen.findByTestId('query-tab-help-button');
await act(async () => {
await openHelpButton.click();
});
const helpModal = await screen.findByRole('dialog');
expect(helpModal).toBeInTheDocument();
});
it('Should not show add expression button when expressions are disabled', async () => {
config.expressionsEnabled = false;
renderScenario({});
const addExpressionButton = screen.queryByTestId('query-tab-add-expression');
expect(addExpressionButton).not.toBeInTheDocument();
});
});
function renderScenario(overrides: Partial<Props>) {
const props: Props = {
onOptionsChange: jest.fn(),
queryRunner: new PanelQueryRunner({
getDataSupport: jest.fn(),
getFieldOverrideOptions: jest.fn(),
getTransformations: jest.fn(),
}),
options: {
queries: [
{
datasource: mockDS,
refId: 'A',
},
{
datasource: mockDS,
refId: 'B',
},
],
dataSource: mockDS,
},
onRunQueries: function (): void {
throw new Error('Function not implemented.');
},
};
Object.assign(props, overrides);
return {
props,
renderResult: render(<QueryGroup {...props} />),
};
}

View File

@ -32,7 +32,7 @@ import { QueryEditorRows } from './QueryEditorRows';
import { QueryGroupOptionsEditor } from './QueryGroupOptions';
import { SavedQueryPicker } from './SavedQueryPicker';
interface Props {
export interface Props {
queryRunner: PanelQueryRunner;
options: QueryGroupOptions;
onOpenQueryInspector?: () => void;
@ -94,6 +94,7 @@ export class QueryGroup extends PureComponent<Props, State> {
try {
const ds = await this.dataSourceSrv.get(options.dataSource);
const dsSettings = this.dataSourceSrv.getInstanceSettings(options.dataSource);
const defaultDataSource = await this.dataSourceSrv.get();
const datasource = ds.getRef();
const queries = options.queries.map((q) => ({
@ -290,6 +291,7 @@ export class QueryGroup extends PureComponent<Props, State> {
icon="question-circle"
title="Open data source help"
onClick={this.onOpenHelp}
data-testid="query-tab-help-button"
/>
</div>
<div className={styles.dataSourceRowItemOptions}>
@ -375,7 +377,6 @@ export class QueryGroup extends PureComponent<Props, State> {
renderQueries(dsSettings: DataSourceInstanceSettings) {
const { onRunQueries } = this.props;
const { data, queries } = this.state;
if (isSharedDashboardQuery(dsSettings.name)) {
return (
<DashboardQueryEditor
@ -429,6 +430,7 @@ export class QueryGroup extends PureComponent<Props, State> {
onClick={this.onAddQueryClick}
variant="secondary"
aria-label={selectors.components.QueryTab.addQuery}
data-testid="query-tab-add-query"
>
Query
</Button>
@ -439,6 +441,7 @@ export class QueryGroup extends PureComponent<Props, State> {
onClick={this.onAddExpressionClick}
variant="secondary"
className={styles.expressionButton}
data-testid="query-tab-add-expression"
>
<span>Expression&nbsp;</span>
</Button>