mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Add integration tests to query builder (#79978)
* Loki: Add integration tests * Use findBy instead of getBy * Fix console error
This commit is contained in:
parent
54369158ce
commit
7be8301a26
@ -1,6 +1,7 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor, findAllByRole } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { getSelectParent } from 'test/helpers/selectOptionInTest';
|
||||
|
||||
import { createLokiDatasource } from '../../mocks';
|
||||
|
||||
@ -30,6 +31,96 @@ describe('LokiQueryBuilderContainer', () => {
|
||||
refId: 'A',
|
||||
});
|
||||
});
|
||||
it('uses | to separate multiple values in label filters', async () => {
|
||||
const props = {
|
||||
query: {
|
||||
expr: '{app="app1"}',
|
||||
refId: 'A',
|
||||
},
|
||||
datasource: createLokiDatasource(),
|
||||
onChange: jest.fn(),
|
||||
onRunQuery: () => {},
|
||||
showExplain: false,
|
||||
};
|
||||
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);
|
||||
props.datasource.languageProvider.fetchSeriesLabels = jest.fn().mockReturnValue({ job: ['grafana', 'loki'] });
|
||||
props.onChange = jest.fn();
|
||||
|
||||
render(<LokiQueryBuilderContainer {...props} />);
|
||||
await userEvent.click(screen.getByLabelText('Add'));
|
||||
const labels = screen.getByText(/Label filters/);
|
||||
const selects = await findAllByRole(getSelectParent(labels)!, 'combobox');
|
||||
await userEvent.click(selects[3]);
|
||||
userEvent.click(await screen.findByText('job'));
|
||||
|
||||
await userEvent.click(selects[4]);
|
||||
userEvent.click(await screen.findByText('=~'));
|
||||
|
||||
await userEvent.click(selects[5]);
|
||||
userEvent.click(await screen.findByText('grafana'));
|
||||
|
||||
await userEvent.click(selects[5]);
|
||||
userEvent.click(await screen.findByText('loki'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(props.onChange).toBeCalledWith({ expr: '{app="app1", job=~"grafana|loki"}', refId: 'A' });
|
||||
});
|
||||
});
|
||||
|
||||
it('highlights the query in preview using loki grammar', async () => {
|
||||
const props = {
|
||||
query: {
|
||||
expr: '{app="baz"} | logfmt',
|
||||
refId: 'A',
|
||||
},
|
||||
datasource: createLokiDatasource(),
|
||||
onChange: jest.fn(),
|
||||
onRunQuery: () => {},
|
||||
showExplain: false,
|
||||
};
|
||||
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);
|
||||
render(<LokiQueryBuilderContainer {...props} />);
|
||||
expect(screen.getByText('{')).toHaveClass('token punctuation');
|
||||
expect(screen.getByText('"baz"')).toHaveClass('token label-value attr-value');
|
||||
expect(screen.getByText('|')).toHaveClass('token pipe-operator operator');
|
||||
expect(screen.getByText('logfmt')).toHaveClass('token pipe-operations keyword');
|
||||
});
|
||||
|
||||
it('shows conflicting label expressions', async () => {
|
||||
const props = {
|
||||
query: {
|
||||
expr: '{job="grafana"} | app!="bar" | app="bar"',
|
||||
refId: 'A',
|
||||
},
|
||||
datasource: createLokiDatasource(),
|
||||
onChange: jest.fn(),
|
||||
onRunQuery: () => {},
|
||||
showExplain: false,
|
||||
};
|
||||
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);
|
||||
|
||||
render(<LokiQueryBuilderContainer {...props} />);
|
||||
expect(screen.getAllByText('You have conflicting label filters')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('uses <expr> as placeholder for query in explain section', async () => {
|
||||
const props = {
|
||||
query: {
|
||||
expr: '{job="grafana"} | logfmt',
|
||||
refId: 'A',
|
||||
},
|
||||
datasource: createLokiDatasource(),
|
||||
onChange: jest.fn(),
|
||||
onRunQuery: () => {},
|
||||
showExplain: true,
|
||||
};
|
||||
props.datasource.getDataSamples = jest.fn().mockResolvedValue([]);
|
||||
|
||||
render(<LokiQueryBuilderContainer {...props} />);
|
||||
expect(screen.getByText('<')).toBeInTheDocument();
|
||||
expect(screen.getByText('expr')).toBeInTheDocument();
|
||||
expect(screen.getByText('>')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
async function addOperation(section: string, op: string) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { useEffect, useId, useState } from 'react';
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import { Draggable, DraggableProvided } from 'react-beautiful-dnd';
|
||||
|
||||
import { DataSourceApi, GrafanaTheme2, TimeRange } from '@grafana/data';
|
||||
import { Button, Icon, InlineField, Tooltip, useTheme2, Stack } from '@grafana/ui';
|
||||
@ -145,6 +145,34 @@ export function OperationEditor({
|
||||
return isConflicting ? true : undefined;
|
||||
};
|
||||
|
||||
// We need to extract this into a component to prevent InlineField passing invalid to div which produces console error
|
||||
const StyledOperationHeader = ({ provided }: { provided: DraggableProvided }) => (
|
||||
<div
|
||||
className={cx(styles.card, (shouldFlash || highlight) && styles.cardHighlight, isConflicting && styles.cardError)}
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
data-testid={`operations.${index}.wrapper`}
|
||||
>
|
||||
<OperationHeader
|
||||
operation={operation}
|
||||
dragHandleProps={provided.dragHandleProps}
|
||||
def={def}
|
||||
index={index}
|
||||
onChange={onChange}
|
||||
onRemove={onRemove}
|
||||
queryModeller={queryModeller}
|
||||
/>
|
||||
<div className={styles.body}>{operationElements}</div>
|
||||
{restParam}
|
||||
{index < query.operations.length - 1 && (
|
||||
<div className={styles.arrow}>
|
||||
<div className={styles.arrowLine} />
|
||||
<div className={styles.arrowArrow} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Draggable draggableId={`operation-${index}`} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
@ -153,34 +181,7 @@ export function OperationEditor({
|
||||
invalid={isInvalid(snapshot.isDragging)}
|
||||
className={cx(styles.error, styles.cardWrapper)}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
styles.card,
|
||||
(shouldFlash || highlight) && styles.cardHighlight,
|
||||
isConflicting && styles.cardError
|
||||
)}
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
data-testid={`operations.${index}.wrapper`}
|
||||
>
|
||||
<OperationHeader
|
||||
operation={operation}
|
||||
dragHandleProps={provided.dragHandleProps}
|
||||
def={def}
|
||||
index={index}
|
||||
onChange={onChange}
|
||||
onRemove={onRemove}
|
||||
queryModeller={queryModeller}
|
||||
/>
|
||||
<div className={styles.body}>{operationElements}</div>
|
||||
{restParam}
|
||||
{index < query.operations.length - 1 && (
|
||||
<div className={styles.arrow}>
|
||||
<div className={styles.arrowLine} />
|
||||
<div className={styles.arrowArrow} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<StyledOperationHeader provided={provided} />
|
||||
</InlineField>
|
||||
)}
|
||||
</Draggable>
|
||||
|
Loading…
Reference in New Issue
Block a user