route.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. import assert from 'assert';
  2. import moment from 'moment'; // jshint ignore:line
  3. import React from 'react'; // jshint ignore:line
  4. import Route from 'misago/components/threads/route'; // jshint ignore:line
  5. import misago from 'misago/index';
  6. import reducer from 'misago/reducers/threads';
  7. import title from 'misago/services/page-title';
  8. import snackbar from 'misago/services/snackbar';
  9. import store from 'misago/services/store';
  10. import * as testUtils from 'misago/utils/test-utils';
  11. let snackbarStore = null;
  12. /* jshint ignore:start */
  13. let route = {
  14. 'lists': [
  15. {
  16. type: 'all',
  17. path: '',
  18. name: "All",
  19. longName: "All threads"
  20. },
  21. {
  22. type: 'my',
  23. path: 'my/',
  24. name: "My",
  25. longName: "My threads"
  26. },
  27. {
  28. type: 'new',
  29. path: 'new/',
  30. name: "New",
  31. longName: "New threads"
  32. },
  33. {
  34. type: 'unread',
  35. path: 'unread/',
  36. name: "Unread",
  37. longName: "Unread threads"
  38. },
  39. {
  40. type: 'subscribed',
  41. path: 'subscribed/',
  42. name: "Subscribed",
  43. longName: "Subscribed threads"
  44. }
  45. ],
  46. list: {
  47. type: 'all',
  48. path: '',
  49. name: "All",
  50. longName: "All threads"
  51. },
  52. categoriesMap: {
  53. 1: {
  54. id: 1,
  55. parent: null,
  56. name: "Lorem",
  57. description: {
  58. plain: "Lorem ipsum dolor met sit amet eli.",
  59. html: "<p>Lorem ipsum dolor met sit amet eli.</p>"
  60. },
  61. css_class: null,
  62. absolute_url: '/categories/category-1/'
  63. },
  64. 2: {
  65. id: 2,
  66. parent: null,
  67. name: "Ipsum",
  68. description: {
  69. plain: "Lorem ipsum dolor met sit amet eli.",
  70. html: "<p>Lorem ipsum dolor met sit amet eli.</p>"
  71. },
  72. css_class: null,
  73. absolute_url: '/categories/category-2/'
  74. },
  75. 3: {
  76. id: 3,
  77. parent: null,
  78. name: "Dolor met",
  79. description: {
  80. plain: "Lorem ipsum dolor met sit amet eli.",
  81. html: "<p>Lorem ipsum dolor met sit amet eli.</p>"
  82. },
  83. css_class: null,
  84. absolute_url: '/categories/category-3/'
  85. }
  86. },
  87. category: {
  88. id: 1,
  89. parent: null,
  90. name: "Lorem",
  91. description: {
  92. plain: "Lorem ipsum dolor met sit amet eli.",
  93. html: "<p>Lorem ipsum dolor met sit amet eli.</p>"
  94. },
  95. css_class: null,
  96. absolute_url: '/categories/category-1/'
  97. }
  98. };
  99. /* jshint ignore:end */
  100. let thread = {
  101. id: 1,
  102. title: "Test thread",
  103. category: 3,
  104. top_category: 1,
  105. started_on: moment().format(),
  106. last_post: 3,
  107. last_poster_name: 'BobBoberson',
  108. last_poster_url: null,
  109. last_post_on: moment().format(),
  110. is_read: true,
  111. acl: {},
  112. };
  113. describe("Threads List Route", function() {
  114. beforeEach(function() {
  115. snackbarStore = testUtils.snackbarStoreMock();
  116. snackbar.init(snackbarStore);
  117. misago._context = {
  118. CATEGORIES_ON_INDEX: false,
  119. THREADS_API: '/test-api/threads/',
  120. SETTINGS: {
  121. forum_name: "Test Forum",
  122. forum_index_title: "Forum Index"
  123. }
  124. };
  125. store.constructor();
  126. store.addReducer('threads', reducer, []);
  127. store.addReducer('tick', function(state, action) {
  128. if (action || true) {
  129. return {'tick': 123};
  130. }
  131. }, {});
  132. store.init();
  133. title.init(
  134. misago._context.SETTINGS.forum_index_title,
  135. misago._context.SETTINGS.forum_name);
  136. });
  137. afterEach(function() {
  138. testUtils.unmountComponents();
  139. testUtils.snackbarClear(snackbar);
  140. $.mockjax.clear();
  141. });
  142. it("inits with preloaded data", function(done) {
  143. misago._context.THREADS = {
  144. results: [],
  145. count: 1,
  146. more: 0,
  147. page: 1,
  148. pages: 1,
  149. subcategories: [2, 3]
  150. };
  151. /* jshint ignore:start */
  152. let threads = [
  153. thread
  154. ];
  155. testUtils.render(<Route route={route} threads={threads} />);
  156. /* jshint ignore:end */
  157. testUtils.onElement('#test-mount .thread-title', function(element) {
  158. assert.equal($('.threads-list li').length, 1, "one thread was rendered");
  159. assert.equal($('.page-header h1').text(), "Lorem",
  160. "category name is shown in header");
  161. assert.equal($('.category-description .lead p').text(),
  162. "Lorem ipsum dolor met sit amet eli.",
  163. "category description was displayed");
  164. assert.equal($('.subcategories-list li').length, 2,
  165. "categories picker shows two cats");
  166. assert.equal(document.title, "Lorem | Test Forum",
  167. "valid page title is set");
  168. assert.equal(element.text(), "Test thread", "test thread was displayed");
  169. assert.ok(!$('.pager-more').length, "load more button is hidden");
  170. done();
  171. });
  172. });
  173. it("loads data", function(done) {
  174. $.mockjax({
  175. url: '/test-api/threads/?category=1&list=all&page=1',
  176. status: 200,
  177. responseText: {
  178. results: [thread],
  179. count: 1,
  180. more: 0,
  181. page: 1,
  182. pages: 1,
  183. subcategories: [2]
  184. }
  185. });
  186. /* jshint ignore:start */
  187. testUtils.render(<Route route={route} threads={[]} />);
  188. /* jshint ignore:end */
  189. window.setTimeout(function() {
  190. let state = store.getState().threads;
  191. assert.equal(state.length, 1, "one thread was loaded into store");
  192. assert.equal(state[0].title, "Test thread", "test thread was loaded");
  193. assert.equal($('.subcategories-list li').length, 1,
  194. "categories picker shows one category");
  195. done();
  196. } , 300);
  197. });
  198. it("loads empty", function(done) {
  199. $.mockjax({
  200. url: '/test-api/threads/?category=1&list=all&page=1',
  201. status: 200,
  202. responseText: {
  203. results: [],
  204. count: 0,
  205. more: 0,
  206. page: 1,
  207. pages: 1,
  208. subcategories: []
  209. }
  210. });
  211. /* jshint ignore:start */
  212. testUtils.render(<Route route={route} threads={[]} />);
  213. /* jshint ignore:end */
  214. window.setTimeout(function() {
  215. let state = store.getState().threads;
  216. assert.equal(state.length, 0, "no threads were loaded into store");
  217. assert.equal($('.subcategories-list li').length, 0,
  218. "categories picker is empty");
  219. done();
  220. } , 300);
  221. });
  222. it("handles backend error", function(done) {
  223. $.mockjax({
  224. url: '/test-api/threads/?category=1&list=all&page=1',
  225. status: 500
  226. });
  227. snackbarStore.callback(function(message) {
  228. assert.deepEqual(message, {
  229. message: "Unknown error has occured.",
  230. type: 'error'
  231. }, "error message was shown");
  232. done();
  233. });
  234. /* jshint ignore:start */
  235. testUtils.render(<Route route={route} threads={[]} />);
  236. /* jshint ignore:end */
  237. });
  238. it("handles backend rejection", function(done) {
  239. $.mockjax({
  240. url: '/test-api/threads/?category=1&list=all&page=1',
  241. status: 403,
  242. responseText: {
  243. detail: "Nope, can't show you that."
  244. }
  245. });
  246. snackbarStore.callback(function(message) {
  247. assert.deepEqual(message, {
  248. message: "Nope, can't show you that.",
  249. type: 'error'
  250. }, "error message was shown");
  251. done();
  252. });
  253. /* jshint ignore:start */
  254. testUtils.render(<Route route={route} threads={[]} />);
  255. /* jshint ignore:end */
  256. });
  257. it("loads additional threads", function(done) {
  258. $.mockjax({
  259. url: '/test-api/threads/?category=1&list=all&page=1',
  260. status: 200,
  261. responseText: {
  262. results: [thread],
  263. count: 1,
  264. more: 1,
  265. page: 1,
  266. pages: 2,
  267. subcategories: []
  268. }
  269. });
  270. $.mockjax({
  271. url: '/test-api/threads/?category=1&list=all&page=2',
  272. status: 200,
  273. responseText: {
  274. results: [Object.assign({}, thread, {
  275. id: 2,
  276. title: "Other thread",
  277. last_post: 2
  278. })],
  279. count: 1,
  280. more: 0,
  281. page: 2,
  282. pages: 2,
  283. subcategories: [1]
  284. }
  285. });
  286. /* jshint ignore:start */
  287. testUtils.render(<Route route={route} threads={[]} />);
  288. /* jshint ignore:end */
  289. testUtils.onElement('#test-mount .pager-more .btn', function() {
  290. assert.ok(true, "load more button is present");
  291. testUtils.simulateClick('#test-mount .pager-more .btn');
  292. window.setTimeout(function() {
  293. let state = store.getState().threads;
  294. assert.equal(state.length, 2, "two thread were loaded into store");
  295. assert.equal(state[0].title, "Test thread", "test thread was loaded");
  296. assert.equal(state[1].title, "Other thread", "new thread was added");
  297. assert.equal($('.subcategories-list li').length, 1,
  298. "categories picker shows one category");
  299. done();
  300. } , 300);
  301. });
  302. });
  303. it("renders for forum index", function(done) {
  304. $.mockjax({
  305. url: '/test-api/threads/?category=&list=all&page=1',
  306. status: 200,
  307. responseText: {
  308. results: [thread],
  309. count: 1,
  310. more: 0,
  311. page: 1,
  312. pages: 1,
  313. subcategories: [2]
  314. }
  315. });
  316. /* jshint ignore:start */
  317. let finalRoute = Object.assign({}, route, {
  318. category: Object.assign({}, route.category, {
  319. special_role: true
  320. })
  321. });
  322. testUtils.render(<Route route={finalRoute} threads={[]} />);
  323. /* jshint ignore:end */
  324. window.setTimeout(function() {
  325. assert.equal($('.page-header h1').text(), "Forum Index",
  326. "forum title is shown in header");
  327. assert.equal(document.title, "Forum Index",
  328. "valid page title is set");
  329. done();
  330. } , 300);
  331. });
  332. });