language_provider.test.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // @ts-ignore
  2. import Plain from 'slate-plain-serializer';
  3. import LanguageProvider, { LABEL_REFRESH_INTERVAL } from './language_provider';
  4. import { advanceTo, clear, advanceBy } from 'jest-date-mock';
  5. import { beforeEach } from 'test/lib/common';
  6. import { DataQueryResponseData } from '@grafana/ui';
  7. describe('Language completion provider', () => {
  8. const datasource = {
  9. metadataRequest: () => ({ data: { data: [] as DataQueryResponseData[] } }),
  10. };
  11. describe('empty query suggestions', () => {
  12. it('returns no suggestions on emtpty context', () => {
  13. const instance = new LanguageProvider(datasource);
  14. const value = Plain.deserialize('');
  15. const result = instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
  16. expect(result.context).toBeUndefined();
  17. expect(result.refresher).toBeUndefined();
  18. expect(result.suggestions.length).toEqual(0);
  19. });
  20. it('returns default suggestions with history on emtpty context when history was provided', () => {
  21. const instance = new LanguageProvider(datasource);
  22. const value = Plain.deserialize('');
  23. const history = [
  24. {
  25. query: { refId: '1', expr: '{app="foo"}' },
  26. },
  27. ];
  28. const result = instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] }, { history });
  29. expect(result.context).toBeUndefined();
  30. expect(result.refresher).toBeUndefined();
  31. expect(result.suggestions).toMatchObject([
  32. {
  33. label: 'History',
  34. items: [
  35. {
  36. label: '{app="foo"}',
  37. },
  38. ],
  39. },
  40. ]);
  41. });
  42. it('returns no suggestions within regexp', () => {
  43. const instance = new LanguageProvider(datasource);
  44. const value = Plain.deserialize('{} ()');
  45. const range = value.selection.merge({
  46. anchorOffset: 4,
  47. });
  48. const valueWithSelection = value.change().select(range).value;
  49. const history = [
  50. {
  51. query: { refId: '1', expr: '{app="foo"}' },
  52. },
  53. ];
  54. const result = instance.provideCompletionItems(
  55. {
  56. text: '',
  57. prefix: '',
  58. value: valueWithSelection,
  59. wrapperClasses: [],
  60. },
  61. { history }
  62. );
  63. expect(result.context).toBeUndefined();
  64. expect(result.refresher).toBeUndefined();
  65. expect(result.suggestions.length).toEqual(0);
  66. });
  67. });
  68. describe('label suggestions', () => {
  69. it('returns default label suggestions on label context', () => {
  70. const instance = new LanguageProvider(datasource);
  71. const value = Plain.deserialize('{}');
  72. const range = value.selection.merge({
  73. anchorOffset: 1,
  74. });
  75. const valueWithSelection = value.change().select(range).value;
  76. const result = instance.provideCompletionItems({
  77. text: '',
  78. prefix: '',
  79. wrapperClasses: ['context-labels'],
  80. value: valueWithSelection,
  81. });
  82. expect(result.context).toBe('context-labels');
  83. expect(result.suggestions).toEqual([{ items: [{ label: 'job' }, { label: 'namespace' }], label: 'Labels' }]);
  84. });
  85. });
  86. });
  87. describe('Query imports', () => {
  88. const datasource = {
  89. metadataRequest: () => ({ data: { data: [] as DataQueryResponseData[] } }),
  90. };
  91. it('returns empty queries for unknown origin datasource', async () => {
  92. const instance = new LanguageProvider(datasource);
  93. const result = await instance.importQueries([{ refId: 'bar', expr: 'foo' }], 'unknown');
  94. expect(result).toEqual([{ refId: 'bar', expr: '' }]);
  95. });
  96. describe('prometheus query imports', () => {
  97. it('returns empty query from metric-only query', async () => {
  98. const instance = new LanguageProvider(datasource);
  99. const result = await instance.importPrometheusQuery('foo');
  100. expect(result).toEqual('');
  101. });
  102. it('returns empty query from selector query if label is not available', async () => {
  103. const datasourceWithLabels = {
  104. metadataRequest: (url: string) =>
  105. url === '/api/prom/label' ? { data: { data: ['other'] } } : { data: { data: [] as DataQueryResponseData[] } },
  106. };
  107. const instance = new LanguageProvider(datasourceWithLabels);
  108. const result = await instance.importPrometheusQuery('{foo="bar"}');
  109. expect(result).toEqual('{}');
  110. });
  111. it('returns selector query from selector query with common labels', async () => {
  112. const datasourceWithLabels = {
  113. metadataRequest: (url: string) =>
  114. url === '/api/prom/label' ? { data: { data: ['foo'] } } : { data: { data: [] as DataQueryResponseData[] } },
  115. };
  116. const instance = new LanguageProvider(datasourceWithLabels);
  117. const result = await instance.importPrometheusQuery('metric{foo="bar",baz="42"}');
  118. expect(result).toEqual('{foo="bar"}');
  119. });
  120. it('returns selector query from selector query with all labels if logging label list is empty', async () => {
  121. const datasourceWithLabels = {
  122. metadataRequest: (url: string) =>
  123. url === '/api/prom/label'
  124. ? { data: { data: [] as DataQueryResponseData[] } }
  125. : { data: { data: [] as DataQueryResponseData[] } },
  126. };
  127. const instance = new LanguageProvider(datasourceWithLabels);
  128. const result = await instance.importPrometheusQuery('metric{foo="bar",baz="42"}');
  129. expect(result).toEqual('{baz="42",foo="bar"}');
  130. });
  131. });
  132. });
  133. describe('Labels refresh', () => {
  134. const datasource = {
  135. metadataRequest: () => ({ data: { data: [] as DataQueryResponseData[] } }),
  136. };
  137. const instance = new LanguageProvider(datasource);
  138. beforeEach(() => {
  139. instance.fetchLogLabels = jest.fn();
  140. });
  141. afterEach(() => {
  142. jest.clearAllMocks();
  143. clear();
  144. });
  145. it("should not refresh labels if refresh interval hasn't passed", () => {
  146. advanceTo(new Date(2019, 1, 1, 0, 0, 0));
  147. instance.logLabelFetchTs = Date.now();
  148. advanceBy(LABEL_REFRESH_INTERVAL / 2);
  149. instance.refreshLogLabels();
  150. expect(instance.fetchLogLabels).not.toBeCalled();
  151. });
  152. it('should refresh labels if refresh interval passed', () => {
  153. advanceTo(new Date(2019, 1, 1, 0, 0, 0));
  154. instance.logLabelFetchTs = Date.now();
  155. advanceBy(LABEL_REFRESH_INTERVAL + 1);
  156. instance.refreshLogLabels();
  157. expect(instance.fetchLogLabels).toBeCalled();
  158. });
  159. });