1import React from 'react';
2import { LogDetails, Props } from './LogDetails';
3import { LogRowModel, LogLevel, MutableDataFrame, Field, GrafanaTheme2 } from '@grafana/data';
4import { mount } from 'enzyme';
5import { LogDetailsRow } from './LogDetailsRow';
6
7const setup = (propOverrides?: Partial<Props>, rowOverrides?: Partial<LogRowModel>) => {
8  const props: Props = {
9    theme: {} as GrafanaTheme2,
10    showDuplicates: false,
11    wrapLogMessage: false,
12    row: {
13      dataFrame: new MutableDataFrame(),
14      entryFieldIndex: 0,
15      rowIndex: 0,
16      logLevel: 'error' as LogLevel,
17      timeFromNow: '',
18      timeEpochMs: 1546297200000,
19      timeEpochNs: '1546297200000000000',
20      timeLocal: '',
21      timeUtc: '',
22      hasAnsi: false,
23      hasUnescapedContent: false,
24      entry: '',
25      raw: '',
26      uid: '0',
27      labels: {},
28      ...(rowOverrides || {}),
29    },
30    getRows: () => [],
31    onClickFilterLabel: () => {},
32    onClickFilterOutLabel: () => {},
33    ...(propOverrides || {}),
34  };
35
36  return mount(<LogDetails {...props} />);
37};
38
39describe('LogDetails', () => {
40  describe('when labels are present', () => {
41    it('should render heading', () => {
42      const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
43      expect(wrapper.find({ 'aria-label': 'Log labels' }).hostNodes()).toHaveLength(1);
44    });
45    it('should render labels', () => {
46      const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
47      expect(wrapper.text().includes('key1label1key2label2')).toBe(true);
48    });
49  });
50  describe('when log row has error', () => {
51    it('should not render log level border', () => {
52      const wrapper = setup({ hasError: true }, undefined);
53      expect(wrapper.find({ 'aria-label': 'Log level' }).html()).not.toContain('logs-row__level');
54    });
55  });
56  describe('when row entry has parsable fields', () => {
57    it('should render heading ', () => {
58      const wrapper = setup(undefined, { entry: 'test=successful' });
59      expect(wrapper.find({ title: 'Ad-hoc statistics' }).hostNodes()).toHaveLength(1);
60    });
61    it('should render detected fields', () => {
62      const wrapper = setup(undefined, { entry: 'test=successful' });
63      expect(wrapper.text().includes('testsuccessful')).toBe(true);
64    });
65  });
66  describe('when row entry have parsable fields and labels are present', () => {
67    it('should render all headings', () => {
68      const wrapper = setup(undefined, { entry: 'test=successful', labels: { key: 'label' } });
69      expect(wrapper.find({ 'aria-label': 'Log labels' })).toHaveLength(1);
70      expect(wrapper.find({ 'aria-label': 'Detected fields' })).toHaveLength(1);
71    });
72    it('should render all labels and detected fields', () => {
73      const wrapper = setup(undefined, {
74        entry: 'test=successful',
75        labels: { key: 'label' },
76      });
77      expect(wrapper.text().includes('keylabel')).toBe(true);
78      expect(wrapper.text().includes('testsuccessful')).toBe(true);
79    });
80  });
81  describe('when row entry and labels are not present', () => {
82    it('should render no details available message', () => {
83      const wrapper = setup(undefined, { entry: '' });
84      expect(wrapper.text().includes('No details available')).toBe(true);
85    });
86    it('should not render headings', () => {
87      const wrapper = setup(undefined, { entry: '' });
88      expect(wrapper.find({ 'aria-label': 'Log labels' })).toHaveLength(0);
89      expect(wrapper.find({ 'aria-label': 'Detected fields' })).toHaveLength(0);
90    });
91  });
92
93  it('should render fields from dataframe with links', () => {
94    const entry = 'traceId=1234 msg="some message"';
95    const dataFrame = new MutableDataFrame({
96      fields: [
97        { name: 'entry', values: [entry] },
98        // As we have traceId in message already this will shadow it.
99        {
100          name: 'traceId',
101          values: ['1234'],
102          config: { links: [{ title: 'link', url: 'localhost:3210/${__value.text}' }] },
103        },
104        { name: 'userId', values: ['5678'] },
105      ],
106    });
107    const wrapper = setup(
108      {
109        getFieldLinks: (field: Field, rowIndex: number) => {
110          if (field.config && field.config.links) {
111            return field.config.links.map((link) => {
112              return {
113                href: link.url.replace('${__value.text}', field.values.get(rowIndex)),
114                title: link.title,
115                target: '_blank',
116                origin: field,
117              };
118            });
119          }
120          return [];
121        },
122      },
123      { entry, dataFrame, entryFieldIndex: 0, rowIndex: 0 }
124    );
125    expect(wrapper.find(LogDetailsRow).length).toBe(3);
126    const traceIdRow = wrapper.find(LogDetailsRow).filter({ parsedKey: 'traceId' });
127    expect(traceIdRow.length).toBe(1);
128    expect(traceIdRow.find('a').hostNodes().length).toBe(1);
129    expect((traceIdRow.find('a').getDOMNode() as HTMLAnchorElement).href).toBe('localhost:3210/1234');
130  });
131});
132