Table.test.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import _ from 'lodash';
  2. import TableModel from 'app/core/table_model';
  3. import { getColorDefinitionByName } from '@grafana/ui';
  4. import { ScopedVars } from '@grafana/ui/src/types';
  5. import { getTheme } from '../../themes';
  6. import Table, { ColumnStyle } from './Table';
  7. // TODO: this is commented out with *x* describe!
  8. // Essentially all the elements need to replace the <td> with <div>
  9. xdescribe('when rendering table', () => {
  10. const SemiDarkOrange = getColorDefinitionByName('semi-dark-orange');
  11. describe('given 13 columns', () => {
  12. const table = new TableModel();
  13. table.columns = [
  14. { text: 'Time' },
  15. { text: 'Value' },
  16. { text: 'Colored' },
  17. { text: 'Undefined' },
  18. { text: 'String' },
  19. { text: 'United', unit: 'bps' },
  20. { text: 'Sanitized' },
  21. { text: 'Link' },
  22. { text: 'Array' },
  23. { text: 'Mapping' },
  24. { text: 'RangeMapping' },
  25. { text: 'MappingColored' },
  26. { text: 'RangeMappingColored' },
  27. ];
  28. table.rows = [
  29. [1388556366666, 1230, 40, undefined, '', '', 'my.host.com', 'host1', ['value1', 'value2'], 1, 2, 1, 2],
  30. ];
  31. const styles: ColumnStyle[] = [
  32. {
  33. pattern: 'Time',
  34. type: 'date',
  35. alias: 'Timestamp',
  36. },
  37. {
  38. pattern: '/(Val)ue/',
  39. type: 'number',
  40. unit: 'ms',
  41. decimals: 3,
  42. alias: '$1',
  43. },
  44. {
  45. pattern: 'Colored',
  46. type: 'number',
  47. unit: 'none',
  48. decimals: 1,
  49. colorMode: 'value',
  50. thresholds: [50, 80],
  51. colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
  52. },
  53. {
  54. pattern: 'String',
  55. type: 'string',
  56. },
  57. {
  58. pattern: 'String',
  59. type: 'string',
  60. },
  61. {
  62. pattern: 'United',
  63. type: 'number',
  64. unit: 'ms',
  65. decimals: 2,
  66. },
  67. {
  68. pattern: 'Sanitized',
  69. type: 'string',
  70. sanitize: true,
  71. },
  72. {
  73. pattern: 'Link',
  74. type: 'string',
  75. link: true,
  76. linkUrl: '/dashboard?param=$__cell&param_1=$__cell_1&param_2=$__cell_2',
  77. linkTooltip: '$__cell $__cell_1 $__cell_6',
  78. linkTargetBlank: true,
  79. },
  80. {
  81. pattern: 'Array',
  82. type: 'number',
  83. unit: 'ms',
  84. decimals: 3,
  85. },
  86. {
  87. pattern: 'Mapping',
  88. type: 'string',
  89. mappingType: 1,
  90. valueMaps: [
  91. {
  92. value: '1',
  93. text: 'on',
  94. },
  95. {
  96. value: '0',
  97. text: 'off',
  98. },
  99. {
  100. value: 'HELLO WORLD',
  101. text: 'HELLO GRAFANA',
  102. },
  103. {
  104. value: 'value1, value2',
  105. text: 'value3, value4',
  106. },
  107. ],
  108. },
  109. {
  110. pattern: 'RangeMapping',
  111. type: 'string',
  112. mappingType: 2,
  113. rangeMaps: [
  114. {
  115. from: '1',
  116. to: '3',
  117. text: 'on',
  118. },
  119. {
  120. from: '3',
  121. to: '6',
  122. text: 'off',
  123. },
  124. ],
  125. },
  126. {
  127. pattern: 'MappingColored',
  128. type: 'string',
  129. mappingType: 1,
  130. valueMaps: [
  131. {
  132. value: '1',
  133. text: 'on',
  134. },
  135. {
  136. value: '0',
  137. text: 'off',
  138. },
  139. ],
  140. colorMode: 'value',
  141. thresholds: [1, 2],
  142. colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
  143. },
  144. {
  145. pattern: 'RangeMappingColored',
  146. type: 'string',
  147. mappingType: 2,
  148. rangeMaps: [
  149. {
  150. from: '1',
  151. to: '3',
  152. text: 'on',
  153. },
  154. {
  155. from: '3',
  156. to: '6',
  157. text: 'off',
  158. },
  159. ],
  160. colorMode: 'value',
  161. thresholds: [2, 5],
  162. colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
  163. },
  164. ];
  165. // const sanitize = value => {
  166. // return 'sanitized';
  167. // };
  168. const replaceVariables = (value: any, scopedVars: ScopedVars | undefined) => {
  169. if (scopedVars) {
  170. // For testing variables replacement in link
  171. _.each(scopedVars, (val, key) => {
  172. value = value.replace('$' + key, val.value);
  173. });
  174. }
  175. return value;
  176. };
  177. const renderer = new Table({
  178. styles,
  179. data: table,
  180. replaceVariables,
  181. showHeader: true,
  182. width: 100,
  183. height: 100,
  184. theme: getTheme(),
  185. });
  186. it('time column should be formated', () => {
  187. const html = renderer.renderCell(0, 0, 1388556366666);
  188. expect(html).toBe('<td>2014-01-01T06:06:06Z</td>');
  189. });
  190. it('time column with epoch as string should be formatted', () => {
  191. const html = renderer.renderCell(0, 0, '1388556366666');
  192. expect(html).toBe('<td>2014-01-01T06:06:06Z</td>');
  193. });
  194. it('time column with RFC2822 date as string should be formatted', () => {
  195. const html = renderer.renderCell(0, 0, 'Sat, 01 Dec 2018 01:00:00 GMT');
  196. expect(html).toBe('<td>2018-12-01T01:00:00Z</td>');
  197. });
  198. it('time column with ISO date as string should be formatted', () => {
  199. const html = renderer.renderCell(0, 0, '2018-12-01T01:00:00Z');
  200. expect(html).toBe('<td>2018-12-01T01:00:00Z</td>');
  201. });
  202. it('undefined time column should be rendered as -', () => {
  203. const html = renderer.renderCell(0, 0, undefined);
  204. expect(html).toBe('<td>-</td>');
  205. });
  206. it('null time column should be rendered as -', () => {
  207. const html = renderer.renderCell(0, 0, null);
  208. expect(html).toBe('<td>-</td>');
  209. });
  210. it('number column with unit specified should ignore style unit', () => {
  211. const html = renderer.renderCell(5, 0, 1230);
  212. expect(html).toBe('<td>1.23 kbps</td>');
  213. });
  214. it('number column should be formated', () => {
  215. const html = renderer.renderCell(1, 0, 1230);
  216. expect(html).toBe('<td>1.230 s</td>');
  217. });
  218. it('number style should ignore string values', () => {
  219. const html = renderer.renderCell(1, 0, 'asd');
  220. expect(html).toBe('<td>asd</td>');
  221. });
  222. it('colored cell should have style (handles HEX color values)', () => {
  223. const html = renderer.renderCell(2, 0, 40);
  224. expect(html).toBe('<td style="color:#00ff00">40.0</td>');
  225. });
  226. it('colored cell should have style (handles named color values', () => {
  227. const html = renderer.renderCell(2, 0, 55);
  228. expect(html).toBe(`<td style="color:${SemiDarkOrange.variants.dark}">55.0</td>`);
  229. });
  230. it('colored cell should have style handles(rgb color values)', () => {
  231. const html = renderer.renderCell(2, 0, 85);
  232. expect(html).toBe('<td style="color:rgb(1,0,0)">85.0</td>');
  233. });
  234. it('unformated undefined should be rendered as string', () => {
  235. const html = renderer.renderCell(3, 0, 'value');
  236. expect(html).toBe('<td>value</td>');
  237. });
  238. it('string style with escape html should return escaped html', () => {
  239. const html = renderer.renderCell(4, 0, '&breaking <br /> the <br /> row');
  240. expect(html).toBe('<td>&amp;breaking &lt;br /&gt; the &lt;br /&gt; row</td>');
  241. });
  242. it('undefined formater should return escaped html', () => {
  243. const html = renderer.renderCell(3, 0, '&breaking <br /> the <br /> row');
  244. expect(html).toBe('<td>&amp;breaking &lt;br /&gt; the &lt;br /&gt; row</td>');
  245. });
  246. it('undefined value should render as -', () => {
  247. const html = renderer.renderCell(3, 0, undefined);
  248. expect(html).toBe('<td></td>');
  249. });
  250. it('sanitized value should render as', () => {
  251. const html = renderer.renderCell(6, 0, 'text <a href="http://google.com">link</a>');
  252. expect(html).toBe('<td>sanitized</td>');
  253. });
  254. it('Time column title should be Timestamp', () => {
  255. expect(table.columns[0].title).toBe('Timestamp');
  256. });
  257. it('Value column title should be Val', () => {
  258. expect(table.columns[1].title).toBe('Val');
  259. });
  260. it('Colored column title should be Colored', () => {
  261. expect(table.columns[2].title).toBe('Colored');
  262. });
  263. it('link should render as', () => {
  264. const html = renderer.renderCell(7, 0, 'host1');
  265. const expectedHtml = `
  266. <td class="table-panel-cell-link">
  267. <a href="/dashboard?param=host1&param_1=1230&param_2=40"
  268. target="_blank" data-link-tooltip data-original-title="host1 1230 my.host.com" data-placement="right">
  269. host1
  270. </a>
  271. </td>
  272. `;
  273. expect(normalize(html + '')).toBe(normalize(expectedHtml));
  274. });
  275. it('Array column should not use number as formatter', () => {
  276. const html = renderer.renderCell(8, 0, ['value1', 'value2']);
  277. expect(html).toBe('<td>value1, value2</td>');
  278. });
  279. it('numeric value should be mapped to text', () => {
  280. const html = renderer.renderCell(9, 0, 1);
  281. expect(html).toBe('<td>on</td>');
  282. });
  283. it('string numeric value should be mapped to text', () => {
  284. const html = renderer.renderCell(9, 0, '0');
  285. expect(html).toBe('<td>off</td>');
  286. });
  287. it('string value should be mapped to text', () => {
  288. const html = renderer.renderCell(9, 0, 'HELLO WORLD');
  289. expect(html).toBe('<td>HELLO GRAFANA</td>');
  290. });
  291. it('array column value should be mapped to text', () => {
  292. const html = renderer.renderCell(9, 0, ['value1', 'value2']);
  293. expect(html).toBe('<td>value3, value4</td>');
  294. });
  295. it('value should be mapped to text (range)', () => {
  296. const html = renderer.renderCell(10, 0, 2);
  297. expect(html).toBe('<td>on</td>');
  298. });
  299. it('value should be mapped to text (range)', () => {
  300. const html = renderer.renderCell(10, 0, 5);
  301. expect(html).toBe('<td>off</td>');
  302. });
  303. it('array column value should not be mapped to text', () => {
  304. const html = renderer.renderCell(10, 0, ['value1', 'value2']);
  305. expect(html).toBe('<td>value1, value2</td>');
  306. });
  307. it('value should be mapped to text and colored cell should have style', () => {
  308. const html = renderer.renderCell(11, 0, 1);
  309. expect(html).toBe(`<td style="color:${SemiDarkOrange.variants.dark}">on</td>`);
  310. });
  311. it('value should be mapped to text and colored cell should have style', () => {
  312. const html = renderer.renderCell(11, 0, '1');
  313. expect(html).toBe(`<td style="color:${SemiDarkOrange.variants.dark}">on</td>`);
  314. });
  315. it('value should be mapped to text and colored cell should have style', () => {
  316. const html = renderer.renderCell(11, 0, 0);
  317. expect(html).toBe('<td style="color:#00ff00">off</td>');
  318. });
  319. it('value should be mapped to text and colored cell should have style', () => {
  320. const html = renderer.renderCell(11, 0, '0');
  321. expect(html).toBe('<td style="color:#00ff00">off</td>');
  322. });
  323. it('value should be mapped to text and colored cell should have style', () => {
  324. const html = renderer.renderCell(11, 0, '2.1');
  325. expect(html).toBe('<td style="color:rgb(1,0,0)">2.1</td>');
  326. });
  327. it('value should be mapped to text (range) and colored cell should have style', () => {
  328. const html = renderer.renderCell(12, 0, 0);
  329. expect(html).toBe('<td style="color:#00ff00">0</td>');
  330. });
  331. it('value should be mapped to text (range) and colored cell should have style', () => {
  332. const html = renderer.renderCell(12, 0, 1);
  333. expect(html).toBe('<td style="color:#00ff00">on</td>');
  334. });
  335. it('value should be mapped to text (range) and colored cell should have style', () => {
  336. const html = renderer.renderCell(12, 0, 4);
  337. expect(html).toBe(`<td style="color:${SemiDarkOrange.variants.dark}">off</td>`);
  338. });
  339. it('value should be mapped to text (range) and colored cell should have style', () => {
  340. const html = renderer.renderCell(12, 0, '7.1');
  341. expect(html).toBe('<td style="color:rgb(1,0,0)">7.1</td>');
  342. });
  343. });
  344. });
  345. function normalize(str: string) {
  346. return str.replace(/\s+/gm, ' ').trim();
  347. }