template_srv.test.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. import { TemplateSrv } from '../template_srv';
  2. describe('templateSrv', () => {
  3. let _templateSrv: any;
  4. function initTemplateSrv(variables: any) {
  5. _templateSrv = new TemplateSrv();
  6. _templateSrv.init(variables);
  7. }
  8. describe('init', () => {
  9. beforeEach(() => {
  10. initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'oogle' } }]);
  11. });
  12. it('should initialize template data', () => {
  13. const target = _templateSrv.replace('this.[[test]].filters');
  14. expect(target).toBe('this.oogle.filters');
  15. });
  16. });
  17. describe('replace can pass scoped vars', () => {
  18. beforeEach(() => {
  19. initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'oogle' } }]);
  20. });
  21. it('scoped vars should support objects', () => {
  22. const target = _templateSrv.replace('${series.name} ${series.nested.field}', {
  23. series: { value: { name: 'Server1', nested: { field: 'nested' } } },
  24. });
  25. expect(target).toBe('Server1 nested');
  26. });
  27. it('scoped vars should support objects with propert names with dot', () => {
  28. const target = _templateSrv.replace('${series.name} ${series.nested["field.with.dot"]}', {
  29. series: { value: { name: 'Server1', nested: { 'field.with.dot': 'nested' } } },
  30. });
  31. expect(target).toBe('Server1 nested');
  32. });
  33. it('scoped vars should support arrays of objects', () => {
  34. const target = _templateSrv.replace('${series.rows[0].name} ${series.rows[1].name}', {
  35. series: { value: { rows: [{ name: 'first' }, { name: 'second' }] } },
  36. });
  37. expect(target).toBe('first second');
  38. });
  39. it('should replace $test with scoped value', () => {
  40. const target = _templateSrv.replace('this.$test.filters', {
  41. test: { value: 'mupp', text: 'asd' },
  42. });
  43. expect(target).toBe('this.mupp.filters');
  44. });
  45. it('should replace ${test} with scoped value', () => {
  46. const target = _templateSrv.replace('this.${test}.filters', {
  47. test: { value: 'mupp', text: 'asd' },
  48. });
  49. expect(target).toBe('this.mupp.filters');
  50. });
  51. it('should replace ${test:glob} with scoped value', () => {
  52. const target = _templateSrv.replace('this.${test:glob}.filters', {
  53. test: { value: 'mupp', text: 'asd' },
  54. });
  55. expect(target).toBe('this.mupp.filters');
  56. });
  57. it('should replace $test with scoped text', () => {
  58. const target = _templateSrv.replaceWithText('this.$test.filters', {
  59. test: { value: 'mupp', text: 'asd' },
  60. });
  61. expect(target).toBe('this.asd.filters');
  62. });
  63. it('should replace ${test} with scoped text', () => {
  64. const target = _templateSrv.replaceWithText('this.${test}.filters', {
  65. test: { value: 'mupp', text: 'asd' },
  66. });
  67. expect(target).toBe('this.asd.filters');
  68. });
  69. it('should replace ${test:glob} with scoped text', () => {
  70. const target = _templateSrv.replaceWithText('this.${test:glob}.filters', {
  71. test: { value: 'mupp', text: 'asd' },
  72. });
  73. expect(target).toBe('this.asd.filters');
  74. });
  75. });
  76. describe('getAdhocFilters', () => {
  77. beforeEach(() => {
  78. initTemplateSrv([
  79. {
  80. type: 'datasource',
  81. name: 'ds',
  82. current: { value: 'logstash', text: 'logstash' },
  83. },
  84. { type: 'adhoc', name: 'test', datasource: 'oogle', filters: [1] },
  85. { type: 'adhoc', name: 'test2', datasource: '$ds', filters: [2] },
  86. ]);
  87. });
  88. it('should return filters if datasourceName match', () => {
  89. const filters = _templateSrv.getAdhocFilters('oogle');
  90. expect(filters).toMatchObject([1]);
  91. });
  92. it('should return empty array if datasourceName does not match', () => {
  93. const filters = _templateSrv.getAdhocFilters('oogleasdasd');
  94. expect(filters).toMatchObject([]);
  95. });
  96. it('should return filters when datasourceName match via data source variable', () => {
  97. const filters = _templateSrv.getAdhocFilters('logstash');
  98. expect(filters).toMatchObject([2]);
  99. });
  100. });
  101. describe('replace can pass multi / all format', () => {
  102. beforeEach(() => {
  103. initTemplateSrv([
  104. {
  105. type: 'query',
  106. name: 'test',
  107. current: { value: ['value1', 'value2'] },
  108. },
  109. ]);
  110. });
  111. it('should replace $test with globbed value', () => {
  112. const target = _templateSrv.replace('this.$test.filters', {}, 'glob');
  113. expect(target).toBe('this.{value1,value2}.filters');
  114. });
  115. describe('when the globbed variable only has one value', () => {
  116. beforeEach(() => {
  117. initTemplateSrv([
  118. {
  119. type: 'query',
  120. name: 'test',
  121. current: { value: ['value1'] },
  122. },
  123. ]);
  124. });
  125. it('should not glob the value', () => {
  126. const target = _templateSrv.replace('this.$test.filters', {}, 'glob');
  127. expect(target).toBe('this.value1.filters');
  128. });
  129. });
  130. it('should replace ${test} with globbed value', () => {
  131. const target = _templateSrv.replace('this.${test}.filters', {}, 'glob');
  132. expect(target).toBe('this.{value1,value2}.filters');
  133. });
  134. it('should replace ${test:glob} with globbed value', () => {
  135. const target = _templateSrv.replace('this.${test:glob}.filters', {});
  136. expect(target).toBe('this.{value1,value2}.filters');
  137. });
  138. it('should replace $test with piped value', () => {
  139. const target = _templateSrv.replace('this=$test', {}, 'pipe');
  140. expect(target).toBe('this=value1|value2');
  141. });
  142. it('should replace ${test} with piped value', () => {
  143. const target = _templateSrv.replace('this=${test}', {}, 'pipe');
  144. expect(target).toBe('this=value1|value2');
  145. });
  146. it('should replace ${test:pipe} with piped value', () => {
  147. const target = _templateSrv.replace('this=${test:pipe}', {});
  148. expect(target).toBe('this=value1|value2');
  149. });
  150. it('should replace ${test:pipe} with piped value and $test with globbed value', () => {
  151. const target = _templateSrv.replace('${test:pipe},$test', {}, 'glob');
  152. expect(target).toBe('value1|value2,{value1,value2}');
  153. });
  154. });
  155. describe('variable with all option', () => {
  156. beforeEach(() => {
  157. initTemplateSrv([
  158. {
  159. type: 'query',
  160. name: 'test',
  161. current: { value: '$__all' },
  162. options: [{ value: '$__all' }, { value: 'value1' }, { value: 'value2' }],
  163. },
  164. ]);
  165. });
  166. it('should replace $test with formatted all value', () => {
  167. const target = _templateSrv.replace('this.$test.filters', {}, 'glob');
  168. expect(target).toBe('this.{value1,value2}.filters');
  169. });
  170. it('should replace ${test} with formatted all value', () => {
  171. const target = _templateSrv.replace('this.${test}.filters', {}, 'glob');
  172. expect(target).toBe('this.{value1,value2}.filters');
  173. });
  174. it('should replace ${test:glob} with formatted all value', () => {
  175. const target = _templateSrv.replace('this.${test:glob}.filters', {});
  176. expect(target).toBe('this.{value1,value2}.filters');
  177. });
  178. it('should replace ${test:pipe} with piped value and $test with globbed value', () => {
  179. const target = _templateSrv.replace('${test:pipe},$test', {}, 'glob');
  180. expect(target).toBe('value1|value2,{value1,value2}');
  181. });
  182. });
  183. describe('variable with all option and custom value', () => {
  184. beforeEach(() => {
  185. initTemplateSrv([
  186. {
  187. type: 'query',
  188. name: 'test',
  189. current: { value: '$__all' },
  190. allValue: '*',
  191. options: [{ value: 'value1' }, { value: 'value2' }],
  192. },
  193. ]);
  194. });
  195. it('should replace $test with formatted all value', () => {
  196. const target = _templateSrv.replace('this.$test.filters', {}, 'glob');
  197. expect(target).toBe('this.*.filters');
  198. });
  199. it('should replace ${test} with formatted all value', () => {
  200. const target = _templateSrv.replace('this.${test}.filters', {}, 'glob');
  201. expect(target).toBe('this.*.filters');
  202. });
  203. it('should replace ${test:glob} with formatted all value', () => {
  204. const target = _templateSrv.replace('this.${test:glob}.filters', {});
  205. expect(target).toBe('this.*.filters');
  206. });
  207. it('should not escape custom all value', () => {
  208. const target = _templateSrv.replace('this.$test', {}, 'regex');
  209. expect(target).toBe('this.*');
  210. });
  211. });
  212. describe('lucene format', () => {
  213. it('should properly escape $test with lucene escape sequences', () => {
  214. initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'value/4' } }]);
  215. const target = _templateSrv.replace('this:$test', {}, 'lucene');
  216. expect(target).toBe('this:value\\/4');
  217. });
  218. it('should properly escape ${test} with lucene escape sequences', () => {
  219. initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'value/4' } }]);
  220. const target = _templateSrv.replace('this:${test}', {}, 'lucene');
  221. expect(target).toBe('this:value\\/4');
  222. });
  223. it('should properly escape ${test:lucene} with lucene escape sequences', () => {
  224. initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'value/4' } }]);
  225. const target = _templateSrv.replace('this:${test:lucene}', {});
  226. expect(target).toBe('this:value\\/4');
  227. });
  228. });
  229. describe('format variable to string values', () => {
  230. it('single value should return value', () => {
  231. const result = _templateSrv.formatValue('test');
  232. expect(result).toBe('test');
  233. });
  234. it('multi value and glob format should render glob string', () => {
  235. const result = _templateSrv.formatValue(['test', 'test2'], 'glob');
  236. expect(result).toBe('{test,test2}');
  237. });
  238. it('multi value and lucene should render as lucene expr', () => {
  239. const result = _templateSrv.formatValue(['test', 'test2'], 'lucene');
  240. expect(result).toBe('("test" OR "test2")');
  241. });
  242. it('multi value and regex format should render regex string', () => {
  243. const result = _templateSrv.formatValue(['test.', 'test2'], 'regex');
  244. expect(result).toBe('(test\\.|test2)');
  245. });
  246. it('multi value and pipe should render pipe string', () => {
  247. const result = _templateSrv.formatValue(['test', 'test2'], 'pipe');
  248. expect(result).toBe('test|test2');
  249. });
  250. it('multi value and distributed should render distributed string', () => {
  251. const result = _templateSrv.formatValue(['test', 'test2'], 'distributed', {
  252. name: 'build',
  253. });
  254. expect(result).toBe('test,build=test2');
  255. });
  256. it('multi value and distributed should render when not string', () => {
  257. const result = _templateSrv.formatValue(['test'], 'distributed', {
  258. name: 'build',
  259. });
  260. expect(result).toBe('test');
  261. });
  262. it('multi value and csv format should render csv string', () => {
  263. const result = _templateSrv.formatValue(['test', 'test2'], 'csv');
  264. expect(result).toBe('test,test2');
  265. });
  266. it('multi value and percentencode format should render percent-encoded string', () => {
  267. const result = _templateSrv.formatValue(['foo()bar BAZ', 'test2'], 'percentencode');
  268. expect(result).toBe('%7Bfoo%28%29bar%20BAZ%2Ctest2%7D');
  269. });
  270. it('slash should be properly escaped in regex format', () => {
  271. const result = _templateSrv.formatValue('Gi3/14', 'regex');
  272. expect(result).toBe('Gi3\\/14');
  273. });
  274. });
  275. describe('can check if variable exists', () => {
  276. beforeEach(() => {
  277. initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'oogle' } }]);
  278. });
  279. it('should return true if $test exists', () => {
  280. const result = _templateSrv.variableExists('$test');
  281. expect(result).toBe(true);
  282. });
  283. it('should return true if $test exists in string', () => {
  284. const result = _templateSrv.variableExists('something $test something');
  285. expect(result).toBe(true);
  286. });
  287. it('should return true if [[test]] exists in string', () => {
  288. const result = _templateSrv.variableExists('something [[test]] something');
  289. expect(result).toBe(true);
  290. });
  291. it('should return true if [[test:csv]] exists in string', () => {
  292. const result = _templateSrv.variableExists('something [[test:csv]] something');
  293. expect(result).toBe(true);
  294. });
  295. it('should return true if ${test} exists in string', () => {
  296. const result = _templateSrv.variableExists('something ${test} something');
  297. expect(result).toBe(true);
  298. });
  299. it('should return true if ${test:raw} exists in string', () => {
  300. const result = _templateSrv.variableExists('something ${test:raw} something');
  301. expect(result).toBe(true);
  302. });
  303. it('should return null if there are no variables in string', () => {
  304. const result = _templateSrv.variableExists('string without variables');
  305. expect(result).toBe(null);
  306. });
  307. });
  308. describe('can highlight variables in string', () => {
  309. beforeEach(() => {
  310. initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'oogle' } }]);
  311. });
  312. it('should insert html', () => {
  313. const result = _templateSrv.highlightVariablesAsHtml('$test');
  314. expect(result).toBe('<span class="template-variable">$test</span>');
  315. });
  316. it('should insert html anywhere in string', () => {
  317. const result = _templateSrv.highlightVariablesAsHtml('this $test ok');
  318. expect(result).toBe('this <span class="template-variable">$test</span> ok');
  319. });
  320. it('should ignore if variables does not exist', () => {
  321. const result = _templateSrv.highlightVariablesAsHtml('this $google ok');
  322. expect(result).toBe('this $google ok');
  323. });
  324. });
  325. describe('updateIndex with simple value', () => {
  326. beforeEach(() => {
  327. initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'muuuu' } }]);
  328. });
  329. it('should set current value and update template data', () => {
  330. const target = _templateSrv.replace('this.[[test]].filters');
  331. expect(target).toBe('this.muuuu.filters');
  332. });
  333. });
  334. describe('fillVariableValuesForUrl with multi value', () => {
  335. beforeEach(() => {
  336. initTemplateSrv([
  337. {
  338. type: 'query',
  339. name: 'test',
  340. current: { value: ['val1', 'val2'] },
  341. getValueForUrl: function() {
  342. return this.current.value;
  343. },
  344. },
  345. ]);
  346. });
  347. it('should set multiple url params', () => {
  348. const params: any = {};
  349. _templateSrv.fillVariableValuesForUrl(params);
  350. expect(params['var-test']).toMatchObject(['val1', 'val2']);
  351. });
  352. });
  353. describe('fillVariableValuesForUrl skip url sync', () => {
  354. beforeEach(() => {
  355. initTemplateSrv([
  356. {
  357. name: 'test',
  358. skipUrlSync: true,
  359. current: { value: 'value' },
  360. getValueForUrl: function() {
  361. return this.current.value;
  362. },
  363. },
  364. ]);
  365. });
  366. it('should not include template variable value in url', () => {
  367. const params: any = {};
  368. _templateSrv.fillVariableValuesForUrl(params);
  369. expect(params['var-test']).toBe(undefined);
  370. });
  371. });
  372. describe('fillVariableValuesForUrl with multi value with skip url sync', () => {
  373. beforeEach(() => {
  374. initTemplateSrv([
  375. {
  376. type: 'query',
  377. name: 'test',
  378. skipUrlSync: true,
  379. current: { value: ['val1', 'val2'] },
  380. getValueForUrl: function() {
  381. return this.current.value;
  382. },
  383. },
  384. ]);
  385. });
  386. it('should not include template variable value in url', () => {
  387. const params: any = {};
  388. _templateSrv.fillVariableValuesForUrl(params);
  389. expect(params['var-test']).toBe(undefined);
  390. });
  391. });
  392. describe('fillVariableValuesForUrl with multi value and scopedVars', () => {
  393. beforeEach(() => {
  394. initTemplateSrv([{ type: 'query', name: 'test', current: { value: ['val1', 'val2'] } }]);
  395. });
  396. it('should set scoped value as url params', () => {
  397. const params: any = {};
  398. _templateSrv.fillVariableValuesForUrl(params, {
  399. test: { value: 'val1' },
  400. });
  401. expect(params['var-test']).toBe('val1');
  402. });
  403. });
  404. describe('fillVariableValuesForUrl with multi value, scopedVars and skip url sync', () => {
  405. beforeEach(() => {
  406. initTemplateSrv([{ type: 'query', name: 'test', current: { value: ['val1', 'val2'] } }]);
  407. });
  408. it('should not set scoped value as url params', () => {
  409. const params: any = {};
  410. _templateSrv.fillVariableValuesForUrl(params, {
  411. test: { name: 'test', value: 'val1', skipUrlSync: true },
  412. });
  413. expect(params['var-test']).toBe(undefined);
  414. });
  415. });
  416. describe('replaceWithText', () => {
  417. beforeEach(() => {
  418. initTemplateSrv([
  419. {
  420. type: 'query',
  421. name: 'server',
  422. current: { value: '{asd,asd2}', text: 'All' },
  423. },
  424. {
  425. type: 'interval',
  426. name: 'period',
  427. current: { value: '$__auto_interval_interval', text: 'auto' },
  428. },
  429. {
  430. type: 'textbox',
  431. name: 'empty_on_init',
  432. current: { value: '', text: '' },
  433. },
  434. {
  435. type: 'custom',
  436. name: 'foo',
  437. current: { value: 'constructor', text: 'constructor' },
  438. },
  439. ]);
  440. _templateSrv.setGrafanaVariable('$__auto_interval_interval', '13m');
  441. _templateSrv.updateIndex();
  442. });
  443. it('should replace with text except for grafanaVariables', () => {
  444. const target = _templateSrv.replaceWithText('Server: $server, period: $period');
  445. expect(target).toBe('Server: All, period: 13m');
  446. });
  447. it('should replace empty string-values with an empty string', () => {
  448. const target = _templateSrv.replaceWithText('Hello $empty_on_init');
  449. expect(target).toBe('Hello ');
  450. });
  451. it('should not return a string representation of a constructor property', () => {
  452. const target = _templateSrv.replaceWithText('$foo');
  453. expect(target).not.toBe('function Object() { [native code] }');
  454. expect(target).toBe('constructor');
  455. });
  456. });
  457. describe('built in interval variables', () => {
  458. beforeEach(() => {
  459. initTemplateSrv([]);
  460. });
  461. it('should be possible to fetch value with getBuilInIntervalValue', () => {
  462. const val = _templateSrv.getBuiltInIntervalValue();
  463. expect(val).toBe('1s');
  464. });
  465. it('should replace $__interval_ms with interval milliseconds', () => {
  466. const target = _templateSrv.replace('10 * $__interval_ms', {
  467. __interval_ms: { text: '100', value: '100' },
  468. });
  469. expect(target).toBe('10 * 100');
  470. });
  471. });
  472. });