The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/features/explore/Logs/Logs.test.tsx

516 lines
16 KiB

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React, { ComponentProps } from 'react';
import {
DataFrame,
EventBusSrv,
ExploreLogsPanelState,
ExplorePanelsState,
LoadingState,
LogLevel,
LogRowModel,
MutableDataFrame,
standardTransformersRegistry,
toUtc,
} from '@grafana/data';
import { organizeFieldsTransformer } from '@grafana/data/src/transformations/transformers/organize';
import { config } from '@grafana/runtime';
import { extractFieldsTransformer } from 'app/features/transformers/extractFields/extractFields';
import { Logs } from './Logs';
import { getMockElasticFrame, getMockLokiFrame } from './utils/testMocks.test';
const reportInteraction = jest.fn();
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
reportInteraction: (interactionName: string, properties?: Record<string, unknown> | undefined) =>
reportInteraction(interactionName, properties),
}));
const createAndCopyShortLink = jest.fn();
jest.mock('app/core/utils/shortLinks', () => ({
...jest.requireActual('app/core/utils/shortLinks'),
createAndCopyShortLink: (url: string) => createAndCopyShortLink(url),
}));
jest.mock('app/store/store', () => ({
getState: jest.fn().mockReturnValue({
explore: {
panes: {
left: {
datasource: 'id',
queries: [{ refId: 'A', expr: '', queryType: 'range', datasource: { type: 'loki', uid: 'id' } }],
range: { raw: { from: 'now-1h', to: 'now' } },
},
},
},
}),
dispatch: jest.fn(),
}));
const changePanelState = jest.fn();
jest.mock('../state/explorePane', () => ({
...jest.requireActual('../state/explorePane'),
changePanelState: (exploreId: string, panel: 'logs', panelState: {} | ExploreLogsPanelState) => {
return changePanelState(exploreId, panel, panelState);
},
}));
describe('Logs', () => {
let originalHref = window.location.href;
beforeEach(() => {
jest.clearAllMocks();
});
beforeAll(() => {
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost:3000/explore?test',
},
writable: true,
});
});
beforeAll(() => {
const transformers = [extractFieldsTransformer, organizeFieldsTransformer];
standardTransformersRegistry.setInit(() => {
return transformers.map((t) => {
return {
id: t.id,
aliasIds: t.aliasIds,
name: t.name,
transformation: t,
description: t.description,
editor: () => null,
};
});
});
});
afterAll(() => {
Object.defineProperty(window, 'location', {
value: {
href: originalHref,
},
writable: true,
});
});
const getComponent = (
partialProps?: Partial<ComponentProps<typeof Logs>>,
dataFrame?: DataFrame,
logs?: LogRowModel[]
) => {
const rows = [
makeLog({ uid: '1', rowId: 'id1', timeEpochMs: 1 }),
makeLog({ uid: '2', rowId: 'id2', timeEpochMs: 2 }),
makeLog({ uid: '3', rowId: 'id3', timeEpochMs: 3 }),
];
const testDataFrame = dataFrame ?? getMockLokiFrame();
return (
<Logs
exploreId={'left'}
splitOpen={() => undefined}
logsVolumeEnabled={true}
onSetLogsVolumeEnabled={() => null}
onClickFilterLabel={() => null}
onClickFilterOutLabel={() => null}
logsVolumeData={undefined}
loadLogsVolumeData={() => undefined}
logRows={logs ?? rows}
timeZone={'utc'}
width={50}
loading={false}
loadingState={LoadingState.Done}
absoluteRange={{
from: toUtc('2019-01-01 10:00:00').valueOf(),
to: toUtc('2019-01-01 16:00:00').valueOf(),
}}
range={{
from: toUtc('2019-01-01 10:00:00'),
to: toUtc('2019-01-01 16:00:00'),
raw: { from: 'now-1h', to: 'now' },
}}
addResultsToCache={() => {}}
onChangeTime={() => {}}
clearCache={() => {}}
getFieldLinks={() => {
return [];
}}
eventBus={new EventBusSrv()}
Logs: Show active state of "filter for value" buttons in Logs Details (#70328) * Datasource test: fix describe nesting * Parsing: export handleQuotes function * Modify query: add functions to detect the presence of a label and remove it * Loki: add support to toggle filters if already present * Datasource test: fix describe nesting * Loki: add support to toggle filter out if present * Remove label: handle escaped values * Datasource: add test case for escaped label values * Loki: remove = filter when applying != * Remove selector: add support for Selector node being far from Matcher * Modify query: add unit tests * Elasticsearch: create modifyQuery for elastic * Elastic modify query: implement functions * Elasticsearch: implement modifyQuery functions in datasource * Elasticsearch: update datasource test * Loki modify query: check for streamSelectorPositions length * Elasticsearch query has filter: escape filter value in regex * Remove unused type * Modify query: add functions to detect the presence of a label and remove it * Remove label: handle escaped values * Logs: create props to check for label filters in the query * Log Details Row: use label state props to show visual feedback * Make isCallbacks async * Explore: add placeholder for checking for filter in query * Datasource: define new API method * Inspect query: add base implementation * Remove isFilterOutLabelActive as it will not be needed * Check for "isActive" on every render Otherwise the active state will be out of sync * Elasticsearch: implement inspectQuery in the datasource * Logs: update test * Log details: update test * Datasources: update tests * Inspect query: rename to analize query to prevent confusion * Datasource types: mark method as alpha * Explore: add comment to log-specific functions * Remove duplicated code from bad rebase * Remove label filter: check node type * getMatchersWithFilter: rename argument * Fix bad rebase * Create DataSourceWithQueryManipulationSupport interface * Implement type guard for DataSourceWithQueryManipulationSupport * DataSourceWithQueryManipulationSupport: move to logs module * hasQueryManipulationSupport: change implementation `modifyQuery` comes from the prototype. * DataSourceWithQueryManipulationSupport: expand code comments * AnalyzeQueryOptions: move to logs module * DataSourceWithQueryManipulationSupport: add support for more return types * Fix merge error * Update packages/grafana-data/src/types/logs.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * DatasourceAPI: deprecate modifyQuery * Explore: refactor isFilterLabelActive * DataSourceWithQueryModificationSupport: rename interface * Split interfaces into Analyze and Modify * Query analysis: better name for interface * Fix guard * Create feature flag for active state * Use new feature flag in Explore * DataSourceToggleableQueryFiltersSupport: create a specific interface for this feature * Rename feature flag * De-deprecate modifyQuery * DataSourceToggleableQueryFiltersSupport: Rethink types and methods * Explore: adjust modifyQuery and isFilterLabelActive to new methods * Loki: implement new interface and revert modifyQuery * DataSourceToggleableQueryFiltersSupport: better name for arguments * Elasticsearch: implement new interface and revert modifyQuery * Loki: better name for arguments * Explore: document current limitation on isFilterLabelActive * Explore: place toggleable filters under feature flag * Loki: add tests for the new methods * Loki: add legacy modifyQuery tests * Elasticsearch: add tests for the new methods * Elasticsearch: add legacy modifyQuery tests * Toggle filter action: improve type values * Logs types: update interface description * DataSourceWithToggleableQueryFiltersSupport: update interface name * Update feature flag description * Explore: add todo comment for isFilterLabelActive --------- Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com>
2 years ago
isFilterLabelActive={jest.fn()}
logsFrames={[testDataFrame]}
{...partialProps}
/>
);
};
const setup = (partialProps?: Partial<ComponentProps<typeof Logs>>, dataFrame?: DataFrame, logs?: LogRowModel[]) => {
return render(getComponent(partialProps, dataFrame ? dataFrame : getMockLokiFrame(), logs));
};
describe('scrolling behavior', () => {
let originalInnerHeight: number;
beforeEach(() => {
originalInnerHeight = window.innerHeight;
window.innerHeight = 1000;
window.HTMLElement.prototype.scrollIntoView = jest.fn();
window.HTMLElement.prototype.scroll = jest.fn();
});
afterEach(() => {
window.innerHeight = originalInnerHeight;
});
describe('when `exploreScrollableLogsContainer` is set', () => {
let featureToggle: boolean | undefined;
beforeEach(() => {
featureToggle = config.featureToggles.exploreScrollableLogsContainer;
config.featureToggles.exploreScrollableLogsContainer = true;
});
afterEach(() => {
config.featureToggles.exploreScrollableLogsContainer = featureToggle;
jest.clearAllMocks();
});
it('should call `this.state.logsContainer.scroll`', () => {
const scrollIntoViewSpy = jest.spyOn(window.HTMLElement.prototype, 'scrollIntoView');
jest.spyOn(window.HTMLElement.prototype, 'scrollTop', 'get').mockReturnValue(920);
const scrollSpy = jest.spyOn(window.HTMLElement.prototype, 'scroll');
const logs = [];
for (let i = 0; i < 50; i++) {
logs.push(makeLog({ uid: `uid${i}`, rowId: `id${i}`, timeEpochMs: i }));
}
setup({ panelState: { logs: { id: 'uid47' } } }, undefined, logs);
expect(scrollIntoViewSpy).toBeCalledTimes(1);
// element.getBoundingClientRect().top will always be 0 for jsdom
// calc will be `this.state.logsContainer.scrollTop - window.innerHeight / 2` -> 920 - 500 = 420
expect(scrollSpy).toBeCalledWith({ behavior: 'smooth', top: 420 });
});
});
describe('when `exploreScrollableLogsContainer` is not set', () => {
let featureToggle: boolean | undefined;
beforeEach(() => {
featureToggle = config.featureToggles.exploreScrollableLogsContainer;
config.featureToggles.exploreScrollableLogsContainer = false;
});
afterEach(() => {
config.featureToggles.exploreScrollableLogsContainer = featureToggle;
});
it('should call `scrollElement.scroll`', () => {
const logs = [];
for (let i = 0; i < 50; i++) {
logs.push(makeLog({ uid: `uid${i}`, rowId: `id${i}`, timeEpochMs: i }));
}
const scrollElementMock = {
scroll: jest.fn(),
scrollTop: 920,
};
setup(
{ scrollElement: scrollElementMock as unknown as HTMLDivElement, panelState: { logs: { id: 'uid47' } } },
undefined,
logs
);
// element.getBoundingClientRect().top will always be 0 for jsdom
// calc will be `scrollElement.scrollTop - window.innerHeight / 2` -> 920 - 500 = 420
expect(scrollElementMock.scroll).toBeCalledWith({ behavior: 'smooth', top: 420 });
});
});
});
it('should render logs', () => {
setup();
const logsSection = screen.getByTestId('logRows');
let logRows = logsSection.querySelectorAll('tr');
expect(logRows.length).toBe(3);
expect(logRows[0].textContent).toContain('log message 3');
expect(logRows[2].textContent).toContain('log message 1');
});
it('should render no logs found', () => {
setup({}, undefined, []);
expect(screen.getByText(/no logs found\./i)).toBeInTheDocument();
expect(
screen.getByRole('button', {
name: /scan for older logs/i,
})
).toBeInTheDocument();
});
it('should render a load more button', () => {
const scanningStarted = jest.fn();
render(
<Logs
exploreId={'left'}
splitOpen={() => undefined}
logsVolumeEnabled={true}
onSetLogsVolumeEnabled={() => null}
onClickFilterLabel={() => null}
onClickFilterOutLabel={() => null}
logsVolumeData={undefined}
loadLogsVolumeData={() => undefined}
logRows={[]}
onStartScanning={scanningStarted}
timeZone={'utc'}
width={50}
loading={false}
loadingState={LoadingState.Done}
absoluteRange={{
from: toUtc('2019-01-01 10:00:00').valueOf(),
to: toUtc('2019-01-01 16:00:00').valueOf(),
}}
range={{
from: toUtc('2019-01-01 10:00:00'),
to: toUtc('2019-01-01 16:00:00'),
raw: { from: 'now-1h', to: 'now' },
}}
addResultsToCache={() => {}}
onChangeTime={() => {}}
clearCache={() => {}}
getFieldLinks={() => {
return [];
}}
eventBus={new EventBusSrv()}
Logs: Show active state of "filter for value" buttons in Logs Details (#70328) * Datasource test: fix describe nesting * Parsing: export handleQuotes function * Modify query: add functions to detect the presence of a label and remove it * Loki: add support to toggle filters if already present * Datasource test: fix describe nesting * Loki: add support to toggle filter out if present * Remove label: handle escaped values * Datasource: add test case for escaped label values * Loki: remove = filter when applying != * Remove selector: add support for Selector node being far from Matcher * Modify query: add unit tests * Elasticsearch: create modifyQuery for elastic * Elastic modify query: implement functions * Elasticsearch: implement modifyQuery functions in datasource * Elasticsearch: update datasource test * Loki modify query: check for streamSelectorPositions length * Elasticsearch query has filter: escape filter value in regex * Remove unused type * Modify query: add functions to detect the presence of a label and remove it * Remove label: handle escaped values * Logs: create props to check for label filters in the query * Log Details Row: use label state props to show visual feedback * Make isCallbacks async * Explore: add placeholder for checking for filter in query * Datasource: define new API method * Inspect query: add base implementation * Remove isFilterOutLabelActive as it will not be needed * Check for "isActive" on every render Otherwise the active state will be out of sync * Elasticsearch: implement inspectQuery in the datasource * Logs: update test * Log details: update test * Datasources: update tests * Inspect query: rename to analize query to prevent confusion * Datasource types: mark method as alpha * Explore: add comment to log-specific functions * Remove duplicated code from bad rebase * Remove label filter: check node type * getMatchersWithFilter: rename argument * Fix bad rebase * Create DataSourceWithQueryManipulationSupport interface * Implement type guard for DataSourceWithQueryManipulationSupport * DataSourceWithQueryManipulationSupport: move to logs module * hasQueryManipulationSupport: change implementation `modifyQuery` comes from the prototype. * DataSourceWithQueryManipulationSupport: expand code comments * AnalyzeQueryOptions: move to logs module * DataSourceWithQueryManipulationSupport: add support for more return types * Fix merge error * Update packages/grafana-data/src/types/logs.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * DatasourceAPI: deprecate modifyQuery * Explore: refactor isFilterLabelActive * DataSourceWithQueryModificationSupport: rename interface * Split interfaces into Analyze and Modify * Query analysis: better name for interface * Fix guard * Create feature flag for active state * Use new feature flag in Explore * DataSourceToggleableQueryFiltersSupport: create a specific interface for this feature * Rename feature flag * De-deprecate modifyQuery * DataSourceToggleableQueryFiltersSupport: Rethink types and methods * Explore: adjust modifyQuery and isFilterLabelActive to new methods * Loki: implement new interface and revert modifyQuery * DataSourceToggleableQueryFiltersSupport: better name for arguments * Elasticsearch: implement new interface and revert modifyQuery * Loki: better name for arguments * Explore: document current limitation on isFilterLabelActive * Explore: place toggleable filters under feature flag * Loki: add tests for the new methods * Loki: add legacy modifyQuery tests * Elasticsearch: add tests for the new methods * Elasticsearch: add legacy modifyQuery tests * Toggle filter action: improve type values * Logs types: update interface description * DataSourceWithToggleableQueryFiltersSupport: update interface name * Update feature flag description * Explore: add todo comment for isFilterLabelActive --------- Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com>
2 years ago
isFilterLabelActive={jest.fn()}
/>
);
const button = screen.getByRole('button', {
name: /scan for older logs/i,
});
button.click();
expect(scanningStarted).toHaveBeenCalled();
});
it('should render a stop scanning button', () => {
render(
<Logs
exploreId={'left'}
splitOpen={() => undefined}
logsVolumeEnabled={true}
onSetLogsVolumeEnabled={() => null}
onClickFilterLabel={() => null}
onClickFilterOutLabel={() => null}
logsVolumeData={undefined}
loadLogsVolumeData={() => undefined}
logRows={[]}
scanning={true}
timeZone={'utc'}
width={50}
loading={false}
loadingState={LoadingState.Done}
absoluteRange={{
from: toUtc('2019-01-01 10:00:00').valueOf(),
to: toUtc('2019-01-01 16:00:00').valueOf(),
}}
range={{
from: toUtc('2019-01-01 10:00:00'),
to: toUtc('2019-01-01 16:00:00'),
raw: { from: 'now-1h', to: 'now' },
}}
addResultsToCache={() => {}}
onChangeTime={() => {}}
clearCache={() => {}}
getFieldLinks={() => {
return [];
}}
eventBus={new EventBusSrv()}
Logs: Show active state of "filter for value" buttons in Logs Details (#70328) * Datasource test: fix describe nesting * Parsing: export handleQuotes function * Modify query: add functions to detect the presence of a label and remove it * Loki: add support to toggle filters if already present * Datasource test: fix describe nesting * Loki: add support to toggle filter out if present * Remove label: handle escaped values * Datasource: add test case for escaped label values * Loki: remove = filter when applying != * Remove selector: add support for Selector node being far from Matcher * Modify query: add unit tests * Elasticsearch: create modifyQuery for elastic * Elastic modify query: implement functions * Elasticsearch: implement modifyQuery functions in datasource * Elasticsearch: update datasource test * Loki modify query: check for streamSelectorPositions length * Elasticsearch query has filter: escape filter value in regex * Remove unused type * Modify query: add functions to detect the presence of a label and remove it * Remove label: handle escaped values * Logs: create props to check for label filters in the query * Log Details Row: use label state props to show visual feedback * Make isCallbacks async * Explore: add placeholder for checking for filter in query * Datasource: define new API method * Inspect query: add base implementation * Remove isFilterOutLabelActive as it will not be needed * Check for "isActive" on every render Otherwise the active state will be out of sync * Elasticsearch: implement inspectQuery in the datasource * Logs: update test * Log details: update test * Datasources: update tests * Inspect query: rename to analize query to prevent confusion * Datasource types: mark method as alpha * Explore: add comment to log-specific functions * Remove duplicated code from bad rebase * Remove label filter: check node type * getMatchersWithFilter: rename argument * Fix bad rebase * Create DataSourceWithQueryManipulationSupport interface * Implement type guard for DataSourceWithQueryManipulationSupport * DataSourceWithQueryManipulationSupport: move to logs module * hasQueryManipulationSupport: change implementation `modifyQuery` comes from the prototype. * DataSourceWithQueryManipulationSupport: expand code comments * AnalyzeQueryOptions: move to logs module * DataSourceWithQueryManipulationSupport: add support for more return types * Fix merge error * Update packages/grafana-data/src/types/logs.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * DatasourceAPI: deprecate modifyQuery * Explore: refactor isFilterLabelActive * DataSourceWithQueryModificationSupport: rename interface * Split interfaces into Analyze and Modify * Query analysis: better name for interface * Fix guard * Create feature flag for active state * Use new feature flag in Explore * DataSourceToggleableQueryFiltersSupport: create a specific interface for this feature * Rename feature flag * De-deprecate modifyQuery * DataSourceToggleableQueryFiltersSupport: Rethink types and methods * Explore: adjust modifyQuery and isFilterLabelActive to new methods * Loki: implement new interface and revert modifyQuery * DataSourceToggleableQueryFiltersSupport: better name for arguments * Elasticsearch: implement new interface and revert modifyQuery * Loki: better name for arguments * Explore: document current limitation on isFilterLabelActive * Explore: place toggleable filters under feature flag * Loki: add tests for the new methods * Loki: add legacy modifyQuery tests * Elasticsearch: add tests for the new methods * Elasticsearch: add legacy modifyQuery tests * Toggle filter action: improve type values * Logs types: update interface description * DataSourceWithToggleableQueryFiltersSupport: update interface name * Update feature flag description * Explore: add todo comment for isFilterLabelActive --------- Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com>
2 years ago
isFilterLabelActive={jest.fn()}
/>
);
expect(
screen.getByRole('button', {
name: /stop scan/i,
})
).toBeInTheDocument();
});
it('should render a stop scanning button', () => {
const scanningStopped = jest.fn();
render(
<Logs
exploreId={'left'}
splitOpen={() => undefined}
logsVolumeEnabled={true}
onSetLogsVolumeEnabled={() => null}
onClickFilterLabel={() => null}
onClickFilterOutLabel={() => null}
logsVolumeData={undefined}
loadLogsVolumeData={() => undefined}
logRows={[]}
scanning={true}
onStopScanning={scanningStopped}
timeZone={'utc'}
width={50}
loading={false}
loadingState={LoadingState.Done}
absoluteRange={{
from: toUtc('2019-01-01 10:00:00').valueOf(),
to: toUtc('2019-01-01 16:00:00').valueOf(),
}}
range={{
from: toUtc('2019-01-01 10:00:00'),
to: toUtc('2019-01-01 16:00:00'),
raw: { from: 'now-1h', to: 'now' },
}}
addResultsToCache={() => {}}
onChangeTime={() => {}}
clearCache={() => {}}
getFieldLinks={() => {
return [];
}}
eventBus={new EventBusSrv()}
Logs: Show active state of "filter for value" buttons in Logs Details (#70328) * Datasource test: fix describe nesting * Parsing: export handleQuotes function * Modify query: add functions to detect the presence of a label and remove it * Loki: add support to toggle filters if already present * Datasource test: fix describe nesting * Loki: add support to toggle filter out if present * Remove label: handle escaped values * Datasource: add test case for escaped label values * Loki: remove = filter when applying != * Remove selector: add support for Selector node being far from Matcher * Modify query: add unit tests * Elasticsearch: create modifyQuery for elastic * Elastic modify query: implement functions * Elasticsearch: implement modifyQuery functions in datasource * Elasticsearch: update datasource test * Loki modify query: check for streamSelectorPositions length * Elasticsearch query has filter: escape filter value in regex * Remove unused type * Modify query: add functions to detect the presence of a label and remove it * Remove label: handle escaped values * Logs: create props to check for label filters in the query * Log Details Row: use label state props to show visual feedback * Make isCallbacks async * Explore: add placeholder for checking for filter in query * Datasource: define new API method * Inspect query: add base implementation * Remove isFilterOutLabelActive as it will not be needed * Check for "isActive" on every render Otherwise the active state will be out of sync * Elasticsearch: implement inspectQuery in the datasource * Logs: update test * Log details: update test * Datasources: update tests * Inspect query: rename to analize query to prevent confusion * Datasource types: mark method as alpha * Explore: add comment to log-specific functions * Remove duplicated code from bad rebase * Remove label filter: check node type * getMatchersWithFilter: rename argument * Fix bad rebase * Create DataSourceWithQueryManipulationSupport interface * Implement type guard for DataSourceWithQueryManipulationSupport * DataSourceWithQueryManipulationSupport: move to logs module * hasQueryManipulationSupport: change implementation `modifyQuery` comes from the prototype. * DataSourceWithQueryManipulationSupport: expand code comments * AnalyzeQueryOptions: move to logs module * DataSourceWithQueryManipulationSupport: add support for more return types * Fix merge error * Update packages/grafana-data/src/types/logs.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * DatasourceAPI: deprecate modifyQuery * Explore: refactor isFilterLabelActive * DataSourceWithQueryModificationSupport: rename interface * Split interfaces into Analyze and Modify * Query analysis: better name for interface * Fix guard * Create feature flag for active state * Use new feature flag in Explore * DataSourceToggleableQueryFiltersSupport: create a specific interface for this feature * Rename feature flag * De-deprecate modifyQuery * DataSourceToggleableQueryFiltersSupport: Rethink types and methods * Explore: adjust modifyQuery and isFilterLabelActive to new methods * Loki: implement new interface and revert modifyQuery * DataSourceToggleableQueryFiltersSupport: better name for arguments * Elasticsearch: implement new interface and revert modifyQuery * Loki: better name for arguments * Explore: document current limitation on isFilterLabelActive * Explore: place toggleable filters under feature flag * Loki: add tests for the new methods * Loki: add legacy modifyQuery tests * Elasticsearch: add tests for the new methods * Elasticsearch: add legacy modifyQuery tests * Toggle filter action: improve type values * Logs types: update interface description * DataSourceWithToggleableQueryFiltersSupport: update interface name * Update feature flag description * Explore: add todo comment for isFilterLabelActive --------- Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com>
2 years ago
isFilterLabelActive={jest.fn()}
/>
);
const button = screen.getByRole('button', {
name: /stop scan/i,
});
button.click();
expect(scanningStopped).toHaveBeenCalled();
});
it('should flip the order', async () => {
setup();
const oldestFirstSelection = screen.getByLabelText('Oldest first');
await userEvent.click(oldestFirstSelection);
const logsSection = screen.getByTestId('logRows');
let logRows = logsSection.querySelectorAll('tr');
expect(logRows.length).toBe(3);
expect(logRows[0].textContent).toContain('log message 1');
expect(logRows[2].textContent).toContain('log message 3');
});
describe('for permalinking', () => {
it('should dispatch a `changePanelState` event without the id', () => {
const panelState = { logs: { id: '1' } };
const { rerender } = setup({ loading: false, panelState });
rerender(getComponent({ loading: true, exploreId: 'right', panelState }));
rerender(getComponent({ loading: false, exploreId: 'right', panelState }));
expect(changePanelState).toHaveBeenCalledWith('right', 'logs', { logs: {} });
});
it('should scroll the scrollElement into view if rows contain id', () => {
const panelState = { logs: { id: '3' } };
const scrollElementMock = { scroll: jest.fn() };
setup({ loading: false, scrollElement: scrollElementMock as unknown as HTMLDivElement, panelState });
expect(scrollElementMock.scroll).toHaveBeenCalled();
});
it('should not scroll the scrollElement into view if rows does not contain id', () => {
const panelState = { logs: { id: 'not-included' } };
const scrollElementMock = { scroll: jest.fn() };
setup({ loading: false, scrollElement: scrollElementMock as unknown as HTMLDivElement, panelState });
expect(scrollElementMock.scroll).not.toHaveBeenCalled();
});
it('should call reportInteraction on permalinkClick', async () => {
const panelState = { logs: { id: 'not-included' } };
setup({ loading: false, panelState });
const row = screen.getAllByRole('row');
await userEvent.hover(row[0]);
const linkButton = screen.getByLabelText('Copy shortlink');
await userEvent.click(linkButton);
expect(reportInteraction).toHaveBeenCalledWith('grafana_explore_logs_permalink_clicked', {
datasourceType: 'unknown',
logRowUid: '1',
logRowLevel: 'debug',
});
});
it('should call createAndCopyShortLink on permalinkClick - logs', async () => {
const panelState: Partial<ExplorePanelsState> = { logs: { id: 'not-included', visualisationType: 'logs' } };
setup({ loading: false, panelState });
const row = screen.getAllByRole('row');
await userEvent.hover(row[0]);
const linkButton = screen.getByLabelText('Copy shortlink');
await userEvent.click(linkButton);
expect(createAndCopyShortLink).toHaveBeenCalledWith(
expect.stringMatching(
'range%22:%7B%22from%22:%222019-01-01T10:00:00.000Z%22,%22to%22:%222019-01-01T16:00:00.000Z%22%7D'
)
);
expect(createAndCopyShortLink).toHaveBeenCalledWith(expect.stringMatching('visualisationType%22:%22logs'));
});
});
describe('with table visualisation', () => {
let originalVisualisationTypeValue = config.featureToggles.logsExploreTableVisualisation;
beforeAll(() => {
originalVisualisationTypeValue = config.featureToggles.logsExploreTableVisualisation;
config.featureToggles.logsExploreTableVisualisation = true;
});
afterAll(() => {
config.featureToggles.logsExploreTableVisualisation = originalVisualisationTypeValue;
});
it('should show visualisation type radio group', () => {
setup();
const logsSection = screen.getByRole('radio', { name: 'Show results in table visualisation' });
expect(logsSection).toBeInTheDocument();
});
it('should change visualisation to table on toggle (loki)', async () => {
setup({});
const logsSection = screen.getByRole('radio', { name: 'Show results in table visualisation' });
await userEvent.click(logsSection);
const table = screen.getByTestId('logRowsTable');
expect(table).toBeInTheDocument();
});
it('should change visualisation to table on toggle (elastic)', async () => {
setup({}, getMockElasticFrame());
const logsSection = screen.getByRole('radio', { name: 'Show results in table visualisation' });
await userEvent.click(logsSection);
const table = screen.getByTestId('logRowsTable');
expect(table).toBeInTheDocument();
});
});
});
const makeLog = (overrides: Partial<LogRowModel>): LogRowModel => {
const uid = overrides.uid || '1';
const entry = `log message ${uid}`;
return {
uid,
entryFieldIndex: 0,
rowIndex: 0,
dataFrame: new MutableDataFrame(),
logLevel: LogLevel.debug,
entry,
hasAnsi: false,
hasUnescapedContent: false,
labels: {},
raw: entry,
timeFromNow: '',
timeEpochMs: 1,
timeEpochNs: '1000000',
timeLocal: '',
timeUtc: '',
...overrides,
};
};