history_ctrl_specs.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. import { describe, beforeEach, it, sinon, expect, angularMocks } from 'test/lib/common';
  2. import _ from 'lodash';
  3. import { HistoryListCtrl } from 'app/features/dashboard/history/history';
  4. import { versions, compare, restore } from './history_mocks';
  5. describe('HistoryListCtrl', function() {
  6. var RESTORE_ID = 4;
  7. var ctx: any = {};
  8. var versionsResponse: any = versions();
  9. restore(7, RESTORE_ID);
  10. beforeEach(angularMocks.module('grafana.core'));
  11. beforeEach(angularMocks.module('grafana.services'));
  12. beforeEach(
  13. angularMocks.inject($rootScope => {
  14. ctx.scope = $rootScope.$new();
  15. })
  16. );
  17. var historySrv;
  18. var $rootScope;
  19. beforeEach(function() {
  20. historySrv = {
  21. getHistoryList: sinon.stub(),
  22. calculateDiff: sinon.stub(),
  23. restoreDashboard: sinon.stub(),
  24. };
  25. $rootScope = {
  26. appEvent: sinon.spy(),
  27. onAppEvent: sinon.spy(),
  28. };
  29. });
  30. describe('when the history list component is loaded', function() {
  31. var deferred;
  32. beforeEach(
  33. angularMocks.inject(($controller, $q) => {
  34. deferred = $q.defer();
  35. historySrv.getHistoryList.returns(deferred.promise);
  36. ctx.ctrl = $controller(
  37. HistoryListCtrl,
  38. {
  39. historySrv,
  40. $rootScope,
  41. $scope: ctx.scope,
  42. },
  43. {
  44. dashboard: {
  45. id: 2,
  46. version: 3,
  47. formatDate: sinon.stub().returns('date'),
  48. },
  49. }
  50. );
  51. })
  52. );
  53. it('should immediately attempt to fetch the history list', function() {
  54. expect(historySrv.getHistoryList.calledOnce).to.be(true);
  55. });
  56. describe('and the history list is successfully fetched', function() {
  57. beforeEach(function() {
  58. deferred.resolve(versionsResponse);
  59. ctx.ctrl.$scope.$apply();
  60. });
  61. it("should reset the controller's state", function() {
  62. expect(ctx.ctrl.mode).to.be('list');
  63. expect(ctx.ctrl.delta).to.eql({ basic: '', json: '' });
  64. expect(ctx.ctrl.canCompare).to.be(false);
  65. expect(_.find(ctx.ctrl.revisions, rev => rev.checked)).to.be(undefined);
  66. });
  67. it('should indicate loading has finished', function() {
  68. expect(ctx.ctrl.loading).to.be(false);
  69. });
  70. it('should store the revisions sorted desc by version id', function() {
  71. expect(ctx.ctrl.revisions[0].version).to.be(4);
  72. expect(ctx.ctrl.revisions[1].version).to.be(3);
  73. expect(ctx.ctrl.revisions[2].version).to.be(2);
  74. expect(ctx.ctrl.revisions[3].version).to.be(1);
  75. });
  76. it('should add a checked property to each revision', function() {
  77. var actual = _.filter(ctx.ctrl.revisions, rev => rev.hasOwnProperty('checked'));
  78. expect(actual.length).to.be(4);
  79. });
  80. it('should set all checked properties to false on reset', function() {
  81. ctx.ctrl.revisions[0].checked = true;
  82. ctx.ctrl.revisions[2].checked = true;
  83. ctx.ctrl.reset();
  84. var actual = _.filter(ctx.ctrl.revisions, rev => !rev.checked);
  85. expect(actual.length).to.be(4);
  86. });
  87. });
  88. describe('and fetching the history list fails', function() {
  89. beforeEach(function() {
  90. deferred.reject(new Error('HistoryListError'));
  91. ctx.ctrl.$scope.$apply();
  92. });
  93. it("should reset the controller's state", function() {
  94. expect(ctx.ctrl.mode).to.be('list');
  95. expect(ctx.ctrl.delta).to.eql({ basic: '', json: '' });
  96. expect(_.find(ctx.ctrl.revisions, rev => rev.checked)).to.be(undefined);
  97. });
  98. it('should indicate loading has finished', function() {
  99. expect(ctx.ctrl.loading).to.be(false);
  100. });
  101. it('should have an empty revisions list', function() {
  102. expect(ctx.ctrl.revisions).to.eql([]);
  103. });
  104. });
  105. describe('should update the history list when the dashboard is saved', function() {
  106. beforeEach(function() {
  107. ctx.ctrl.dashboard = { version: 3 };
  108. ctx.ctrl.resetFromSource = sinon.spy();
  109. });
  110. it('should listen for the `dashboard-saved` appEvent', function() {
  111. expect($rootScope.onAppEvent.calledOnce).to.be(true);
  112. expect($rootScope.onAppEvent.getCall(0).args[0]).to.be('dashboard-saved');
  113. });
  114. it('should call `onDashboardSaved` when the appEvent is received', function() {
  115. expect($rootScope.onAppEvent.getCall(0).args[1]).to.not.be(ctx.ctrl.onDashboardSaved);
  116. expect($rootScope.onAppEvent.getCall(0).args[1].toString).to.be(ctx.ctrl.onDashboardSaved.toString);
  117. });
  118. });
  119. });
  120. describe('when the user wants to compare two revisions', function() {
  121. var deferred;
  122. beforeEach(
  123. angularMocks.inject(($controller, $q) => {
  124. deferred = $q.defer();
  125. historySrv.getHistoryList.returns($q.when(versionsResponse));
  126. historySrv.calculateDiff.returns(deferred.promise);
  127. ctx.ctrl = $controller(
  128. HistoryListCtrl,
  129. {
  130. historySrv,
  131. $rootScope,
  132. $scope: ctx.scope,
  133. },
  134. {
  135. dashboard: {
  136. id: 2,
  137. version: 3,
  138. formatDate: sinon.stub().returns('date'),
  139. },
  140. }
  141. );
  142. ctx.ctrl.$scope.onDashboardSaved = sinon.spy();
  143. ctx.ctrl.$scope.$apply();
  144. })
  145. );
  146. it('should have already fetched the history list', function() {
  147. expect(historySrv.getHistoryList.calledOnce).to.be(true);
  148. expect(ctx.ctrl.revisions.length).to.be.above(0);
  149. });
  150. it('should check that two valid versions are selected', function() {
  151. // []
  152. expect(ctx.ctrl.canCompare).to.be(false);
  153. // single value
  154. ctx.ctrl.revisions = [{ checked: true }];
  155. ctx.ctrl.revisionSelectionChanged();
  156. expect(ctx.ctrl.canCompare).to.be(false);
  157. // both values in range
  158. ctx.ctrl.revisions = [{ checked: true }, { checked: true }];
  159. ctx.ctrl.revisionSelectionChanged();
  160. expect(ctx.ctrl.canCompare).to.be(true);
  161. });
  162. describe('and the basic diff is successfully fetched', function() {
  163. beforeEach(function() {
  164. deferred.resolve(compare('basic'));
  165. ctx.ctrl.revisions[1].checked = true;
  166. ctx.ctrl.revisions[3].checked = true;
  167. ctx.ctrl.getDiff('basic');
  168. ctx.ctrl.$scope.$apply();
  169. });
  170. it('should fetch the basic diff if two valid versions are selected', function() {
  171. expect(historySrv.calculateDiff.calledOnce).to.be(true);
  172. expect(ctx.ctrl.delta.basic).to.be('<div></div>');
  173. expect(ctx.ctrl.delta.json).to.be('');
  174. });
  175. it('should set the basic diff view as active', function() {
  176. expect(ctx.ctrl.mode).to.be('compare');
  177. expect(ctx.ctrl.diff).to.be('basic');
  178. });
  179. it('should indicate loading has finished', function() {
  180. expect(ctx.ctrl.loading).to.be(false);
  181. });
  182. });
  183. describe('and the json diff is successfully fetched', function() {
  184. beforeEach(function() {
  185. deferred.resolve(compare('json'));
  186. ctx.ctrl.revisions[1].checked = true;
  187. ctx.ctrl.revisions[3].checked = true;
  188. ctx.ctrl.getDiff('json');
  189. ctx.ctrl.$scope.$apply();
  190. });
  191. it('should fetch the json diff if two valid versions are selected', function() {
  192. expect(historySrv.calculateDiff.calledOnce).to.be(true);
  193. expect(ctx.ctrl.delta.basic).to.be('');
  194. expect(ctx.ctrl.delta.json).to.be('<pre><code></code></pre>');
  195. });
  196. it('should set the json diff view as active', function() {
  197. expect(ctx.ctrl.mode).to.be('compare');
  198. expect(ctx.ctrl.diff).to.be('json');
  199. });
  200. it('should indicate loading has finished', function() {
  201. expect(ctx.ctrl.loading).to.be(false);
  202. });
  203. });
  204. describe('and diffs have already been fetched', function() {
  205. beforeEach(function() {
  206. deferred.resolve(compare('basic'));
  207. ctx.ctrl.revisions[3].checked = true;
  208. ctx.ctrl.revisions[1].checked = true;
  209. ctx.ctrl.delta.basic = 'cached basic';
  210. ctx.ctrl.getDiff('basic');
  211. ctx.ctrl.$scope.$apply();
  212. });
  213. it('should use the cached diffs instead of fetching', function() {
  214. expect(historySrv.calculateDiff.calledOnce).to.be(false);
  215. expect(ctx.ctrl.delta.basic).to.be('cached basic');
  216. });
  217. it('should indicate loading has finished', function() {
  218. expect(ctx.ctrl.loading).to.be(false);
  219. });
  220. });
  221. describe('and fetching the diff fails', function() {
  222. beforeEach(function() {
  223. deferred.reject(new Error('DiffError'));
  224. ctx.ctrl.revisions[3].checked = true;
  225. ctx.ctrl.revisions[1].checked = true;
  226. ctx.ctrl.getDiff('basic');
  227. ctx.ctrl.$scope.$apply();
  228. });
  229. it('should fetch the diff if two valid versions are selected', function() {
  230. expect(historySrv.calculateDiff.calledOnce).to.be(true);
  231. });
  232. it('should return to the history list view', function() {
  233. expect(ctx.ctrl.mode).to.be('list');
  234. });
  235. it('should indicate loading has finished', function() {
  236. expect(ctx.ctrl.loading).to.be(false);
  237. });
  238. it('should have an empty delta/changeset', function() {
  239. expect(ctx.ctrl.delta).to.eql({ basic: '', json: '' });
  240. });
  241. });
  242. });
  243. describe('when the user wants to restore a revision', function() {
  244. var deferred;
  245. beforeEach(
  246. angularMocks.inject(($controller, $q) => {
  247. deferred = $q.defer();
  248. historySrv.getHistoryList.returns($q.when(versionsResponse));
  249. historySrv.restoreDashboard.returns(deferred.promise);
  250. ctx.ctrl = $controller(HistoryListCtrl, {
  251. historySrv,
  252. contextSrv: { user: { name: 'Carlos' } },
  253. $rootScope,
  254. $scope: ctx.scope,
  255. });
  256. ctx.ctrl.dashboard = { id: 1 };
  257. ctx.ctrl.restore();
  258. ctx.ctrl.$scope.$apply();
  259. })
  260. );
  261. it('should display a modal allowing the user to restore or cancel', function() {
  262. expect($rootScope.appEvent.calledOnce).to.be(true);
  263. expect($rootScope.appEvent.calledWith('confirm-modal')).to.be(true);
  264. });
  265. describe('and restore fails to fetch', function() {
  266. beforeEach(function() {
  267. deferred.reject(new Error('RestoreError'));
  268. ctx.ctrl.restoreConfirm(RESTORE_ID);
  269. try {
  270. // this throws error, due to promise rejection
  271. ctx.ctrl.$scope.$apply();
  272. } catch (e) {}
  273. });
  274. it('should indicate loading has finished', function() {
  275. expect(ctx.ctrl.loading).to.be(false);
  276. });
  277. });
  278. });
  279. });