import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { CoreApp, EventBusSrv, LogLevel, LogsDedupStrategy, LogsSortOrder } from '@grafana/data';

import { downloadLogs } from '../../utils';
import { createLogLine, createLogRow } from '../mocks/logRow';

import { type LogListFontSize } from './LogList';
import { LogListContextProvider } from './LogListContext';
import { LogListControls } from './LogListControls';
import { ScrollToLogsEvent } from './virtualization';

const FILTER_LEVELS_LABEL_COPY = 'Filter levels';
const SCROLL_BOTTOM_LABEL_COPY = 'Scroll to bottom';
const SCROLL_TOP_LABEL_COPY = 'Scroll to top';
const OLDEST_LOGS_LABEL_COPY = 'Oldest logs first';
const DEDUPE_LABEL_COPY = 'Deduplication';
const SHOW_TIMESTAMP_LABEL_COPY = 'Show timestamps';
const WRAP_LINES_LABEL_COPY = 'Wrap lines';
const ENABLE_UNWRAPPED_COLUMNS_COPY = 'Enable columns';
const DISABLE_UNWRAPPED_COLUMNS_COPY = 'Disable columns';
const COLUMNS_NOT_SUPPORTED_COPY = 'Columns not supported';
const COLUMNS_ENABLED_COPY = 'Columns enabled';
const COLUMNS_DISABLED_COPY = 'Columns disabled';
const COLUMNS_DISABLED_TOOLTIP_COPY = 'Columns are not supported with line wrapping enabled';
const WRAP_JSON_TOOLTIP_COPY = 'Enable line wrapping and prettify JSON';
const WRAP_JSON_LABEL_COPY = 'Wrap JSON';
const WRAP_DISABLE_LABEL_COPY = 'Disable line wrapping';
const ENABLE_HIGHLIGHTING_LABEL_COPY = 'Enable highlighting';
const EXPANDED_LABEL_COPY = 'Expanded';
const COLLAPSED_LABEL_COPY = 'Collapsed';
const SHOW_UNIQUE_LABELS_LABEL_COPY = 'Show unique labels';
const HIDE_UNIQUE_LABELS_LABEL_COPY = 'Hide unique labels';
const EXPAND_JSON_LOGS_LABEL_COPY = 'Expand JSON logs';
const COLLAPSE_JSON_LOGS_LABEL_COPY = 'Collapse JSON logs';
const ESCAPE_NEWLINES_TOOLTIP_COPY = 'Fix incorrectly escaped newline and tab sequences in log lines';
const REMOVE_ESCAPE_NEWLINES_LABEL_COPY = 'Remove escaping';
const TIMESTAMP_LABEL_COPY = 'Log timestamps';
const TIMESTAMP_HIDE_LABEL_COPY = 'Hide timestamps';
const FONT_SIZE_LARGE_LABEL_COPY = 'Large font';
const FONT_SIZE_LARGE_TOOLTIP_COPY = 'Set large font';
const FONT_SIZE_SMALL_LABEL_COPY = 'Small font';
const FONT_SIZE_SMALL_TOOLTIP_COPY = 'Set small font';
const DOWNLOAD_LOGS_LABEL_COPY = 'Download logs';

const OLDEST_LOGS_LABEL_REGEX = /oldest logs first/;

jest.mock('../../utils', () => ({
  ...jest.requireActual('../../utils'),
  downloadLogs: jest.fn(),
}));

jest.mock('@grafana/assistant', () => {
  return {
    ...jest.requireActual('@grafana/assistant'),
    useAssistant: jest.fn().mockReturnValue({
      isLoading: false,
      isAvailable: true,
    }),
  };
});

const useBooleanFlagValueMock = jest.fn((_: string, defaultValue: boolean) => defaultValue);

const setBooleanFlags = (flags: Record<string, boolean>) => {
  useBooleanFlagValueMock.mockImplementation((flag: string, defaultValue: boolean) => {
    return Object.prototype.hasOwnProperty.call(flags, flag) ? flags[flag] : defaultValue;
  });
};

jest.mock('@openfeature/react-sdk', () => ({
  ...jest.requireActual('@openfeature/react-sdk'),
  useBooleanFlagValue: (flag: string, defaultValue: boolean) => useBooleanFlagValueMock(flag, defaultValue),
}));

