template_srv.test.ts 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. import { dateTime, TimeRange } from '@grafana/data';
  2. import { setDataSourceSrv } from '@grafana/runtime';
  3. import { silenceConsoleOutput } from '../../../test/core/utils/silenceConsoleOutput';
  4. import { initTemplateSrv } from '../../../test/helpers/initTemplateSrv';
  5. import { mockDataSource, MockDataSourceSrv } from '../alerting/unified/mocks';
  6. import { VariableAdapter, variableAdapters } from '../variables/adapters';
  7. import { createAdHocVariableAdapter } from '../variables/adhoc/adapter';
  8. import { createQueryVariableAdapter } from '../variables/query/adapter';
  9. import { VariableModel } from '../variables/types';
  10. import { FormatRegistryID } from './formatRegistry';
  11. const key = 'key';
  12. variableAdapters.setInit(() => [
  13. createQueryVariableAdapter() as unknown as VariableAdapter<VariableModel>,
  14. createAdHocVariableAdapter() as unknown as VariableAdapter<VariableModel>,
  15. ]);
  16. describe('templateSrv', () => {
  17. silenceConsoleOutput();
  18. let _templateSrv: any;
  19. describe('init', () => {
  20. beforeEach(() => {
  21. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: 'oogle' } }]);
  22. });
  23. it('should initialize template data', () => {
  24. const target = _templateSrv.replace('this.[[test]].filters');
  25. expect(target).toBe('this.oogle.filters');
  26. });
  27. });
  28. describe('replace can pass scoped vars', () => {
  29. beforeEach(() => {
  30. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: 'oogle' } }]);
  31. });
  32. it('scoped vars should support objects', () => {
  33. const target = _templateSrv.replace('${series.name} ${series.nested.field}', {
  34. series: { value: { name: 'Server1', nested: { field: 'nested' } } },
  35. });
  36. expect(target).toBe('Server1 nested');
  37. });
  38. it('built in vars should support objects', () => {
  39. _templateSrv.setGlobalVariable('__dashboard', {
  40. value: { name: 'hello' },
  41. });
  42. const target = _templateSrv.replace('${__dashboard.name}');
  43. expect(target).toBe('hello');
  44. });
  45. it('scoped vars should support objects with propert names with dot', () => {
  46. const target = _templateSrv.replace('${series.name} ${series.nested["field.with.dot"]}', {
  47. series: { value: { name: 'Server1', nested: { 'field.with.dot': 'nested' } } },
  48. });
  49. expect(target).toBe('Server1 nested');
  50. });
  51. it('scoped vars should support arrays of objects', () => {
  52. const target = _templateSrv.replace('${series.rows[0].name} ${series.rows[1].name}', {
  53. series: { value: { rows: [{ name: 'first' }, { name: 'second' }] } },
  54. });
  55. expect(target).toBe('first second');
  56. });
  57. it('should replace $test with scoped value', () => {
  58. const target = _templateSrv.replace('this.$test.filters', {
  59. test: { value: 'mupp', text: 'asd' },
  60. });
  61. expect(target).toBe('this.mupp.filters');
  62. });
  63. it('should replace ${test} with scoped value', () => {
  64. const target = _templateSrv.replace('this.${test}.filters', {
  65. test: { value: 'mupp', text: 'asd' },
  66. });
  67. expect(target).toBe('this.mupp.filters');
  68. });
  69. it('should replace ${test:glob} with scoped value', () => {
  70. const target = _templateSrv.replace('this.${test:glob}.filters', {
  71. test: { value: 'mupp', text: 'asd' },
  72. });
  73. expect(target).toBe('this.mupp.filters');
  74. });
  75. it('should replace $test with scoped text', () => {
  76. const target = _templateSrv.replaceWithText('this.$test.filters', {
  77. test: { value: 'mupp', text: 'asd' },
  78. });
  79. expect(target).toBe('this.asd.filters');
  80. });
  81. it('should replace ${test} with scoped text', () => {
  82. const target = _templateSrv.replaceWithText('this.${test}.filters', {
  83. test: { value: 'mupp', text: 'asd' },
  84. });
  85. expect(target).toBe('this.asd.filters');
  86. });
  87. it('should replace ${test.name} with scoped text', () => {
  88. const target = _templateSrv.replaceWithText('this.${test.name}.filters', {
  89. test: { value: { name: 'mupp' }, text: 'asd' },
  90. });
  91. expect(target).toBe('this.mupp.filters');
  92. });
  93. it('should not replace ${test:glob} with scoped text', () => {
  94. const target = _templateSrv.replaceWithText('this.${test:glob}.filters', {
  95. test: { value: 'mupp', text: 'asd' },
  96. });
  97. expect(target).toBe('this.mupp.filters');
  98. });
  99. });
  100. describe('getAdhocFilters', () => {
  101. beforeEach(() => {
  102. _templateSrv = initTemplateSrv(key, [
  103. {
  104. type: 'datasource',
  105. name: 'ds',
  106. current: { value: 'logstash', text: 'logstash' },
  107. },
  108. { type: 'adhoc', name: 'test', datasource: { uid: 'oogle' }, filters: [1] },
  109. { type: 'adhoc', name: 'test2', datasource: { uid: '$ds' }, filters: [2] },
  110. ]);
  111. setDataSourceSrv(
  112. new MockDataSourceSrv({
  113. oogle: mockDataSource({
  114. name: 'oogle',
  115. uid: 'oogle',
  116. }),
  117. })
  118. );
  119. });
  120. it('should return filters if datasourceName match', () => {
  121. const filters = _templateSrv.getAdhocFilters('oogle');
  122. expect(filters).toMatchObject([1]);
  123. });
  124. it('should return empty array if datasourceName does not match', () => {
  125. const filters = _templateSrv.getAdhocFilters('oogleasdasd');
  126. expect(filters).toMatchObject([]);
  127. });
  128. it('should return filters when datasourceName match via data source variable', () => {
  129. const filters = _templateSrv.getAdhocFilters('logstash');
  130. expect(filters).toMatchObject([2]);
  131. });
  132. });
  133. describe('replace can pass multi / all format', () => {
  134. beforeEach(() => {
  135. _templateSrv = initTemplateSrv(key, [
  136. {
  137. type: 'query',
  138. name: 'test',
  139. current: { value: ['value1', 'value2'] },
  140. },
  141. ]);
  142. });
  143. it('should replace $test with globbed value', () => {
  144. const target = _templateSrv.replace('this.$test.filters', {}, 'glob');
  145. expect(target).toBe('this.{value1,value2}.filters');
  146. });
  147. describe('when the globbed variable only has one value', () => {
  148. beforeEach(() => {
  149. _templateSrv = initTemplateSrv(key, [
  150. {
  151. type: 'query',
  152. name: 'test',
  153. current: { value: ['value1'] },
  154. },
  155. ]);
  156. });
  157. it('should not glob the value', () => {
  158. const target = _templateSrv.replace('this.$test.filters', {}, 'glob');
  159. expect(target).toBe('this.value1.filters');
  160. });
  161. });
  162. it('should replace ${test} with globbed value', () => {
  163. const target = _templateSrv.replace('this.${test}.filters', {}, 'glob');
  164. expect(target).toBe('this.{value1,value2}.filters');
  165. });
  166. it('should replace ${test:glob} with globbed value', () => {
  167. const target = _templateSrv.replace('this.${test:glob}.filters', {});
  168. expect(target).toBe('this.{value1,value2}.filters');
  169. });
  170. it('should replace $test with piped value', () => {
  171. const target = _templateSrv.replace('this=$test', {}, 'pipe');
  172. expect(target).toBe('this=value1|value2');
  173. });
  174. it('should replace ${test} with piped value', () => {
  175. const target = _templateSrv.replace('this=${test}', {}, 'pipe');
  176. expect(target).toBe('this=value1|value2');
  177. });
  178. it('should replace ${test:pipe} with piped value', () => {
  179. const target = _templateSrv.replace('this=${test:pipe}', {});
  180. expect(target).toBe('this=value1|value2');
  181. });
  182. it('should replace ${test:pipe} with piped value and $test with globbed value', () => {
  183. const target = _templateSrv.replace('${test:pipe},$test', {}, 'glob');
  184. expect(target).toBe('value1|value2,{value1,value2}');
  185. });
  186. });
  187. describe('variable with all option', () => {
  188. beforeEach(() => {
  189. _templateSrv = initTemplateSrv(key, [
  190. {
  191. type: 'query',
  192. name: 'test',
  193. current: { value: '$__all' },
  194. options: [{ value: '$__all' }, { value: 'value1' }, { value: 'value2' }],
  195. },
  196. ]);
  197. });
  198. it('should replace $test with formatted all value', () => {
  199. const target = _templateSrv.replace('this.$test.filters', {}, 'glob');
  200. expect(target).toBe('this.{value1,value2}.filters');
  201. });
  202. it('should replace ${test} with formatted all value', () => {
  203. const target = _templateSrv.replace('this.${test}.filters', {}, 'glob');
  204. expect(target).toBe('this.{value1,value2}.filters');
  205. });
  206. it('should replace ${test:glob} with formatted all value', () => {
  207. const target = _templateSrv.replace('this.${test:glob}.filters', {});
  208. expect(target).toBe('this.{value1,value2}.filters');
  209. });
  210. it('should replace ${test:pipe} with piped value and $test with globbed value', () => {
  211. const target = _templateSrv.replace('${test:pipe},$test', {}, 'glob');
  212. expect(target).toBe('value1|value2,{value1,value2}');
  213. });
  214. it('should replace ${test:queryparam} with correct query parameter', () => {
  215. const target = _templateSrv.replace('${test:queryparam}', {});
  216. expect(target).toBe('var-test=All');
  217. });
  218. });
  219. describe('variable with all option and custom value', () => {
  220. beforeEach(() => {
  221. _templateSrv = initTemplateSrv(key, [
  222. {
  223. type: 'query',
  224. name: 'test',
  225. current: { value: '$__all' },
  226. allValue: '*',
  227. options: [{ value: 'value1' }, { value: 'value2' }],
  228. },
  229. ]);
  230. });
  231. it('should replace $test with formatted all value', () => {
  232. const target = _templateSrv.replace('this.$test.filters', {}, 'glob');
  233. expect(target).toBe('this.*.filters');
  234. });
  235. it('should replace ${test} with formatted all value', () => {
  236. const target = _templateSrv.replace('this.${test}.filters', {}, 'glob');
  237. expect(target).toBe('this.*.filters');
  238. });
  239. it('should replace ${test:glob} with formatted all value', () => {
  240. const target = _templateSrv.replace('this.${test:glob}.filters', {});
  241. expect(target).toBe('this.*.filters');
  242. });
  243. it('should replace ${test:text} with "all" value', () => {
  244. const target = _templateSrv.replace('this.${test:text}.filters', {});
  245. expect(target).toBe('this.All.filters');
  246. });
  247. it('should not escape custom all value', () => {
  248. const target = _templateSrv.replace('this.$test', {}, 'regex');
  249. expect(target).toBe('this.*');
  250. });
  251. it('should replace ${test:queryparam} with correct query parameter', () => {
  252. const target = _templateSrv.replace('${test:queryparam}', {});
  253. expect(target).toBe('var-test=All');
  254. });
  255. });
  256. describe('lucene format', () => {
  257. it('should properly escape $test with lucene escape sequences', () => {
  258. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: 'value/4' } }]);
  259. const target = _templateSrv.replace('this:$test', {}, 'lucene');
  260. expect(target).toBe('this:value\\/4');
  261. });
  262. it('should properly escape ${test} with lucene escape sequences', () => {
  263. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: 'value/4' } }]);
  264. const target = _templateSrv.replace('this:${test}', {}, 'lucene');
  265. expect(target).toBe('this:value\\/4');
  266. });
  267. it('should properly escape ${test:lucene} with lucene escape sequences', () => {
  268. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: 'value/4' } }]);
  269. const target = _templateSrv.replace('this:${test:lucene}', {});
  270. expect(target).toBe('this:value\\/4');
  271. });
  272. });
  273. describe('html format', () => {
  274. it('should encode values html escape sequences', () => {
  275. _templateSrv = initTemplateSrv(key, [
  276. { type: 'query', name: 'test', current: { value: '<script>alert(asd)</script>' } },
  277. ]);
  278. const target = _templateSrv.replace('$test', {}, 'html');
  279. expect(target).toBe('&lt;script&gt;alert(asd)&lt;/script&gt;');
  280. });
  281. });
  282. describe('format variable to string values', () => {
  283. it('single value should return value', () => {
  284. const result = _templateSrv.formatValue('test');
  285. expect(result).toBe('test');
  286. });
  287. it('should use glob format when unknown format provided', () => {
  288. let result = _templateSrv.formatValue('test', 'nonexistentformat');
  289. expect(result).toBe('test');
  290. result = _templateSrv.formatValue(['test', 'test1'], 'nonexistentformat');
  291. expect(result).toBe('{test,test1}');
  292. });
  293. it('multi value and glob format should render glob string', () => {
  294. const result = _templateSrv.formatValue(['test', 'test2'], 'glob');
  295. expect(result).toBe('{test,test2}');
  296. });
  297. it('multi value and lucene should render as lucene expr', () => {
  298. const result = _templateSrv.formatValue(['test', 'test2'], 'lucene');
  299. expect(result).toBe('("test" OR "test2")');
  300. });
  301. it('multi value and regex format should render regex string', () => {
  302. const result = _templateSrv.formatValue(['test.', 'test2'], 'regex');
  303. expect(result).toBe('(test\\.|test2)');
  304. });
  305. it('multi value and pipe should render pipe string', () => {
  306. const result = _templateSrv.formatValue(['test', 'test2'], 'pipe');
  307. expect(result).toBe('test|test2');
  308. });
  309. it('multi value and distributed should render distributed string', () => {
  310. const result = _templateSrv.formatValue(['test', 'test2'], 'distributed', {
  311. name: 'build',
  312. });
  313. expect(result).toBe('test,build=test2');
  314. });
  315. it('multi value and distributed should render when not string', () => {
  316. const result = _templateSrv.formatValue(['test'], 'distributed', {
  317. name: 'build',
  318. });
  319. expect(result).toBe('test');
  320. });
  321. it('multi value and csv format should render csv string', () => {
  322. const result = _templateSrv.formatValue(['test', 'test2'], 'csv');
  323. expect(result).toBe('test,test2');
  324. });
  325. it('multi value and percentencode format should render percent-encoded string', () => {
  326. const result = _templateSrv.formatValue(['foo()bar BAZ', 'test2'], 'percentencode');
  327. expect(result).toBe('%7Bfoo%28%29bar%20BAZ%2Ctest2%7D');
  328. });
  329. it('slash should be properly escaped in regex format', () => {
  330. const result = _templateSrv.formatValue('Gi3/14', 'regex');
  331. expect(result).toBe('Gi3\\/14');
  332. });
  333. it('single value and singlequote format should render string with value enclosed in single quotes', () => {
  334. const result = _templateSrv.formatValue('test', 'singlequote');
  335. expect(result).toBe("'test'");
  336. });
  337. it('multi value and singlequote format should render string with values enclosed in single quotes', () => {
  338. const result = _templateSrv.formatValue(['test', "test'2"], 'singlequote');
  339. expect(result).toBe("'test','test\\'2'");
  340. });
  341. it('single value and doublequote format should render string with value enclosed in double quotes', () => {
  342. const result = _templateSrv.formatValue('test', 'doublequote');
  343. expect(result).toBe('"test"');
  344. });
  345. it('multi value and doublequote format should render string with values enclosed in double quotes', () => {
  346. const result = _templateSrv.formatValue(['test', 'test"2'], 'doublequote');
  347. expect(result).toBe('"test","test\\"2"');
  348. });
  349. it('single value and sqlstring format should render string with value enclosed in single quotes', () => {
  350. const result = _templateSrv.formatValue("test'value", 'sqlstring');
  351. expect(result).toBe(`'test''value'`);
  352. });
  353. it('multi value and sqlstring format should render string with values enclosed in single quotes', () => {
  354. const result = _templateSrv.formatValue(['test', "test'value2"], 'sqlstring');
  355. expect(result).toBe(`'test','test''value2'`);
  356. });
  357. it('raw format should leave value intact and do no escaping', () => {
  358. const result = _templateSrv.formatValue("'test\n", 'raw');
  359. expect(result).toBe("'test\n");
  360. });
  361. });
  362. describe('can check if variable exists', () => {
  363. beforeEach(() => {
  364. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: 'oogle' } }]);
  365. });
  366. it('should return true if $test exists', () => {
  367. const result = _templateSrv.containsTemplate('$test');
  368. expect(result).toBe(true);
  369. });
  370. it('should return true if $test exists in string', () => {
  371. const result = _templateSrv.containsTemplate('something $test something');
  372. expect(result).toBe(true);
  373. });
  374. it('should return true if [[test]] exists in string', () => {
  375. const result = _templateSrv.containsTemplate('something [[test]] something');
  376. expect(result).toBe(true);
  377. });
  378. it('should return true if [[test:csv]] exists in string', () => {
  379. const result = _templateSrv.containsTemplate('something [[test:csv]] something');
  380. expect(result).toBe(true);
  381. });
  382. it('should return true if ${test} exists in string', () => {
  383. const result = _templateSrv.containsTemplate('something ${test} something');
  384. expect(result).toBe(true);
  385. });
  386. it('should return true if ${test:raw} exists in string', () => {
  387. const result = _templateSrv.containsTemplate('something ${test:raw} something');
  388. expect(result).toBe(true);
  389. });
  390. it('should return null if there are no variables in string', () => {
  391. const result = _templateSrv.containsTemplate('string without variables');
  392. expect(result).toBe(false);
  393. });
  394. });
  395. describe('can highlight variables in string', () => {
  396. beforeEach(() => {
  397. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: 'oogle' } }]);
  398. });
  399. it('should insert html', () => {
  400. const result = _templateSrv.highlightVariablesAsHtml('$test');
  401. expect(result).toBe('<span class="template-variable">$test</span>');
  402. });
  403. it('should insert html anywhere in string', () => {
  404. const result = _templateSrv.highlightVariablesAsHtml('this $test ok');
  405. expect(result).toBe('this <span class="template-variable">$test</span> ok');
  406. });
  407. it('should ignore if variables does not exist', () => {
  408. const result = _templateSrv.highlightVariablesAsHtml('this $google ok');
  409. expect(result).toBe('this $google ok');
  410. });
  411. });
  412. describe('updateIndex with simple value', () => {
  413. beforeEach(() => {
  414. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: 'muuuu' } }]);
  415. });
  416. it('should set current value and update template data', () => {
  417. const target = _templateSrv.replace('this.[[test]].filters');
  418. expect(target).toBe('this.muuuu.filters');
  419. });
  420. });
  421. describe('replaceWithText', () => {
  422. beforeEach(() => {
  423. _templateSrv = initTemplateSrv(key, [
  424. {
  425. type: 'query',
  426. name: 'server',
  427. current: { value: '{asd,asd2}', text: 'All' },
  428. },
  429. {
  430. type: 'interval',
  431. name: 'period',
  432. current: { value: '$__auto_interval_interval', text: 'auto' },
  433. },
  434. {
  435. type: 'textbox',
  436. name: 'empty_on_init',
  437. current: { value: '', text: '' },
  438. },
  439. {
  440. type: 'custom',
  441. name: 'foo',
  442. current: { value: 'constructor', text: 'constructor' },
  443. },
  444. ]);
  445. _templateSrv.setGrafanaVariable('$__auto_interval_interval', '13m');
  446. _templateSrv.updateIndex();
  447. });
  448. it('should replace with text except for grafanaVariables', () => {
  449. const target = _templateSrv.replaceWithText('Server: $server, period: $period');
  450. expect(target).toBe('Server: All, period: 13m');
  451. });
  452. it('should replace empty string-values with an empty string', () => {
  453. const target = _templateSrv.replaceWithText('Hello $empty_on_init');
  454. expect(target).toBe('Hello ');
  455. });
  456. it('should not return a string representation of a constructor property', () => {
  457. const target = _templateSrv.replaceWithText('$foo');
  458. expect(target).not.toBe('function Object() { [native code] }');
  459. expect(target).toBe('constructor');
  460. });
  461. });
  462. describe('replaceWithText can pass all / multi value', () => {
  463. beforeEach(() => {
  464. _templateSrv = initTemplateSrv(key, [
  465. {
  466. type: 'query',
  467. name: 'server',
  468. current: { value: ['server1', 'server2'], text: ['Server 1', 'Server 2'] },
  469. },
  470. {
  471. type: 'textbox',
  472. name: 'empty_on_init',
  473. current: { value: '', text: '' },
  474. },
  475. {
  476. type: 'query',
  477. name: 'databases',
  478. current: { value: '$__all', text: '' },
  479. options: [{ value: '$__all' }, { value: 'db1', text: 'Database 1' }, { value: 'db2', text: 'Database 2' }],
  480. },
  481. {
  482. type: 'custom',
  483. name: 'custom_all_value',
  484. allValue: 'CUSTOM_ALL',
  485. current: { value: '$__all', text: '' },
  486. options: [{ value: '$__all' }, { value: 'A-Value', text: 'This A' }, { value: 'B-Value', text: 'This B' }],
  487. },
  488. ]);
  489. _templateSrv.updateIndex();
  490. });
  491. it('should replace with text with variable label', () => {
  492. const target = _templateSrv.replaceWithText('Server: $server');
  493. expect(target).toBe('Server: Server 1 + Server 2');
  494. });
  495. it('should replace empty string-values with an empty string', () => {
  496. const target = _templateSrv.replaceWithText('Hello $empty_on_init');
  497. expect(target).toBe('Hello ');
  498. });
  499. it('should replace $__all with All', () => {
  500. const target = _templateSrv.replaceWithText('Db: $databases');
  501. expect(target).toBe('Db: All');
  502. });
  503. it('should replace $__all with All for values with custom all', () => {
  504. const target = _templateSrv.replaceWithText('Custom: $custom_all_value');
  505. expect(target).toBe('Custom: All');
  506. });
  507. });
  508. describe('built in interval variables', () => {
  509. beforeEach(() => {
  510. _templateSrv = initTemplateSrv(key, []);
  511. });
  512. it('should replace $__interval_ms with interval milliseconds', () => {
  513. const target = _templateSrv.replace('10 * $__interval_ms', {
  514. __interval_ms: { text: '100', value: '100' },
  515. });
  516. expect(target).toBe('10 * 100');
  517. });
  518. });
  519. describe('date formating', () => {
  520. beforeEach(() => {
  521. _templateSrv = initTemplateSrv(key, [], {
  522. from: dateTime(1594671549254),
  523. to: dateTime(1595237229747),
  524. } as TimeRange);
  525. });
  526. it('should replace ${__from} with ms epoch value', () => {
  527. const target = _templateSrv.replace('${__from}');
  528. expect(target).toBe('1594671549254');
  529. });
  530. it('should replace ${__from:date:seconds} with epoch in seconds', () => {
  531. const target = _templateSrv.replace('${__from:date:seconds}');
  532. expect(target).toBe('1594671549');
  533. });
  534. it('should replace ${__from:date} with iso date', () => {
  535. const target = _templateSrv.replace('${__from:date}');
  536. expect(target).toBe('2020-07-13T20:19:09.254Z');
  537. });
  538. it('should replace ${__from:date:iso} with iso date', () => {
  539. const target = _templateSrv.replace('${__from:date:iso}');
  540. expect(target).toBe('2020-07-13T20:19:09.254Z');
  541. });
  542. it('should replace ${__from:date:YYYY-MM} using custom format', () => {
  543. const target = _templateSrv.replace('${__from:date:YYYY-MM}');
  544. expect(target).toBe('2020-07');
  545. });
  546. });
  547. describe('handle objects gracefully', () => {
  548. beforeEach(() => {
  549. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value: { test: 'A' } } }]);
  550. });
  551. it('should not pass object to custom function', () => {
  552. let passedValue: any = null;
  553. _templateSrv.replace('this.${test}.filters', {}, (value: any) => {
  554. passedValue = value;
  555. });
  556. expect(passedValue).toBe('[object Object]');
  557. });
  558. });
  559. describe('handle objects gracefully and call toString if defined', () => {
  560. beforeEach(() => {
  561. const value = { test: 'A', toString: () => 'hello' };
  562. _templateSrv = initTemplateSrv(key, [{ type: 'query', name: 'test', current: { value } }]);
  563. });
  564. it('should not pass object to custom function', () => {
  565. let passedValue: any = null;
  566. _templateSrv.replace('this.${test}.filters', {}, (value: any) => {
  567. passedValue = value;
  568. });
  569. expect(passedValue).toBe('hello');
  570. });
  571. });
  572. describe('adhoc variables', () => {
  573. beforeEach(() => {
  574. _templateSrv = initTemplateSrv(key, [
  575. {
  576. type: 'adhoc',
  577. name: 'adhoc',
  578. filters: [
  579. {
  580. condition: '',
  581. key: 'alertstate',
  582. operator: '=',
  583. value: 'firing',
  584. },
  585. {
  586. condition: '',
  587. key: 'alertname',
  588. operator: '=',
  589. value: 'ExampleAlertAlwaysFiring',
  590. },
  591. ],
  592. },
  593. ]);
  594. });
  595. it(`should not be handled by any registry items except for queryparam`, () => {
  596. const registryItems = Object.values(FormatRegistryID);
  597. for (const registryItem of registryItems) {
  598. if (registryItem === FormatRegistryID.queryParam) {
  599. continue;
  600. }
  601. const firstTarget = _templateSrv.replace(`\${adhoc:${registryItem}}`, {});
  602. expect(firstTarget).toBe('');
  603. const secondTarget = _templateSrv.replace('${adhoc}', {}, registryItem);
  604. expect(secondTarget).toBe('');
  605. }
  606. });
  607. });
  608. describe('queryparam', () => {
  609. beforeEach(() => {
  610. _templateSrv = initTemplateSrv(key, [
  611. {
  612. type: 'query',
  613. name: 'single',
  614. current: { value: 'value1' },
  615. options: [{ value: 'value1' }, { value: 'value2' }],
  616. },
  617. {
  618. type: 'query',
  619. name: 'multi',
  620. current: { value: ['value1', 'value2'] },
  621. options: [{ value: 'value1' }, { value: 'value2' }],
  622. },
  623. {
  624. type: 'adhoc',
  625. name: 'adhoc',
  626. filters: [
  627. {
  628. condition: '',
  629. key: 'alertstate',
  630. operator: '=',
  631. value: 'firing',
  632. },
  633. {
  634. condition: '',
  635. key: 'alertname',
  636. operator: '=',
  637. value: 'ExampleAlertAlwaysFiring',
  638. },
  639. ],
  640. },
  641. ]);
  642. });
  643. it('query variable with single value with queryparam format should return correct queryparam', () => {
  644. const target = _templateSrv.replace(`\${single:queryparam}`, {});
  645. expect(target).toBe('var-single=value1');
  646. });
  647. it('query variable with single value with queryparam format and scoped vars should return correct queryparam', () => {
  648. const target = _templateSrv.replace(`\${single:queryparam}`, { single: { value: 'value1', text: 'value1' } });
  649. expect(target).toBe('var-single=value1');
  650. });
  651. it('query variable with single value and queryparam format should return correct queryparam', () => {
  652. const target = _templateSrv.replace('${single}', {}, 'queryparam');
  653. expect(target).toBe('var-single=value1');
  654. });
  655. it('query variable with single value and queryparam format and scoped vars should return correct queryparam', () => {
  656. const target = _templateSrv.replace('${single}', { single: { value: 'value1', text: 'value1' } }, 'queryparam');
  657. expect(target).toBe('var-single=value1');
  658. });
  659. it('query variable with multi value with queryparam format should return correct queryparam', () => {
  660. const target = _templateSrv.replace(`\${multi:queryparam}`, {});
  661. expect(target).toBe('var-multi=value1&var-multi=value2');
  662. });
  663. it('query variable with multi value with queryparam format and scoped vars should return correct queryparam', () => {
  664. const target = _templateSrv.replace(`\${multi:queryparam}`, { multi: { value: 'value2', text: 'value2' } });
  665. expect(target).toBe('var-multi=value2');
  666. });
  667. it('query variable with multi value and queryparam format should return correct queryparam', () => {
  668. const target = _templateSrv.replace('${multi}', {}, 'queryparam');
  669. expect(target).toBe('var-multi=value1&var-multi=value2');
  670. });
  671. it('query variable with multi value and queryparam format and scoped vars should return correct queryparam', () => {
  672. const target = _templateSrv.replace('${multi}', { multi: { value: 'value2', text: 'value2' } }, 'queryparam');
  673. expect(target).toBe('var-multi=value2');
  674. });
  675. it('query variable with adhoc value with queryparam format should return correct queryparam', () => {
  676. const target = _templateSrv.replace(`\${adhoc:queryparam}`, {});
  677. expect(target).toBe('var-adhoc=alertstate%7C%3D%7Cfiring&var-adhoc=alertname%7C%3D%7CExampleAlertAlwaysFiring');
  678. });
  679. it('query variable with adhoc value with queryparam format should return correct queryparam', () => {
  680. const target = _templateSrv.replace(`\${adhoc:queryparam}`, { adhoc: { value: 'value2', text: 'value2' } });
  681. expect(target).toBe('var-adhoc=value2');
  682. });
  683. it('query variable with adhoc value and queryparam format should return correct queryparam', () => {
  684. const target = _templateSrv.replace('${adhoc}', {}, 'queryparam');
  685. expect(target).toBe('var-adhoc=alertstate%7C%3D%7Cfiring&var-adhoc=alertname%7C%3D%7CExampleAlertAlwaysFiring');
  686. });
  687. it('query variable with adhoc value and queryparam format should return correct queryparam', () => {
  688. const target = _templateSrv.replace('${adhoc}', { adhoc: { value: 'value2', text: 'value2' } }, 'queryparam');
  689. expect(target).toBe('var-adhoc=value2');
  690. });
  691. });
  692. });