utils.test.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import { assertIsDefined } from 'test/helpers/asserts';
  2. import {
  3. createTheme,
  4. DefaultTimeZone,
  5. EventBusSrv,
  6. FieldConfig,
  7. FieldType,
  8. getDefaultTimeRange,
  9. MutableDataFrame,
  10. VizOrientation,
  11. } from '@grafana/data';
  12. import {
  13. LegendDisplayMode,
  14. TooltipDisplayMode,
  15. VisibilityMode,
  16. GraphGradientMode,
  17. StackingMode,
  18. SortOrder,
  19. } from '@grafana/schema';
  20. import { BarChartFieldConfig } from './models.gen';
  21. import { BarChartOptionsEX, prepareBarChartDisplayValues, preparePlotConfigBuilder } from './utils';
  22. function mockDataFrame() {
  23. const df1 = new MutableDataFrame({
  24. refId: 'A',
  25. fields: [{ name: 'ts', type: FieldType.string, values: ['a', 'b', 'c'] }],
  26. });
  27. const df2 = new MutableDataFrame({
  28. refId: 'B',
  29. fields: [{ name: 'ts', type: FieldType.time, values: [1, 2, 4] }],
  30. });
  31. const f1Config: FieldConfig<BarChartFieldConfig> = {
  32. displayName: 'Metric 1',
  33. decimals: 2,
  34. unit: 'm/s',
  35. custom: {
  36. gradientMode: GraphGradientMode.Opacity,
  37. lineWidth: 2,
  38. fillOpacity: 0.1,
  39. },
  40. };
  41. const f2Config: FieldConfig<BarChartFieldConfig> = {
  42. displayName: 'Metric 2',
  43. decimals: 2,
  44. unit: 'kWh',
  45. custom: {
  46. gradientMode: GraphGradientMode.Hue,
  47. lineWidth: 2,
  48. fillOpacity: 0.1,
  49. },
  50. };
  51. df1.addField({
  52. name: 'metric1',
  53. type: FieldType.number,
  54. config: f1Config,
  55. state: {},
  56. });
  57. df2.addField({
  58. name: 'metric2',
  59. type: FieldType.number,
  60. config: f2Config,
  61. state: {},
  62. });
  63. const info = prepareBarChartDisplayValues([df1], createTheme(), {} as any);
  64. if (!('aligned' in info)) {
  65. throw new Error('Bar chart not prepared correctly');
  66. }
  67. return info.aligned;
  68. }
  69. jest.mock('@grafana/data', () => ({
  70. ...(jest.requireActual('@grafana/data') as any),
  71. DefaultTimeZone: 'utc',
  72. }));
  73. describe('BarChart utils', () => {
  74. describe('preparePlotConfigBuilder', () => {
  75. const frame = mockDataFrame();
  76. const config: BarChartOptionsEX = {
  77. orientation: VizOrientation.Auto,
  78. groupWidth: 20,
  79. barWidth: 2,
  80. showValue: VisibilityMode.Always,
  81. legend: {
  82. displayMode: LegendDisplayMode.List,
  83. placement: 'bottom',
  84. calcs: [],
  85. },
  86. xTickLabelRotation: 0,
  87. xTickLabelMaxLength: 20,
  88. stacking: StackingMode.None,
  89. tooltip: {
  90. mode: TooltipDisplayMode.None,
  91. sort: SortOrder.None,
  92. },
  93. text: {
  94. valueSize: 10,
  95. },
  96. rawValue: (seriesIdx: number, valueIdx: number) => frame.fields[seriesIdx].values.get(valueIdx),
  97. };
  98. it.each([VizOrientation.Auto, VizOrientation.Horizontal, VizOrientation.Vertical])('orientation', (v) => {
  99. const result = preparePlotConfigBuilder({
  100. ...config,
  101. orientation: v,
  102. frame: frame!,
  103. theme: createTheme(),
  104. timeZone: DefaultTimeZone,
  105. getTimeRange: getDefaultTimeRange,
  106. eventBus: new EventBusSrv(),
  107. allFrames: [frame],
  108. }).getConfig();
  109. expect(result).toMatchSnapshot();
  110. });
  111. it.each([VisibilityMode.Always, VisibilityMode.Auto])('value visibility', (v) => {
  112. expect(
  113. preparePlotConfigBuilder({
  114. ...config,
  115. showValue: v,
  116. frame: frame!,
  117. theme: createTheme(),
  118. timeZone: DefaultTimeZone,
  119. getTimeRange: getDefaultTimeRange,
  120. eventBus: new EventBusSrv(),
  121. allFrames: [frame],
  122. }).getConfig()
  123. ).toMatchSnapshot();
  124. });
  125. it.each([StackingMode.None, StackingMode.Percent, StackingMode.Normal])('stacking', (v) => {
  126. expect(
  127. preparePlotConfigBuilder({
  128. ...config,
  129. stacking: v,
  130. frame: frame!,
  131. theme: createTheme(),
  132. timeZone: DefaultTimeZone,
  133. getTimeRange: getDefaultTimeRange,
  134. eventBus: new EventBusSrv(),
  135. allFrames: [frame],
  136. }).getConfig()
  137. ).toMatchSnapshot();
  138. });
  139. });
  140. describe('prepareGraphableFrames', () => {
  141. it('will warn when there is no data in the response', () => {
  142. const result = prepareBarChartDisplayValues([], createTheme(), { stacking: StackingMode.None } as any);
  143. const warning = assertIsDefined('warn' in result ? result : null);
  144. expect(warning.warn).toEqual('No data in response');
  145. });
  146. it('will warn when there is no string or time field', () => {
  147. const df = new MutableDataFrame({
  148. fields: [
  149. { name: 'a', type: FieldType.other, values: [1, 2, 3, 4, 5] },
  150. { name: 'value', values: [1, 2, 3, 4, 5] },
  151. ],
  152. });
  153. const result = prepareBarChartDisplayValues([df], createTheme(), { stacking: StackingMode.None } as any);
  154. const warning = assertIsDefined('warn' in result ? result : null);
  155. expect(warning.warn).toEqual('Bar charts requires a string or time field');
  156. expect(warning).not.toHaveProperty('viz');
  157. });
  158. it('will warn when there are no numeric fields in the response', () => {
  159. const df = new MutableDataFrame({
  160. fields: [
  161. { name: 'a', type: FieldType.string, values: ['a', 'b', 'c', 'd', 'e'] },
  162. { name: 'value', type: FieldType.boolean, values: [true, true, true, true, true] },
  163. ],
  164. });
  165. const result = prepareBarChartDisplayValues([df], createTheme(), { stacking: StackingMode.None } as any);
  166. const warning = assertIsDefined('warn' in result ? result : null);
  167. expect(warning.warn).toEqual('No numeric fields found');
  168. expect(warning).not.toHaveProperty('viz');
  169. });
  170. it('will convert NaN and Infinty to nulls', () => {
  171. const df = new MutableDataFrame({
  172. fields: [
  173. { name: 'a', type: FieldType.string, values: ['a', 'b', 'c', 'd', 'e'] },
  174. { name: 'value', values: [-10, NaN, 10, -Infinity, +Infinity] },
  175. ],
  176. });
  177. const result = prepareBarChartDisplayValues([df], createTheme(), { stacking: StackingMode.None } as any);
  178. const displayValues = assertIsDefined('viz' in result ? result : null);
  179. const field = displayValues.viz[0].fields[1];
  180. expect(field.values.toArray()).toMatchInlineSnapshot(`
  181. Array [
  182. -10,
  183. null,
  184. 10,
  185. null,
  186. null,
  187. ]
  188. `);
  189. });
  190. it('should sort fields when legend sortBy and sortDesc are set', () => {
  191. const frame = new MutableDataFrame({
  192. fields: [
  193. { name: 'string', type: FieldType.string, values: ['a', 'b', 'c'] },
  194. { name: 'a', values: [-10, 20, 10], state: { calcs: { min: -10 } } },
  195. { name: 'b', values: [20, 20, 20], state: { calcs: { min: 20 } } },
  196. { name: 'c', values: [10, 10, 10], state: { calcs: { min: 10 } } },
  197. ],
  198. });
  199. const resultAsc = prepareBarChartDisplayValues([frame], createTheme(), {
  200. legend: { sortBy: 'Min', sortDesc: false },
  201. } as any);
  202. const displayValuesAsc = assertIsDefined('viz' in resultAsc ? resultAsc : null).viz[0];
  203. expect(displayValuesAsc.fields[0].type).toBe(FieldType.string);
  204. expect(displayValuesAsc.fields[1].name).toBe('a');
  205. expect(displayValuesAsc.fields[2].name).toBe('c');
  206. expect(displayValuesAsc.fields[3].name).toBe('b');
  207. const resultDesc = prepareBarChartDisplayValues([frame], createTheme(), {
  208. legend: { sortBy: 'Min', sortDesc: true },
  209. } as any);
  210. const displayValuesDesc = assertIsDefined('viz' in resultDesc ? resultDesc : null).viz[0];
  211. expect(displayValuesDesc.fields[0].type).toBe(FieldType.string);
  212. expect(displayValuesDesc.fields[1].name).toBe('b');
  213. expect(displayValuesDesc.fields[2].name).toBe('c');
  214. expect(displayValuesDesc.fields[3].name).toBe('a');
  215. });
  216. });
  217. });