123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- import { fireEvent, screen, waitFor } from '@testing-library/react';
- import userEvent from '@testing-library/user-event';
- import React from 'react';
- import { serializeStateToUrlParam } from '@grafana/data';
- import { locationService } from '@grafana/runtime';
- import { changeDatasource } from './spec/helper/interactions';
- import { makeLogsQueryResponse, makeMetricsQueryResponse } from './spec/helper/query';
- import { setupExplore, tearDown, waitForExplore } from './spec/helper/setup';
- import { splitOpen } from './state/main';
- type Mock = jest.Mock;
- jest.mock('app/core/core', () => {
- return {
- contextSrv: {
- hasPermission: () => true,
- },
- appEvents: {
- subscribe: () => {},
- publish: () => {},
- },
- };
- });
- jest.mock('react-virtualized-auto-sizer', () => {
- return {
- __esModule: true,
- default(props: any) {
- return <div>{props.children({ width: 1000 })}</div>;
- },
- };
- });
- describe('Wrapper', () => {
- afterEach(() => {
- tearDown();
- });
- it('shows warning if there are no data sources', async () => {
- setupExplore({ datasources: [] });
- // Will throw if isn't found
- screen.getByText(/Explore requires at least one data source/i);
- });
- it('inits url and renders editor but does not call query on empty url', async () => {
- const { datasources } = setupExplore();
- await waitForExplore();
- // At this point url should be initialised to some defaults
- expect(locationService.getSearchObject()).toEqual({
- orgId: '1',
- left: serializeStateToUrlParam({
- datasource: 'loki',
- queries: [{ refId: 'A' }],
- range: { from: 'now-1h', to: 'now' },
- }),
- });
- expect(datasources.loki.query).not.toBeCalled();
- });
- it('runs query when url contains query and renders results', async () => {
- const urlParams = {
- left: serializeStateToUrlParam({
- datasource: 'loki',
- queries: [{ refId: 'A', expr: '{ label="value"}' }],
- range: { from: 'now-1h', to: 'now' },
- }),
- };
- const { datasources } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
- // Make sure we render the logs panel
- await screen.findByText(/^Logs$/);
- // Make sure we render the log line
- await screen.findByText(/custom log line/i);
- // And that the editor gets the expr from the url
- await screen.findByText(`loki Editor input: { label="value"}`);
- // We did not change the url
- expect(locationService.getSearchObject()).toEqual({
- orgId: '1',
- ...urlParams,
- });
- // We called the data source query method once
- expect(datasources.loki.query).toBeCalledTimes(1);
- expect((datasources.loki.query as Mock).mock.calls[0][0]).toMatchObject({
- targets: [{ expr: '{ label="value"}' }],
- });
- });
- it('handles url change and runs the new query', async () => {
- const urlParams = { left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]) };
- const { datasources } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
- // Wait for rendering the logs
- await screen.findByText(/custom log line/i);
- (datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse('different log'));
- locationService.partial({
- left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="different"}' }]),
- });
- // Editor renders the new query
- await screen.findByText(`loki Editor input: { label="different"}`);
- // Renders new response
- await screen.findByText(/different log/i);
- });
- it('handles url change and runs the new query with different datasource', async () => {
- const urlParams = { left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]) };
- const { datasources } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
- // Wait for rendering the logs
- await screen.findByText(/custom log line/i);
- await screen.findByText(`loki Editor input: { label="value"}`);
- (datasources.elastic.query as Mock).mockReturnValueOnce(makeMetricsQueryResponse());
- locationService.partial({
- left: JSON.stringify(['now-1h', 'now', 'elastic', { expr: 'other query' }]),
- });
- // Editor renders the new query
- await screen.findByText(`elastic Editor input: other query`);
- // Renders graph
- await screen.findByText(/Graph/i);
- });
- it('handles changing the datasource manually', async () => {
- const urlParams = { left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}', refId: 'A' }]) };
- const { datasources } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
- await waitForExplore();
- await changeDatasource('elastic');
- await screen.findByText('elastic Editor input:');
- expect(datasources.elastic.query).not.toBeCalled();
- expect(locationService.getSearchObject()).toEqual({
- orgId: '1',
- left: serializeStateToUrlParam({
- datasource: 'elastic',
- queries: [{ refId: 'A' }],
- range: { from: 'now-1h', to: 'now' },
- }),
- });
- });
- it('opens the split pane when split button is clicked', async () => {
- setupExplore();
- // Wait for rendering the editor
- const splitButton = await screen.findByText(/split/i);
- fireEvent.click(splitButton);
- await waitFor(() => {
- const editors = screen.getAllByText('loki Editor input:');
- expect(editors.length).toBe(2);
- });
- });
- it('inits with two panes if specified in url', async () => {
- const urlParams = {
- left: serializeStateToUrlParam({
- datasource: 'loki',
- queries: [{ refId: 'A', expr: '{ label="value"}' }],
- range: { from: 'now-1h', to: 'now' },
- }),
- right: serializeStateToUrlParam({
- datasource: 'elastic',
- queries: [{ refId: 'A', expr: 'error' }],
- range: { from: 'now-1h', to: 'now' },
- }),
- };
- const { datasources } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
- (datasources.elastic.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
- // Make sure we render the logs panel
- await waitFor(() => {
- const logsPanels = screen.getAllByText(/^Logs$/);
- expect(logsPanels.length).toBe(2);
- });
- // Make sure we render the log line
- const logsLines = await screen.findAllByText(/custom log line/i);
- expect(logsLines.length).toBe(2);
- // And that the editor gets the expr from the url
- await screen.findByText(`loki Editor input: { label="value"}`);
- await screen.findByText(`elastic Editor input: error`);
- // We did not change the url
- expect(locationService.getSearchObject()).toEqual({
- orgId: '1',
- ...urlParams,
- });
- // We called the data source query method once
- expect(datasources.loki.query).toBeCalledTimes(1);
- expect((datasources.loki.query as Mock).mock.calls[0][0]).toMatchObject({
- targets: [{ expr: '{ label="value"}' }],
- });
- expect(datasources.elastic.query).toBeCalledTimes(1);
- expect((datasources.elastic.query as Mock).mock.calls[0][0]).toMatchObject({
- targets: [{ expr: 'error' }],
- });
- });
- it('can close a pane from a split', async () => {
- const urlParams = {
- left: JSON.stringify(['now-1h', 'now', 'loki', { refId: 'A' }]),
- right: JSON.stringify(['now-1h', 'now', 'elastic', { refId: 'A' }]),
- };
- setupExplore({ urlParams });
- const closeButtons = await screen.findAllByTitle(/Close split pane/i);
- await userEvent.click(closeButtons[1]);
- await waitFor(() => {
- const logsPanels = screen.queryAllByTitle(/Close split pane/i);
- expect(logsPanels.length).toBe(0);
- });
- });
- it('handles url change to split view', async () => {
- const urlParams = {
- left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
- };
- const { datasources } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValue(makeLogsQueryResponse());
- (datasources.elastic.query as Mock).mockReturnValue(makeLogsQueryResponse());
- locationService.partial({
- left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
- right: JSON.stringify(['now-1h', 'now', 'elastic', { expr: 'error' }]),
- });
- // Editor renders the new query
- await screen.findByText(`loki Editor input: { label="value"}`);
- await screen.findByText(`elastic Editor input: error`);
- });
- it('handles opening split with split open func', async () => {
- const urlParams = {
- left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
- };
- const { datasources, store } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValue(makeLogsQueryResponse());
- (datasources.elastic.query as Mock).mockReturnValue(makeLogsQueryResponse());
- // This is mainly to wait for render so that the left pane state is initialized as that is needed for splitOpen
- // to work
- await screen.findByText(`loki Editor input: { label="value"}`);
- store.dispatch(splitOpen<any>({ datasourceUid: 'elastic', query: { expr: 'error' } }) as any);
- // Editor renders the new query
- await screen.findByText(`elastic Editor input: error`);
- await screen.findByText(`loki Editor input: { label="value"}`);
- });
- it('changes the document title of the explore page to include the datasource in use', async () => {
- const urlParams = {
- left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
- };
- const { datasources } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValue(makeLogsQueryResponse());
- // This is mainly to wait for render so that the left pane state is initialized as that is needed for the title
- // to include the datasource
- await screen.findByText(`loki Editor input: { label="value"}`);
- await waitFor(() => expect(document.title).toEqual('Explore - loki - Grafana'));
- });
- it('changes the document title to include the two datasources in use in split view mode', async () => {
- const urlParams = {
- left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
- };
- const { datasources, store } = setupExplore({ urlParams });
- (datasources.loki.query as Mock).mockReturnValue(makeLogsQueryResponse());
- (datasources.elastic.query as Mock).mockReturnValue(makeLogsQueryResponse());
- // This is mainly to wait for render so that the left pane state is initialized as that is needed for splitOpen
- // to work
- await screen.findByText(`loki Editor input: { label="value"}`);
- store.dispatch(splitOpen<any>({ datasourceUid: 'elastic', query: { expr: 'error' } }) as any);
- await waitFor(() => expect(document.title).toEqual('Explore - loki | elastic - Grafana'));
- });
- it('removes `from` and `to` parameters from url when first mounted', async () => {
- setupExplore({ searchParams: 'from=1&to=2&orgId=1' });
- expect(locationService.getSearchObject()).toEqual(expect.not.objectContaining({ from: '1', to: '2' }));
- expect(locationService.getSearchObject()).toEqual(expect.objectContaining({ orgId: '1' }));
- });
- });
|