PageRenderTime 26ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/core/test/unit/models_plugins/pagination_spec.js

https://gitlab.com/gpestana/Ghost
JavaScript | 371 lines | 292 code | 72 blank | 7 comment | 11 complexity | d2d6c3173f55aa75578c97873c50caf9 MD5 | raw file
  1. /*globals describe, it, before, beforeEach, afterEach */
  2. /*jshint expr:true*/
  3. var should = require('should'),
  4. sinon = require('sinon'),
  5. Promise = require('bluebird'),
  6. rewire = require('rewire'),
  7. // Thing we're testing
  8. pagination = rewire('../../../server/models/plugins/pagination');
  9. // To stop jshint complaining
  10. should.equal(true, true);
  11. describe('pagination', function () {
  12. var sandbox,
  13. paginationUtils;
  14. beforeEach(function () {
  15. sandbox = sinon.sandbox.create();
  16. });
  17. afterEach(function () {
  18. sandbox.restore();
  19. });
  20. describe('paginationUtils', function () {
  21. before(function () {
  22. paginationUtils = pagination.__get__('paginationUtils');
  23. });
  24. describe('formatResponse', function () {
  25. var formatResponse;
  26. before(function () {
  27. formatResponse = paginationUtils.formatResponse;
  28. });
  29. it('returns correct pagination object for single page', function () {
  30. formatResponse(5, {limit: 10, page: 1}).should.eql({
  31. limit: 10,
  32. next: null,
  33. page: 1,
  34. pages: 1,
  35. prev: null,
  36. total: 5
  37. });
  38. });
  39. it('returns correct pagination object for first page of many', function () {
  40. formatResponse(44, {limit: 5, page: 1}).should.eql({
  41. limit: 5,
  42. next: 2,
  43. page: 1,
  44. pages: 9,
  45. prev: null,
  46. total: 44
  47. });
  48. });
  49. it('returns correct pagination object for middle page of many', function () {
  50. formatResponse(44, {limit: 5, page: 9}).should.eql({
  51. limit: 5,
  52. next: null,
  53. page: 9,
  54. pages: 9,
  55. prev: 8,
  56. total: 44
  57. });
  58. });
  59. it('returns correct pagination object for last page of many', function () {
  60. formatResponse(44, {limit: 5, page: 3}).should.eql({
  61. limit: 5,
  62. next: 4,
  63. page: 3,
  64. pages: 9,
  65. prev: 2,
  66. total: 44
  67. });
  68. });
  69. it('returns correct pagination object when page not set', function () {
  70. formatResponse(5, {limit: 10}).should.eql({
  71. limit: 10,
  72. next: null,
  73. page: 1,
  74. pages: 1,
  75. prev: null,
  76. total: 5
  77. });
  78. });
  79. it('returns correct pagination object for limit all', function () {
  80. formatResponse(5, {limit: 'all'}).should.eql({
  81. limit: 'all',
  82. next: null,
  83. page: 1,
  84. pages: 1,
  85. prev: null,
  86. total: 5
  87. });
  88. });
  89. });
  90. describe('parseOptions', function () {
  91. var parseOptions;
  92. before(function () {
  93. parseOptions = paginationUtils.parseOptions;
  94. });
  95. it('should use defaults if no options are passed', function () {
  96. parseOptions().should.eql({
  97. limit: 15,
  98. page: 1
  99. });
  100. });
  101. it('should accept numbers for limit and page', function () {
  102. parseOptions({
  103. limit: 10,
  104. page: 2
  105. }).should.eql({
  106. limit: 10,
  107. page: 2
  108. });
  109. });
  110. it('should use defaults if bad options are passed', function () {
  111. parseOptions({
  112. limit: 'thelma',
  113. page: 'louise'
  114. }).should.eql({
  115. limit: 15,
  116. page: 1
  117. });
  118. });
  119. it('should permit all for limit', function () {
  120. parseOptions({
  121. limit: 'all'
  122. }).should.eql({
  123. limit: 'all',
  124. page: 1
  125. });
  126. });
  127. });
  128. describe('addLimitAndOffset', function () {
  129. var addLimitAndOffset,
  130. collection = {};
  131. before(function () {
  132. addLimitAndOffset = paginationUtils.addLimitAndOffset;
  133. });
  134. beforeEach(function () {
  135. collection.query = sandbox.stub().returns(collection);
  136. });
  137. it('should add query options if limit is set', function () {
  138. addLimitAndOffset(collection, {limit: 5, page: 1});
  139. collection.query.calledTwice.should.be.true;
  140. collection.query.firstCall.calledWith('limit', 5).should.be.true;
  141. collection.query.secondCall.calledWith('offset', 0).should.be.true;
  142. });
  143. it('should not add query options if limit is not set', function () {
  144. addLimitAndOffset(collection, {page: 1});
  145. collection.query.called.should.be.false;
  146. });
  147. });
  148. });
  149. describe('fetchPage', function () {
  150. var model, bookshelf, knex, mockQuery;
  151. before(function () {
  152. paginationUtils = pagination.__get__('paginationUtils');
  153. });
  154. beforeEach(function () {
  155. // Stub paginationUtils
  156. paginationUtils.parseOptions = sandbox.stub();
  157. paginationUtils.addLimitAndOffset = sandbox.stub();
  158. paginationUtils.formatResponse = sandbox.stub().returns({});
  159. // Mock out bookshelf model
  160. mockQuery = {
  161. clone: sandbox.stub(),
  162. select: sandbox.stub(),
  163. toQuery: sandbox.stub()
  164. };
  165. mockQuery.clone.returns(mockQuery);
  166. mockQuery.select.returns([{aggregate: 1}]);
  167. model = function () {};
  168. model.prototype.fetchAll = sandbox.stub().returns(Promise.resolve({}));
  169. model.prototype.query = sandbox.stub();
  170. model.prototype.query.returns(mockQuery);
  171. knex = {raw: sandbox.stub().returns(Promise.resolve())};
  172. bookshelf = {Model: model, knex: knex};
  173. pagination(bookshelf);
  174. });
  175. it('extends Model with fetchPage', function () {
  176. bookshelf.Model.prototype.should.have.ownProperty('fetchPage');
  177. bookshelf.Model.prototype.fetchPage.should.be.a.Function;
  178. });
  179. it('calls all paginationUtils and methods', function (done) {
  180. paginationUtils.parseOptions.returns({});
  181. bookshelf.Model.prototype.fetchPage().then(function () {
  182. sinon.assert.callOrder(
  183. paginationUtils.parseOptions,
  184. model.prototype.query,
  185. mockQuery.clone,
  186. mockQuery.select,
  187. paginationUtils.addLimitAndOffset,
  188. model.prototype.fetchAll,
  189. paginationUtils.formatResponse
  190. );
  191. paginationUtils.parseOptions.calledOnce.should.be.true;
  192. paginationUtils.parseOptions.calledWith(undefined).should.be.true;
  193. paginationUtils.addLimitAndOffset.calledOnce.should.be.true;
  194. paginationUtils.formatResponse.calledOnce.should.be.true;
  195. model.prototype.query.calledOnce.should.be.true;
  196. model.prototype.query.firstCall.calledWith().should.be.true;
  197. mockQuery.clone.calledOnce.should.be.true;
  198. mockQuery.clone.firstCall.calledWith().should.be.true;
  199. mockQuery.select.calledOnce.should.be.true;
  200. mockQuery.select.calledWith().should.be.true;
  201. model.prototype.fetchAll.calledOnce.should.be.true;
  202. model.prototype.fetchAll.calledWith({}).should.be.true;
  203. done();
  204. }).catch(done);
  205. });
  206. it('calls all paginationUtils and methods when order set', function (done) {
  207. var orderOptions = {order: {id: 'DESC'}};
  208. paginationUtils.parseOptions.returns(orderOptions);
  209. bookshelf.Model.prototype.fetchPage(orderOptions).then(function () {
  210. sinon.assert.callOrder(
  211. paginationUtils.parseOptions,
  212. model.prototype.query,
  213. mockQuery.clone,
  214. mockQuery.select,
  215. paginationUtils.addLimitAndOffset,
  216. model.prototype.query,
  217. model.prototype.fetchAll,
  218. paginationUtils.formatResponse
  219. );
  220. paginationUtils.parseOptions.calledOnce.should.be.true;
  221. paginationUtils.parseOptions.calledWith(orderOptions).should.be.true;
  222. paginationUtils.addLimitAndOffset.calledOnce.should.be.true;
  223. paginationUtils.formatResponse.calledOnce.should.be.true;
  224. model.prototype.query.calledTwice.should.be.true;
  225. model.prototype.query.firstCall.calledWith().should.be.true;
  226. model.prototype.query.secondCall.calledWith('orderBy', 'undefined.id', 'DESC').should.be.true;
  227. mockQuery.clone.calledOnce.should.be.true;
  228. mockQuery.clone.firstCall.calledWith().should.be.true;
  229. mockQuery.select.calledOnce.should.be.true;
  230. mockQuery.select.calledWith().should.be.true;
  231. model.prototype.fetchAll.calledOnce.should.be.true;
  232. model.prototype.fetchAll.calledWith(orderOptions).should.be.true;
  233. done();
  234. }).catch(done);
  235. });
  236. it('calls all paginationUtils and methods when group by set', function (done) {
  237. var groupOptions = {groups: ['posts.id']};
  238. paginationUtils.parseOptions.returns(groupOptions);
  239. bookshelf.Model.prototype.fetchPage(groupOptions).then(function () {
  240. sinon.assert.callOrder(
  241. paginationUtils.parseOptions,
  242. model.prototype.query,
  243. mockQuery.clone,
  244. mockQuery.select,
  245. paginationUtils.addLimitAndOffset,
  246. model.prototype.query,
  247. model.prototype.fetchAll,
  248. paginationUtils.formatResponse
  249. );
  250. paginationUtils.parseOptions.calledOnce.should.be.true;
  251. paginationUtils.parseOptions.calledWith(groupOptions).should.be.true;
  252. paginationUtils.addLimitAndOffset.calledOnce.should.be.true;
  253. paginationUtils.formatResponse.calledOnce.should.be.true;
  254. model.prototype.query.calledTwice.should.be.true;
  255. model.prototype.query.firstCall.calledWith().should.be.true;
  256. model.prototype.query.secondCall.calledWith('groupBy', 'posts.id').should.be.true;
  257. mockQuery.clone.calledOnce.should.be.true;
  258. mockQuery.clone.firstCall.calledWith().should.be.true;
  259. mockQuery.select.calledOnce.should.be.true;
  260. mockQuery.select.calledWith().should.be.true;
  261. model.prototype.fetchAll.calledOnce.should.be.true;
  262. model.prototype.fetchAll.calledWith(groupOptions).should.be.true;
  263. done();
  264. }).catch(done);
  265. });
  266. it('returns expected response', function (done) {
  267. paginationUtils.parseOptions.returns({});
  268. bookshelf.Model.prototype.fetchPage().then(function (result) {
  269. result.should.have.ownProperty('collection');
  270. result.should.have.ownProperty('pagination');
  271. result.collection.should.be.an.Object;
  272. result.pagination.should.be.an.Object;
  273. done();
  274. });
  275. });
  276. it('returns expected response even when aggregate is empty', function (done) {
  277. // override aggregate response
  278. mockQuery.select.returns([]);
  279. paginationUtils.parseOptions.returns({});
  280. bookshelf.Model.prototype.fetchPage().then(function (result) {
  281. result.should.have.ownProperty('collection');
  282. result.should.have.ownProperty('pagination');
  283. result.collection.should.be.an.Object;
  284. result.pagination.should.be.an.Object;
  285. done();
  286. });
  287. });
  288. it('will output sql statements in debug mode', function (done) {
  289. model.prototype.debug = true;
  290. mockQuery.select.returns({toQuery: function () {}});
  291. paginationUtils.parseOptions.returns({});
  292. var consoleSpy = sandbox.spy(console, 'log');
  293. bookshelf.Model.prototype.fetchPage().then(function () {
  294. consoleSpy.calledOnce.should.be.true;
  295. done();
  296. });
  297. });
  298. });
  299. });