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