const fontSize: LogListFontSize = 'default';
const contextProps = {
  app: CoreApp.Unknown,
  containerElement: document.createElement('div'),
  dedupStrategy: LogsDedupStrategy.exact,
  displayedFields: [],
  enableLogDetails: false,
  fontSize,
  logs: [],
  showControls: true,
  showLevel: true,
  showTime: false,
  sortOrder: LogsSortOrder.Ascending,
  syntaxHighlighting: false,
  wrapLogMessage: false,
  isAssistantAvailable: false,
  openAssistantByLog: () => {},
  unwrappedColumns: false,
};

const assertExpandedOptionsCopyVisible = () => {
  expect(screen.getByText(EXPANDED_LABEL_COPY)).toBeVisible();
  expect(screen.getByText(SCROLL_BOTTOM_LABEL_COPY)).toBeVisible();
  expect(screen.getByText(OLDEST_LOGS_LABEL_COPY)).toBeVisible();
  expect(screen.getByText(DEDUPE_LABEL_COPY)).toBeVisible();
  expect(screen.getByText(SCROLL_TOP_LABEL_COPY)).toBeVisible();
};
describe('LogListControls', () => {
  beforeEach(() => {
    setBooleanFlags({ newLogsPanel: false });
  });

  test('Renders without errors', () => {
    render(
      <LogListContextProvider {...contextProps}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    expect(screen.getByLabelText(SCROLL_BOTTOM_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(OLDEST_LOGS_LABEL_REGEX)).toBeInTheDocument();
    expect(screen.getByLabelText(DEDUPE_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(FILTER_LEVELS_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(SHOW_TIMESTAMP_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(WRAP_LINES_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(ENABLE_HIGHLIGHTING_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(SCROLL_TOP_LABEL_COPY)).toBeInTheDocument();
    expect(screen.queryByLabelText(SHOW_UNIQUE_LABELS_LABEL_COPY)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(EXPAND_JSON_LOGS_LABEL_COPY)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(ESCAPE_NEWLINES_TOOLTIP_COPY)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(REMOVE_ESCAPE_NEWLINES_LABEL_COPY)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(ENABLE_UNWRAPPED_COLUMNS_COPY)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(DISABLE_UNWRAPPED_COLUMNS_COPY)).not.toBeInTheDocument();
  });

  test('Renders legacy controls', () => {
    render(
      <LogListContextProvider {...contextProps} app={CoreApp.Explore} showUniqueLabels={false} prettifyJSON={false}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    expect(screen.getByLabelText(SHOW_UNIQUE_LABELS_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(EXPAND_JSON_LOGS_LABEL_COPY)).toBeInTheDocument();
  });

  test.each([CoreApp.Dashboard, CoreApp.PanelEditor, CoreApp.PanelViewer])(
    'Renders a subset of options for dashboards',
    (app: CoreApp) => {
      render(
        <LogListContextProvider {...contextProps} app={app}>
          <LogListControls eventBus={new EventBusSrv()} />
        </LogListContextProvider>
      );
      expect(screen.getByLabelText(SCROLL_BOTTOM_LABEL_COPY)).toBeInTheDocument();
      expect(screen.getByLabelText(SCROLL_TOP_LABEL_COPY)).toBeInTheDocument();
      expect(screen.getByLabelText(FILTER_LEVELS_LABEL_COPY)).toBeInTheDocument();
      expect(screen.queryByLabelText(OLDEST_LOGS_LABEL_REGEX)).not.toBeInTheDocument();
      expect(screen.queryByLabelText(DEDUPE_LABEL_COPY)).not.toBeInTheDocument();
      expect(screen.queryByLabelText(SHOW_TIMESTAMP_LABEL_COPY)).not.toBeInTheDocument();
      expect(screen.queryByLabelText(WRAP_LINES_LABEL_COPY)).not.toBeInTheDocument();
      expect(screen.queryByLabelText(ENABLE_HIGHLIGHTING_LABEL_COPY)).not.toBeInTheDocument();
    }
  );

  test('Renders a subset of options for plugins', () => {
    render(
      <LogListContextProvider {...contextProps} app={CoreApp.Unknown}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    expect(screen.getByLabelText(SCROLL_BOTTOM_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(OLDEST_LOGS_LABEL_REGEX)).toBeInTheDocument();
    expect(screen.getByLabelText(DEDUPE_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(FILTER_LEVELS_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(SHOW_TIMESTAMP_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(WRAP_LINES_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(ENABLE_HIGHLIGHTING_LABEL_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(SCROLL_TOP_LABEL_COPY)).toBeInTheDocument();
    expect(screen.queryByLabelText(SHOW_UNIQUE_LABELS_LABEL_COPY)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(EXPAND_JSON_LOGS_LABEL_COPY)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(ESCAPE_NEWLINES_TOOLTIP_COPY)).not.toBeInTheDocument();
    expect(screen.queryByLabelText(REMOVE_ESCAPE_NEWLINES_LABEL_COPY)).not.toBeInTheDocument();
  });

  test('Allows to scroll', async () => {
    const eventBus = new EventBusSrv();
    jest.spyOn(eventBus, 'publish');
    render(
      <LogListContextProvider {...contextProps}>
        <LogListControls eventBus={eventBus} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(SCROLL_BOTTOM_LABEL_COPY));
    await userEvent.click(screen.getByLabelText(SCROLL_TOP_LABEL_COPY));
    expect(eventBus.publish).toHaveBeenCalledTimes(2);
    expect(eventBus.publish).toHaveBeenCalledWith(
      new ScrollToLogsEvent({
        scrollTo: 'bottom',
      })
    );
    expect(eventBus.publish).toHaveBeenCalledWith(
      new ScrollToLogsEvent({
        scrollTo: 'top',
      })
    );
  });

  test('Expands options', async () => {
    render(
      <LogListContextProvider {...contextProps} sortOrder={LogsSortOrder.Ascending}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    // Initial state should be collapsed
    expect(screen.getByLabelText(COLLAPSED_LABEL_COPY)).toBeVisible();
    // Expanded label should not be visible
    expect(screen.queryByText(EXPANDED_LABEL_COPY)).not.toBeInTheDocument();
    // Expand options
    await userEvent.click(screen.getByLabelText(COLLAPSED_LABEL_COPY));
    // Verify that the label (state) is not collapsed
    expect(screen.queryByLabelText(COLLAPSED_LABEL_COPY)).not.toBeInTheDocument();
    expect(screen.getByLabelText(EXPANDED_LABEL_COPY)).toBeVisible();
    // Verify the expanded labels are rendered
    assertExpandedOptionsCopyVisible();
  });

  test('Expands options shown by default with container width > 1200', async () => {
    const div = document.createElement('div');
    const divSpy = jest.spyOn(div, 'clientWidth', 'get');
    //@ts-expect-error
    divSpy['clientWidth'] = 1201;
    render(
      //@ts-expect-error
      <LogListContextProvider {...contextProps} sortOrder={LogsSortOrder.Ascending} containerElement={divSpy}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );

    // Verify the expanded labels are rendered
    assertExpandedOptionsCopyVisible();
    // Collapse options
    await userEvent.click(screen.getByLabelText(EXPANDED_LABEL_COPY));
    // State should be collapsed
    expect(screen.getByLabelText(COLLAPSED_LABEL_COPY)).toBeVisible();
  });

  test('Controls sort order', async () => {
    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider
        {...contextProps}
        sortOrder={LogsSortOrder.Ascending}
        onLogOptionsChange={onLogOptionsChange}
      >
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(OLDEST_LOGS_LABEL_REGEX));
    expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
    expect(onLogOptionsChange).toHaveBeenCalledWith('sortOrder', LogsSortOrder.Descending);
  });

  test('Controls deduplication', async () => {
    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider {...contextProps} onLogOptionsChange={onLogOptionsChange}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(DEDUPE_LABEL_COPY));
    await userEvent.click(screen.getByText('Numbers'));
    expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
    expect(onLogOptionsChange).toHaveBeenCalledWith('dedupStrategy', LogsDedupStrategy.numbers);
  });

  test('Sets all level filters if not provided', async () => {
    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider {...contextProps} onLogOptionsChange={onLogOptionsChange}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(FILTER_LEVELS_LABEL_COPY));
    expect(await screen.findByText('All levels')).toBeVisible();
    expect(screen.getByText('Info')).toBeVisible();
    expect(screen.getByText('Debug')).toBeVisible();
    expect(screen.getByText('Trace')).toBeVisible();
    expect(screen.getByText('Warning')).toBeVisible();
    expect(screen.getByText('Error')).toBeVisible();
    expect(screen.getByText('Critical')).toBeVisible();
    await userEvent.click(screen.getByText('Error'));
    expect(onLogOptionsChange).toHaveBeenCalledWith('filterLevels', ['error']);
  });

  test('Sets all level filters from provided levels', async () => {
    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider {...contextProps} onLogOptionsChange={onLogOptionsChange}>
        <LogListControls eventBus={new EventBusSrv()} logLevels={[LogLevel.critical, LogLevel.information]} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(FILTER_LEVELS_LABEL_COPY));
    expect(await screen.findByText('All levels')).toBeVisible();
    expect(screen.getByText('Info')).toBeVisible();
    expect(screen.queryByText('Debug')).not.toBeInTheDocument();
    expect(screen.queryByText('Trace')).not.toBeInTheDocument();
    expect(screen.queryByText('Warning')).not.toBeInTheDocument();
    expect(screen.queryByText('Error')).not.toBeInTheDocument();
    expect(screen.getByText('Critical')).toBeVisible();
    await userEvent.click(screen.getByText('Critical'));
    expect(onLogOptionsChange).toHaveBeenCalledWith('filterLevels', ['critical']);
  });

  test('Controls timestamp visibility', async () => {
    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider {...contextProps} showTime={false} onLogOptionsChange={onLogOptionsChange}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(SHOW_TIMESTAMP_LABEL_COPY));
    expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
    expect(onLogOptionsChange).toHaveBeenCalledWith('showTime', true);
  });

  test('Controls line wrapping', async () => {
    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider {...contextProps} wrapLogMessage={false} onLogOptionsChange={onLogOptionsChange}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(WRAP_LINES_LABEL_COPY));
    expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
    expect(onLogOptionsChange).toHaveBeenCalledWith('wrapLogMessage', true);
  });

  test('Controls line wrapping and prettify JSON', async () => {
    setBooleanFlags({ newLogsPanel: true });

    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider
        {...contextProps}
        wrapLogMessage={false}
        onLogOptionsChange={onLogOptionsChange}
        prettifyJSON={false}
      >
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );

    await userEvent.click(screen.getByLabelText('Wrap disabled'));
    await userEvent.click(screen.getByText('Enable line wrapping'));

    expect(onLogOptionsChange).toHaveBeenCalledTimes(2);
    expect(onLogOptionsChange).toHaveBeenCalledWith('wrapLogMessage', true);
    expect(onLogOptionsChange).toHaveBeenCalledWith('prettifyLogMessage', false);

    await userEvent.click(screen.getByLabelText(WRAP_LINES_LABEL_COPY));
    await userEvent.click(screen.getByText(WRAP_JSON_TOOLTIP_COPY));

    expect(onLogOptionsChange).toHaveBeenCalledTimes(4);
    expect(onLogOptionsChange).toHaveBeenCalledWith('prettifyLogMessage', true);

    await userEvent.click(screen.getByLabelText(WRAP_JSON_LABEL_COPY));
    await userEvent.click(screen.getByText(WRAP_DISABLE_LABEL_COPY));

    expect(onLogOptionsChange).toHaveBeenCalledWith('wrapLogMessage', false);
    expect(onLogOptionsChange).toHaveBeenCalledWith('prettifyLogMessage', false);

    expect(onLogOptionsChange).toHaveBeenCalledTimes(6);
  });

  test('Enables column controls with unwrapped logs', async () => {
    setBooleanFlags({ newLogsPanel: true });

    const { rerender } = render(
      <LogListContextProvider {...contextProps} wrapLogMessage={false} unwrappedColumns>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );

    expect(screen.getByLabelText(DISABLE_UNWRAPPED_COLUMNS_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(DISABLE_UNWRAPPED_COLUMNS_COPY)).toBeEnabled();
    expect(screen.getByText(COLUMNS_ENABLED_COPY)).toBeInTheDocument();
    expect(screen.queryByText(COLUMNS_DISABLED_COPY)).not.toBeInTheDocument();

    rerender(
      <LogListContextProvider {...contextProps} wrapLogMessage={false} unwrappedColumns={false}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );

    expect(screen.getByLabelText(ENABLE_UNWRAPPED_COLUMNS_COPY)).toBeInTheDocument();
    expect(screen.getByLabelText(ENABLE_UNWRAPPED_COLUMNS_COPY)).toBeEnabled();
    expect(screen.getByText(COLUMNS_DISABLED_COPY)).toBeInTheDocument();
    expect(screen.queryByText(COLUMNS_ENABLED_COPY)).not.toBeInTheDocument();
  });

  test('Disables column controls for wrapped logs', async () => {
    setBooleanFlags({ newLogsPanel: true });

    render(
      <LogListContextProvider {...contextProps} wrapLogMessage unwrappedColumns>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );

    expect(screen.getByLabelText(COLUMNS_DISABLED_TOOLTIP_COPY)).toBeDisabled();
    expect(screen.getByText(COLUMNS_NOT_SUPPORTED_COPY)).toBeInTheDocument();
  });

  test('Controls timestamp resolution', async () => {
    setBooleanFlags({ newLogsPanel: true });

    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider {...contextProps} showTime={false} onLogOptionsChange={onLogOptionsChange}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );

    await userEvent.click(screen.getByLabelText(TIMESTAMP_LABEL_COPY));
    await userEvent.click(screen.getByText('Show millisecond timestamps'));

    expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
    expect(onLogOptionsChange).toHaveBeenCalledWith('showTime', true);

    await userEvent.click(screen.getByLabelText(TIMESTAMP_LABEL_COPY));
    await userEvent.click(screen.getByText('Show nanosecond timestamps'));

    expect(onLogOptionsChange).toHaveBeenCalledTimes(2);

    await userEvent.click(screen.getByLabelText(TIMESTAMP_LABEL_COPY));
    await userEvent.click(screen.getByText(TIMESTAMP_HIDE_LABEL_COPY));

    expect(onLogOptionsChange).toHaveBeenCalledTimes(3);
    expect(onLogOptionsChange).toHaveBeenCalledWith('showTime', false);
  });

  test('Controls syntax highlighting', async () => {
    const onLogOptionsChange = jest.fn();
    render(
      <LogListContextProvider {...contextProps} syntaxHighlighting={false} onLogOptionsChange={onLogOptionsChange}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(ENABLE_HIGHLIGHTING_LABEL_COPY));
    expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
    expect(onLogOptionsChange).toHaveBeenCalledWith('syntaxHighlighting', true);
  });

  test('Controls unique labels', async () => {
    const { rerender } = render(
      <LogListContextProvider {...contextProps} app={CoreApp.Explore} showUniqueLabels={false}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(SHOW_UNIQUE_LABELS_LABEL_COPY));
    rerender(
      <LogListContextProvider {...contextProps} app={CoreApp.Explore} showUniqueLabels={false}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    expect(screen.getByLabelText(HIDE_UNIQUE_LABELS_LABEL_COPY));
  });

  test('Controls Expand JSON logs', async () => {
    const { rerender } = render(
      <LogListContextProvider {...contextProps} prettifyJSON={false}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(EXPAND_JSON_LOGS_LABEL_COPY));
    rerender(
      <LogListContextProvider {...contextProps} showUniqueLabels={false}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    expect(screen.getByLabelText(COLLAPSE_JSON_LOGS_LABEL_COPY));
  });

  test('Controls font size', async () => {
    setBooleanFlags({ newLogsPanel: true });

    render(
      <LogListContextProvider {...contextProps}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(FONT_SIZE_LARGE_LABEL_COPY));
    await screen.findByLabelText(FONT_SIZE_LARGE_TOOLTIP_COPY);

    await userEvent.click(screen.getByLabelText(FONT_SIZE_SMALL_LABEL_COPY));
    await screen.findByLabelText(FONT_SIZE_SMALL_TOOLTIP_COPY);
  });

  test.each([
    ['txt', 'text'],
    ['json', 'json'],
    ['csv', 'csv'],
  ])('Allows to download logs', async (label: string, format: string) => {
    jest.mocked(downloadLogs).mockClear();
    render(
      <LogListContextProvider {...contextProps}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(DOWNLOAD_LOGS_LABEL_COPY));
    await userEvent.click(await screen.findByText(label));
    expect(downloadLogs).toHaveBeenCalledTimes(1);
    expect(downloadLogs).toHaveBeenCalledWith(format, [], undefined, []);
  });

  test('Allows to download logs filtered logs', async () => {
    jest.mocked(downloadLogs).mockClear();
    const log1 = createLogRow({ logLevel: LogLevel.error });
    const log2 = createLogRow({ logLevel: LogLevel.warning });
    const logs = [log1, log2];
    const filteredLogs = [log1];

    render(
      <LogListContextProvider {...contextProps} logs={logs} filterLevels={[LogLevel.error]}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(DOWNLOAD_LOGS_LABEL_COPY));
    await userEvent.click(await screen.findByText('txt'));
    expect(downloadLogs).toHaveBeenCalledWith('text', filteredLogs, undefined, []);
  });

  test('Controls new lines', async () => {
    const log = createLogLine({ entry: 'the\\r\\nentry', hasUnescapedContent: true });
    const { rerender } = render(
      <LogListContextProvider {...contextProps} logs={[log]}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(ESCAPE_NEWLINES_TOOLTIP_COPY));
    rerender(
      <LogListContextProvider {...contextProps} logs={[log]}>
        <LogListControls eventBus={new EventBusSrv()} />
      </LogListContextProvider>
    );
    await userEvent.click(screen.getByLabelText(REMOVE_ESCAPE_NEWLINES_LABEL_COPY));
  });
});
