variable_srv.jest.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. import '../all';
  2. import { VariableSrv } from '../variable_srv';
  3. import moment from 'moment';
  4. import $q from 'q';
  5. // import { model } from 'mobx-state-tree/dist/internal';
  6. // import { Emitter } from 'app/core/core';
  7. describe('VariableSrv', function() {
  8. var ctx = <any>{
  9. datasourceSrv: {},
  10. timeSrv: {
  11. timeRange: () => {},
  12. },
  13. $rootScope: {
  14. $on: () => {},
  15. },
  16. $injector: {
  17. instantiate: (ctr, obj) => new ctr(obj.model),
  18. },
  19. templateSrv: {
  20. setGrafanaVariable: jest.fn(),
  21. init: () => {},
  22. updateTemplateData: () => {},
  23. },
  24. $location: {
  25. search: () => {},
  26. },
  27. };
  28. // beforeEach(ctx.providePhase(['datasourceSrv', 'timeSrv', 'templateSrv', '$location']));
  29. // beforeEach(
  30. // angularMocks.inject(($rootScope, $q, $location, $injector) => {
  31. // ctx.$q = $q;
  32. // ctx.$rootScope = $rootScope;
  33. // ctx.$location = $location;
  34. // ctx.variableSrv = $injector.get('variableSrv');
  35. // ctx.variableSrv.init({
  36. // templating: { list: [] },
  37. // events: new Emitter(),
  38. // updateSubmenuVisibility: sinon.stub(),
  39. // });
  40. // ctx.$rootScope.$digest();
  41. // })
  42. // );
  43. function describeUpdateVariable(desc, fn) {
  44. describe(desc, function() {
  45. var scenario: any = {};
  46. scenario.setup = function(setupFn) {
  47. scenario.setupFn = setupFn;
  48. };
  49. beforeEach(function() {
  50. scenario.setupFn();
  51. var ds: any = {};
  52. ds.metricFindQuery = Promise.resolve(scenario.queryResult);
  53. ctx.variableSrv = new VariableSrv(ctx.$rootScope, $q, ctx.$location, ctx.$injector, ctx.templateSrv);
  54. ctx.variableSrv.timeSrv = ctx.timeSrv;
  55. console.log(ctx.variableSrv.timeSrv);
  56. ctx.variableSrv.datasourceSrv = {
  57. get: Promise.resolve(ds),
  58. getMetricSources: () => scenario.metricSources,
  59. };
  60. ctx.variableSrv.init({
  61. templating: { list: [] },
  62. updateSubmenuVisibility: () => {},
  63. });
  64. scenario.variable = ctx.variableSrv.createVariableFromModel(scenario.variableModel);
  65. ctx.variableSrv.addVariable(scenario.variable);
  66. ctx.variableSrv.updateOptions(scenario.variable);
  67. // ctx.$rootScope.$digest();
  68. });
  69. fn(scenario);
  70. });
  71. }
  72. describeUpdateVariable('interval variable without auto', scenario => {
  73. scenario.setup(() => {
  74. scenario.variableModel = {
  75. type: 'interval',
  76. query: '1s,2h,5h,1d',
  77. name: 'test',
  78. };
  79. });
  80. it('should update options array', () => {
  81. expect(scenario.variable.options.length).toBe(4);
  82. expect(scenario.variable.options[0].text).toBe('1s');
  83. expect(scenario.variable.options[0].value).toBe('1s');
  84. });
  85. });
  86. //
  87. // Interval variable update
  88. //
  89. describeUpdateVariable('interval variable with auto', scenario => {
  90. scenario.setup(() => {
  91. scenario.variableModel = {
  92. type: 'interval',
  93. query: '1s,2h,5h,1d',
  94. name: 'test',
  95. auto: true,
  96. auto_count: 10,
  97. };
  98. var range = {
  99. from: moment(new Date())
  100. .subtract(7, 'days')
  101. .toDate(),
  102. to: new Date(),
  103. };
  104. ctx.timeSrv.timeRange = () => range;
  105. // ctx.templateSrv.setGrafanaVariable = jest.fn();
  106. });
  107. it('should update options array', function() {
  108. expect(scenario.variable.options.length).toBe(5);
  109. expect(scenario.variable.options[0].text).toBe('auto');
  110. expect(scenario.variable.options[0].value).toBe('$__auto_interval_test');
  111. });
  112. it('should set $__auto_interval_test', function() {
  113. var call = ctx.templateSrv.setGrafanaVariable.firstCall;
  114. expect(call.args[0]).toBe('$__auto_interval_test');
  115. expect(call.args[1]).toBe('12h');
  116. });
  117. // updateAutoValue() gets called twice: once directly once via VariableSrv.validateVariableSelectionState()
  118. // So use lastCall instead of a specific call number
  119. it('should set $__auto_interval', function() {
  120. var call = ctx.templateSrv.setGrafanaVariable.lastCall;
  121. expect(call.args[0]).toBe('$__auto_interval');
  122. expect(call.args[1]).toBe('12h');
  123. });
  124. });
  125. //
  126. // Query variable update
  127. //
  128. describeUpdateVariable('query variable with empty current object and refresh', function(scenario) {
  129. scenario.setup(function() {
  130. scenario.variableModel = {
  131. type: 'query',
  132. query: '',
  133. name: 'test',
  134. current: {},
  135. };
  136. scenario.queryResult = [{ text: 'backend1' }, { text: 'backend2' }];
  137. });
  138. it('should set current value to first option', function() {
  139. expect(scenario.variable.options.length).toBe(2);
  140. expect(scenario.variable.current.value).toBe('backend1');
  141. });
  142. });
  143. describeUpdateVariable(
  144. 'query variable with multi select and new options does not contain some selected values',
  145. function(scenario) {
  146. scenario.setup(function() {
  147. scenario.variableModel = {
  148. type: 'query',
  149. query: '',
  150. name: 'test',
  151. current: {
  152. value: ['val1', 'val2', 'val3'],
  153. text: 'val1 + val2 + val3',
  154. },
  155. };
  156. scenario.queryResult = [{ text: 'val2' }, { text: 'val3' }];
  157. });
  158. it('should update current value', function() {
  159. expect(scenario.variable.current.value).toEqual(['val2', 'val3']);
  160. expect(scenario.variable.current.text).toEqual('val2 + val3');
  161. });
  162. }
  163. );
  164. describeUpdateVariable(
  165. 'query variable with multi select and new options does not contain any selected values',
  166. function(scenario) {
  167. scenario.setup(function() {
  168. scenario.variableModel = {
  169. type: 'query',
  170. query: '',
  171. name: 'test',
  172. current: {
  173. value: ['val1', 'val2', 'val3'],
  174. text: 'val1 + val2 + val3',
  175. },
  176. };
  177. scenario.queryResult = [{ text: 'val5' }, { text: 'val6' }];
  178. });
  179. it('should update current value with first one', function() {
  180. expect(scenario.variable.current.value).toEqual('val5');
  181. expect(scenario.variable.current.text).toEqual('val5');
  182. });
  183. }
  184. );
  185. describeUpdateVariable('query variable with multi select and $__all selected', function(scenario) {
  186. scenario.setup(function() {
  187. scenario.variableModel = {
  188. type: 'query',
  189. query: '',
  190. name: 'test',
  191. includeAll: true,
  192. current: {
  193. value: ['$__all'],
  194. text: 'All',
  195. },
  196. };
  197. scenario.queryResult = [{ text: 'val5' }, { text: 'val6' }];
  198. });
  199. it('should keep current All value', function() {
  200. expect(scenario.variable.current.value).toEqual(['$__all']);
  201. expect(scenario.variable.current.text).toEqual('All');
  202. });
  203. });
  204. describeUpdateVariable('query variable with numeric results', function(scenario) {
  205. scenario.setup(function() {
  206. scenario.variableModel = {
  207. type: 'query',
  208. query: '',
  209. name: 'test',
  210. current: {},
  211. };
  212. scenario.queryResult = [{ text: 12, value: 12 }];
  213. });
  214. it('should set current value to first option', function() {
  215. expect(scenario.variable.current.value).toBe('12');
  216. expect(scenario.variable.options[0].value).toBe('12');
  217. expect(scenario.variable.options[0].text).toBe('12');
  218. });
  219. });
  220. describeUpdateVariable('basic query variable', function(scenario) {
  221. scenario.setup(function() {
  222. scenario.variableModel = { type: 'query', query: 'apps.*', name: 'test' };
  223. scenario.queryResult = [{ text: 'backend1' }, { text: 'backend2' }];
  224. });
  225. it('should update options array', function() {
  226. expect(scenario.variable.options.length).toBe(2);
  227. expect(scenario.variable.options[0].text).toBe('backend1');
  228. expect(scenario.variable.options[0].value).toBe('backend1');
  229. expect(scenario.variable.options[1].value).toBe('backend2');
  230. });
  231. it('should select first option as value', function() {
  232. expect(scenario.variable.current.value).toBe('backend1');
  233. });
  234. });
  235. describeUpdateVariable('and existing value still exists in options', function(scenario) {
  236. scenario.setup(function() {
  237. scenario.variableModel = { type: 'query', query: 'apps.*', name: 'test' };
  238. scenario.variableModel.current = { value: 'backend2', text: 'backend2' };
  239. scenario.queryResult = [{ text: 'backend1' }, { text: 'backend2' }];
  240. });
  241. it('should keep variable value', function() {
  242. expect(scenario.variable.current.text).toBe('backend2');
  243. });
  244. });
  245. describeUpdateVariable('and regex pattern exists', function(scenario) {
  246. scenario.setup(function() {
  247. scenario.variableModel = { type: 'query', query: 'apps.*', name: 'test' };
  248. scenario.variableModel.regex = '/apps.*(backend_[0-9]+)/';
  249. scenario.queryResult = [
  250. { text: 'apps.backend.backend_01.counters.req' },
  251. { text: 'apps.backend.backend_02.counters.req' },
  252. ];
  253. });
  254. it('should extract and use match group', function() {
  255. expect(scenario.variable.options[0].value).toBe('backend_01');
  256. });
  257. });
  258. describeUpdateVariable('and regex pattern exists and no match', function(scenario) {
  259. scenario.setup(function() {
  260. scenario.variableModel = { type: 'query', query: 'apps.*', name: 'test' };
  261. scenario.variableModel.regex = '/apps.*(backendasd[0-9]+)/';
  262. scenario.queryResult = [
  263. { text: 'apps.backend.backend_01.counters.req' },
  264. { text: 'apps.backend.backend_02.counters.req' },
  265. ];
  266. });
  267. it('should not add non matching items, None option should be added instead', function() {
  268. expect(scenario.variable.options.length).toBe(1);
  269. expect(scenario.variable.options[0].isNone).toBe(true);
  270. });
  271. });
  272. describeUpdateVariable('regex pattern without slashes', function(scenario) {
  273. scenario.setup(function() {
  274. scenario.variableModel = { type: 'query', query: 'apps.*', name: 'test' };
  275. scenario.variableModel.regex = 'backend_01';
  276. scenario.queryResult = [
  277. { text: 'apps.backend.backend_01.counters.req' },
  278. { text: 'apps.backend.backend_02.counters.req' },
  279. ];
  280. });
  281. it('should return matches options', function() {
  282. expect(scenario.variable.options.length).toBe(1);
  283. });
  284. });
  285. describeUpdateVariable('regex pattern remove duplicates', function(scenario) {
  286. scenario.setup(function() {
  287. scenario.variableModel = { type: 'query', query: 'apps.*', name: 'test' };
  288. scenario.variableModel.regex = '/backend_01/';
  289. scenario.queryResult = [
  290. { text: 'apps.backend.backend_01.counters.req' },
  291. { text: 'apps.backend.backend_01.counters.req' },
  292. ];
  293. });
  294. it('should return matches options', function() {
  295. expect(scenario.variable.options.length).toBe(1);
  296. });
  297. });
  298. describeUpdateVariable('with include All', function(scenario) {
  299. scenario.setup(function() {
  300. scenario.variableModel = {
  301. type: 'query',
  302. query: 'apps.*',
  303. name: 'test',
  304. includeAll: true,
  305. };
  306. scenario.queryResult = [{ text: 'backend1' }, { text: 'backend2' }, { text: 'backend3' }];
  307. });
  308. it('should add All option', function() {
  309. expect(scenario.variable.options[0].text).toBe('All');
  310. expect(scenario.variable.options[0].value).toBe('$__all');
  311. });
  312. });
  313. describeUpdateVariable('with include all and custom value', function(scenario) {
  314. scenario.setup(function() {
  315. scenario.variableModel = {
  316. type: 'query',
  317. query: 'apps.*',
  318. name: 'test',
  319. includeAll: true,
  320. allValue: '*',
  321. };
  322. scenario.queryResult = [{ text: 'backend1' }, { text: 'backend2' }, { text: 'backend3' }];
  323. });
  324. it('should add All option with custom value', function() {
  325. expect(scenario.variable.options[0].value).toBe('$__all');
  326. });
  327. });
  328. describeUpdateVariable('without sort', function(scenario) {
  329. scenario.setup(function() {
  330. scenario.variableModel = {
  331. type: 'query',
  332. query: 'apps.*',
  333. name: 'test',
  334. sort: 0,
  335. };
  336. scenario.queryResult = [{ text: 'bbb2' }, { text: 'aaa10' }, { text: 'ccc3' }];
  337. });
  338. it('should return options without sort', function() {
  339. expect(scenario.variable.options[0].text).toBe('bbb2');
  340. expect(scenario.variable.options[1].text).toBe('aaa10');
  341. expect(scenario.variable.options[2].text).toBe('ccc3');
  342. });
  343. });
  344. describeUpdateVariable('with alphabetical sort (asc)', function(scenario) {
  345. scenario.setup(function() {
  346. scenario.variableModel = {
  347. type: 'query',
  348. query: 'apps.*',
  349. name: 'test',
  350. sort: 1,
  351. };
  352. scenario.queryResult = [{ text: 'bbb2' }, { text: 'aaa10' }, { text: 'ccc3' }];
  353. });
  354. it('should return options with alphabetical sort', function() {
  355. expect(scenario.variable.options[0].text).toBe('aaa10');
  356. expect(scenario.variable.options[1].text).toBe('bbb2');
  357. expect(scenario.variable.options[2].text).toBe('ccc3');
  358. });
  359. });
  360. describeUpdateVariable('with alphabetical sort (desc)', function(scenario) {
  361. scenario.setup(function() {
  362. scenario.variableModel = {
  363. type: 'query',
  364. query: 'apps.*',
  365. name: 'test',
  366. sort: 2,
  367. };
  368. scenario.queryResult = [{ text: 'bbb2' }, { text: 'aaa10' }, { text: 'ccc3' }];
  369. });
  370. it('should return options with alphabetical sort', function() {
  371. expect(scenario.variable.options[0].text).toBe('ccc3');
  372. expect(scenario.variable.options[1].text).toBe('bbb2');
  373. expect(scenario.variable.options[2].text).toBe('aaa10');
  374. });
  375. });
  376. describeUpdateVariable('with numerical sort (asc)', function(scenario) {
  377. scenario.setup(function() {
  378. scenario.variableModel = {
  379. type: 'query',
  380. query: 'apps.*',
  381. name: 'test',
  382. sort: 3,
  383. };
  384. scenario.queryResult = [{ text: 'bbb2' }, { text: 'aaa10' }, { text: 'ccc3' }];
  385. });
  386. it('should return options with numerical sort', function() {
  387. expect(scenario.variable.options[0].text).toBe('bbb2');
  388. expect(scenario.variable.options[1].text).toBe('ccc3');
  389. expect(scenario.variable.options[2].text).toBe('aaa10');
  390. });
  391. });
  392. describeUpdateVariable('with numerical sort (desc)', function(scenario) {
  393. scenario.setup(function() {
  394. scenario.variableModel = {
  395. type: 'query',
  396. query: 'apps.*',
  397. name: 'test',
  398. sort: 4,
  399. };
  400. scenario.queryResult = [{ text: 'bbb2' }, { text: 'aaa10' }, { text: 'ccc3' }];
  401. });
  402. it('should return options with numerical sort', function() {
  403. expect(scenario.variable.options[0].text).toBe('aaa10');
  404. expect(scenario.variable.options[1].text).toBe('ccc3');
  405. expect(scenario.variable.options[2].text).toBe('bbb2');
  406. });
  407. });
  408. //
  409. // datasource variable update
  410. //
  411. describeUpdateVariable('datasource variable with regex filter', function(scenario) {
  412. scenario.setup(function() {
  413. scenario.variableModel = {
  414. type: 'datasource',
  415. query: 'graphite',
  416. name: 'test',
  417. current: { value: 'backend4_pee', text: 'backend4_pee' },
  418. regex: '/pee$/',
  419. };
  420. scenario.metricSources = [
  421. { name: 'backend1', meta: { id: 'influx' } },
  422. { name: 'backend2_pee', meta: { id: 'graphite' } },
  423. { name: 'backend3', meta: { id: 'graphite' } },
  424. { name: 'backend4_pee', meta: { id: 'graphite' } },
  425. ];
  426. });
  427. it('should set only contain graphite ds and filtered using regex', function() {
  428. expect(scenario.variable.options.length).toBe(2);
  429. expect(scenario.variable.options[0].value).toBe('backend2_pee');
  430. expect(scenario.variable.options[1].value).toBe('backend4_pee');
  431. });
  432. it('should keep current value if available', function() {
  433. expect(scenario.variable.current.value).toBe('backend4_pee');
  434. });
  435. });
  436. //
  437. // Custom variable update
  438. //
  439. describeUpdateVariable('update custom variable', function(scenario) {
  440. scenario.setup(function() {
  441. scenario.variableModel = {
  442. type: 'custom',
  443. query: 'hej, hop, asd',
  444. name: 'test',
  445. };
  446. });
  447. it('should update options array', function() {
  448. expect(scenario.variable.options.length).toBe(3);
  449. expect(scenario.variable.options[0].text).toBe('hej');
  450. expect(scenario.variable.options[1].value).toBe('hop');
  451. });
  452. });
  453. describe('multiple interval variables with auto', function() {
  454. var variable1, variable2;
  455. beforeEach(function() {
  456. var range = {
  457. from: moment(new Date())
  458. .subtract(7, 'days')
  459. .toDate(),
  460. to: new Date(),
  461. };
  462. ctx.timeSrv.timeRange = () => range;
  463. ctx.templateSrv.setGrafanaVariable = jest.fn();
  464. var variableModel1 = {
  465. type: 'interval',
  466. query: '1s,2h,5h,1d',
  467. name: 'variable1',
  468. auto: true,
  469. auto_count: 10,
  470. };
  471. variable1 = ctx.variableSrv.createVariableFromModel(variableModel1);
  472. ctx.variableSrv.addVariable(variable1);
  473. var variableModel2 = {
  474. type: 'interval',
  475. query: '1s,2h,5h',
  476. name: 'variable2',
  477. auto: true,
  478. auto_count: 1000,
  479. };
  480. variable2 = ctx.variableSrv.createVariableFromModel(variableModel2);
  481. ctx.variableSrv.addVariable(variable2);
  482. ctx.variableSrv.updateOptions(variable1);
  483. ctx.variableSrv.updateOptions(variable2);
  484. ctx.$rootScope.$digest();
  485. });
  486. it('should update options array', function() {
  487. expect(variable1.options.length).toBe(5);
  488. expect(variable1.options[0].text).toBe('auto');
  489. expect(variable1.options[0].value).toBe('$__auto_interval_variable1');
  490. expect(variable2.options.length).toBe(4);
  491. expect(variable2.options[0].text).toBe('auto');
  492. expect(variable2.options[0].value).toBe('$__auto_interval_variable2');
  493. });
  494. it('should correctly set $__auto_interval_variableX', function() {
  495. var variable1Set,
  496. variable2Set,
  497. legacySet,
  498. unknownSet = false;
  499. // updateAutoValue() gets called repeatedly: once directly once via VariableSrv.validateVariableSelectionState()
  500. // So check that all calls are valid rather than expect a specific number and/or ordering of calls
  501. for (var i = 0; i < ctx.templateSrv.setGrafanaVariable.mock.calls.length; i++) {
  502. var call = ctx.templateSrv.setGrafanaVariable.mock.calls[i];
  503. switch (call.args[0]) {
  504. case '$__auto_interval_variable1':
  505. expect(call[1]).toBe('12h');
  506. variable1Set = true;
  507. break;
  508. case '$__auto_interval_variable2':
  509. expect(call[1]).toBe('10m');
  510. variable2Set = true;
  511. break;
  512. case '$__auto_interval':
  513. expect(call[1]).toEqual(expect.stringMatching(/^(12h|10m)$/));
  514. legacySet = true;
  515. break;
  516. default:
  517. unknownSet = true;
  518. break;
  519. }
  520. }
  521. expect(variable1Set).toBe.equal(true);
  522. expect(variable2Set).toBe.equal(true);
  523. expect(legacySet).toBe.equal(true);
  524. expect(unknownSet).toBe.equal(false);
  525. });
  526. });
  527. });