PromQueryField.test.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import React from 'react';
  2. import Enzyme, { shallow } from 'enzyme';
  3. import Adapter from 'enzyme-adapter-react-16';
  4. import Plain from 'slate-plain-serializer';
  5. import PromQueryField, { groupMetricsByPrefix, RECORDING_RULES_GROUP } from './PromQueryField';
  6. Enzyme.configure({ adapter: new Adapter() });
  7. describe('PromQueryField typeahead handling', () => {
  8. const defaultProps = {
  9. request: () => ({ data: { data: [] } }),
  10. };
  11. it('returns default suggestions on emtpty context', () => {
  12. const instance = shallow(<PromQueryField {...defaultProps} />).instance() as PromQueryField;
  13. const result = instance.getTypeahead({ text: '', prefix: '', wrapperClasses: [] });
  14. expect(result.context).toBeUndefined();
  15. expect(result.refresher).toBeUndefined();
  16. expect(result.suggestions.length).toEqual(2);
  17. });
  18. describe('range suggestions', () => {
  19. it('returns range suggestions in range context', () => {
  20. const instance = shallow(<PromQueryField {...defaultProps} />).instance() as PromQueryField;
  21. const result = instance.getTypeahead({ text: '1', prefix: '1', wrapperClasses: ['context-range'] });
  22. expect(result.context).toBe('context-range');
  23. expect(result.refresher).toBeUndefined();
  24. expect(result.suggestions).toEqual([
  25. {
  26. items: [{ label: '1m' }, { label: '5m' }, { label: '10m' }, { label: '30m' }, { label: '1h' }],
  27. label: 'Range vector',
  28. },
  29. ]);
  30. });
  31. });
  32. describe('metric suggestions', () => {
  33. it('returns metrics suggestions by default', () => {
  34. const instance = shallow(
  35. <PromQueryField {...defaultProps} metrics={['foo', 'bar']} />
  36. ).instance() as PromQueryField;
  37. const result = instance.getTypeahead({ text: 'a', prefix: 'a', wrapperClasses: [] });
  38. expect(result.context).toBeUndefined();
  39. expect(result.refresher).toBeUndefined();
  40. expect(result.suggestions.length).toEqual(2);
  41. });
  42. it('returns default suggestions after a binary operator', () => {
  43. const instance = shallow(
  44. <PromQueryField {...defaultProps} metrics={['foo', 'bar']} />
  45. ).instance() as PromQueryField;
  46. const result = instance.getTypeahead({ text: '*', prefix: '', wrapperClasses: [] });
  47. expect(result.context).toBeUndefined();
  48. expect(result.refresher).toBeUndefined();
  49. expect(result.suggestions.length).toEqual(2);
  50. });
  51. });
  52. describe('label suggestions', () => {
  53. it('returns default label suggestions on label context and no metric', () => {
  54. const instance = shallow(<PromQueryField {...defaultProps} />).instance() as PromQueryField;
  55. const value = Plain.deserialize('{}');
  56. const range = value.selection.merge({
  57. anchorOffset: 1,
  58. });
  59. const valueWithSelection = value.change().select(range).value;
  60. const result = instance.getTypeahead({
  61. text: '',
  62. prefix: '',
  63. wrapperClasses: ['context-labels'],
  64. value: valueWithSelection,
  65. });
  66. expect(result.context).toBe('context-labels');
  67. expect(result.suggestions).toEqual([{ items: [{ label: 'job' }, { label: 'instance' }], label: 'Labels' }]);
  68. });
  69. it('returns label suggestions on label context and metric', () => {
  70. const instance = shallow(
  71. <PromQueryField {...defaultProps} labelKeys={{ '{__name__="metric"}': ['bar'] }} />
  72. ).instance() as PromQueryField;
  73. const value = Plain.deserialize('metric{}');
  74. const range = value.selection.merge({
  75. anchorOffset: 7,
  76. });
  77. const valueWithSelection = value.change().select(range).value;
  78. const result = instance.getTypeahead({
  79. text: '',
  80. prefix: '',
  81. wrapperClasses: ['context-labels'],
  82. value: valueWithSelection,
  83. });
  84. expect(result.context).toBe('context-labels');
  85. expect(result.suggestions).toEqual([{ items: [{ label: 'bar' }], label: 'Labels' }]);
  86. });
  87. it('returns label suggestions on label context but leaves out labels that already exist', () => {
  88. const instance = shallow(
  89. <PromQueryField {...defaultProps} labelKeys={{ '{job="foo"}': ['bar', 'job'] }} />
  90. ).instance() as PromQueryField;
  91. const value = Plain.deserialize('{job="foo",}');
  92. const range = value.selection.merge({
  93. anchorOffset: 11,
  94. });
  95. const valueWithSelection = value.change().select(range).value;
  96. const result = instance.getTypeahead({
  97. text: '',
  98. prefix: '',
  99. wrapperClasses: ['context-labels'],
  100. value: valueWithSelection,
  101. });
  102. expect(result.context).toBe('context-labels');
  103. expect(result.suggestions).toEqual([{ items: [{ label: 'bar' }], label: 'Labels' }]);
  104. });
  105. it('returns a refresher on label context and unavailable metric', () => {
  106. const instance = shallow(
  107. <PromQueryField {...defaultProps} labelKeys={{ '{__name__="foo"}': ['bar'] }} />
  108. ).instance() as PromQueryField;
  109. const value = Plain.deserialize('metric{}');
  110. const range = value.selection.merge({
  111. anchorOffset: 7,
  112. });
  113. const valueWithSelection = value.change().select(range).value;
  114. const result = instance.getTypeahead({
  115. text: '',
  116. prefix: '',
  117. wrapperClasses: ['context-labels'],
  118. value: valueWithSelection,
  119. });
  120. expect(result.context).toBeUndefined();
  121. expect(result.refresher).toBeInstanceOf(Promise);
  122. expect(result.suggestions).toEqual([]);
  123. });
  124. it('returns label values on label context when given a metric and a label key', () => {
  125. const instance = shallow(
  126. <PromQueryField
  127. {...defaultProps}
  128. labelKeys={{ '{__name__="metric"}': ['bar'] }}
  129. labelValues={{ '{__name__="metric"}': { bar: ['baz'] } }}
  130. />
  131. ).instance() as PromQueryField;
  132. const value = Plain.deserialize('metric{bar=ba}');
  133. const range = value.selection.merge({
  134. anchorOffset: 13,
  135. });
  136. const valueWithSelection = value.change().select(range).value;
  137. const result = instance.getTypeahead({
  138. text: '=ba',
  139. prefix: 'ba',
  140. wrapperClasses: ['context-labels'],
  141. labelKey: 'bar',
  142. value: valueWithSelection,
  143. });
  144. expect(result.context).toBe('context-label-values');
  145. expect(result.suggestions).toEqual([{ items: [{ label: 'baz' }], label: 'Label values for "bar"' }]);
  146. });
  147. it('returns label suggestions on aggregation context and metric w/ selector', () => {
  148. const instance = shallow(
  149. <PromQueryField {...defaultProps} labelKeys={{ '{__name__="metric",foo="xx"}': ['bar'] }} />
  150. ).instance() as PromQueryField;
  151. const value = Plain.deserialize('sum(metric{foo="xx"}) by ()');
  152. const range = value.selection.merge({
  153. anchorOffset: 26,
  154. });
  155. const valueWithSelection = value.change().select(range).value;
  156. const result = instance.getTypeahead({
  157. text: '',
  158. prefix: '',
  159. wrapperClasses: ['context-aggregation'],
  160. value: valueWithSelection,
  161. });
  162. expect(result.context).toBe('context-aggregation');
  163. expect(result.suggestions).toEqual([{ items: [{ label: 'bar' }], label: 'Labels' }]);
  164. });
  165. it('returns label suggestions on aggregation context and metric w/o selector', () => {
  166. const instance = shallow(
  167. <PromQueryField {...defaultProps} labelKeys={{ '{__name__="metric"}': ['bar'] }} />
  168. ).instance() as PromQueryField;
  169. const value = Plain.deserialize('sum(metric) by ()');
  170. const range = value.selection.merge({
  171. anchorOffset: 16,
  172. });
  173. const valueWithSelection = value.change().select(range).value;
  174. const result = instance.getTypeahead({
  175. text: '',
  176. prefix: '',
  177. wrapperClasses: ['context-aggregation'],
  178. value: valueWithSelection,
  179. });
  180. expect(result.context).toBe('context-aggregation');
  181. expect(result.suggestions).toEqual([{ items: [{ label: 'bar' }], label: 'Labels' }]);
  182. });
  183. });
  184. });
  185. describe('groupMetricsByPrefix()', () => {
  186. it('returns an empty group for no metrics', () => {
  187. expect(groupMetricsByPrefix([])).toEqual([]);
  188. });
  189. it('returns options grouped by prefix', () => {
  190. expect(groupMetricsByPrefix(['foo_metric'])).toMatchObject([
  191. {
  192. value: 'foo',
  193. children: [
  194. {
  195. value: 'foo_metric',
  196. },
  197. ],
  198. },
  199. ]);
  200. });
  201. it('returns options without prefix as toplevel option', () => {
  202. expect(groupMetricsByPrefix(['metric'])).toMatchObject([
  203. {
  204. value: 'metric',
  205. },
  206. ]);
  207. });
  208. it('returns recording rules grouped separately', () => {
  209. expect(groupMetricsByPrefix([':foo_metric:'])).toMatchObject([
  210. {
  211. value: RECORDING_RULES_GROUP,
  212. children: [
  213. {
  214. value: ':foo_metric:',
  215. },
  216. ],
  217. },
  218. ]);
  219. });
  220. });