route.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  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, { hydrateThread } from 'misago/reducers/threads'; // jshint ignore:line
  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_post_url: '/thread/test-thread-132/last/',
  108. last_poster_name: 'BobBoberson',
  109. last_poster_url: null,
  110. last_post_on: moment().format(),
  111. is_read: true,
  112. acl: {},
  113. };
  114. describe("Threads List Route", function() {
  115. beforeEach(function() {
  116. snackbarStore = testUtils.snackbarStoreMock();
  117. snackbar.init(snackbarStore);
  118. misago._context = {
  119. CATEGORIES_ON_INDEX: false,
  120. THREADS_API: '/test-api/threads/',
  121. SETTINGS: {
  122. forum_name: "Test Forum",
  123. forum_index_title: "Forum Index"
  124. }
  125. };
  126. store.constructor();
  127. store.addReducer('threads', reducer, []);
  128. store.addReducer('tick', function(state, action) {
  129. if (action || true) {
  130. return {'tick': 123};
  131. }
  132. }, {});
  133. store.init();
  134. title.init(
  135. misago._context.SETTINGS.forum_index_title,
  136. misago._context.SETTINGS.forum_name);
  137. });
  138. afterEach(function() {
  139. testUtils.unmountComponents();
  140. testUtils.snackbarClear(snackbar);
  141. $.mockjax.clear();
  142. });
  143. it("inits with preloaded data", function(done) {
  144. misago._context.THREADS = {
  145. results: [],
  146. count: 1,
  147. more: 0,
  148. page: 1,
  149. pages: 1,
  150. subcategories: [2, 3]
  151. };
  152. /* jshint ignore:start */
  153. let threads = [
  154. hydrateThread(thread)
  155. ];
  156. testUtils.render(<Route route={route} threads={threads} />);
  157. /* jshint ignore:end */
  158. testUtils.onElement('#test-mount .thread-title', function(element) {
  159. assert.equal($('.threads-list .item-read').length, 1,
  160. "one thread was rendered");
  161. assert.equal($('.page-header h1').text(), "Lorem",
  162. "category name is shown in header");
  163. assert.equal($('.category-description .lead p').text(),
  164. "Lorem ipsum dolor met sit amet eli.",
  165. "category description was displayed");
  166. assert.equal($('.subcategories-list li').length, 2,
  167. "categories picker shows two cats");
  168. assert.equal(document.title, "Lorem | Test Forum",
  169. "valid page title is set");
  170. assert.equal(element.text(), "Test thread", "test thread was displayed");
  171. assert.ok(!$('.pager-more').length, "load more button is hidden");
  172. done();
  173. });
  174. });
  175. it("loads data", function(done) {
  176. $.mockjax({
  177. url: '/test-api/threads/?category=1&list=all&page=1',
  178. status: 200,
  179. responseText: {
  180. results: [thread],
  181. count: 1,
  182. more: 0,
  183. page: 1,
  184. pages: 1,
  185. subcategories: [2]
  186. }
  187. });
  188. /* jshint ignore:start */
  189. testUtils.render(<Route route={route} threads={[]} />);
  190. /* jshint ignore:end */
  191. window.setTimeout(function() {
  192. let state = store.getState().threads;
  193. assert.equal(state.length, 1, "one thread was loaded into store");
  194. assert.equal(state[0].title, "Test thread", "test thread was loaded");
  195. assert.equal($('.subcategories-list li').length, 1,
  196. "categories picker shows one category");
  197. done();
  198. } , 300);
  199. });
  200. it("loads empty", function(done) {
  201. $.mockjax({
  202. url: '/test-api/threads/?category=1&list=all&page=1',
  203. status: 200,
  204. responseText: {
  205. results: [],
  206. count: 0,
  207. more: 0,
  208. page: 1,
  209. pages: 1,
  210. subcategories: []
  211. }
  212. });
  213. /* jshint ignore:start */
  214. testUtils.render(<Route route={route} threads={[]} />);
  215. /* jshint ignore:end */
  216. window.setTimeout(function() {
  217. let state = store.getState().threads;
  218. assert.equal(state.length, 0, "no threads were loaded into store");
  219. assert.equal($('.subcategories-list li').length, 0,
  220. "categories picker is empty");
  221. done();
  222. } , 300);
  223. });
  224. it("handles backend error", function(done) {
  225. $.mockjax({
  226. url: '/test-api/threads/?category=1&list=all&page=1',
  227. status: 500
  228. });
  229. snackbarStore.callback(function(message) {
  230. assert.deepEqual(message, {
  231. message: "Unknown error has occured.",
  232. type: 'error'
  233. }, "error message was shown");
  234. done();
  235. });
  236. /* jshint ignore:start */
  237. testUtils.render(<Route route={route} threads={[]} />);
  238. /* jshint ignore:end */
  239. });
  240. it("handles backend rejection", function(done) {
  241. $.mockjax({
  242. url: '/test-api/threads/?category=1&list=all&page=1',
  243. status: 403,
  244. responseText: {
  245. detail: "Nope, can't show you that."
  246. }
  247. });
  248. snackbarStore.callback(function(message) {
  249. assert.deepEqual(message, {
  250. message: "Nope, can't show you that.",
  251. type: 'error'
  252. }, "error message was shown");
  253. done();
  254. });
  255. /* jshint ignore:start */
  256. testUtils.render(<Route route={route} threads={[]} />);
  257. /* jshint ignore:end */
  258. });
  259. it("loads additional threads", function(done) {
  260. $.mockjax({
  261. url: '/test-api/threads/?category=1&list=all&page=1',
  262. status: 200,
  263. responseText: {
  264. results: [thread],
  265. count: 1,
  266. more: 1,
  267. page: 1,
  268. pages: 2,
  269. subcategories: []
  270. }
  271. });
  272. $.mockjax({
  273. url: '/test-api/threads/?category=1&list=all&page=2',
  274. status: 200,
  275. responseText: {
  276. results: [Object.assign({}, thread, {
  277. id: 2,
  278. title: "Other thread",
  279. last_post: 2
  280. })],
  281. count: 1,
  282. more: 0,
  283. page: 2,
  284. pages: 2,
  285. subcategories: [1]
  286. }
  287. });
  288. /* jshint ignore:start */
  289. testUtils.render(<Route route={route} threads={[]} />);
  290. /* jshint ignore:end */
  291. testUtils.onElement('#test-mount .pager-more .btn', function() {
  292. assert.ok(true, "load more button is present");
  293. testUtils.simulateClick('#test-mount .pager-more .btn');
  294. window.setTimeout(function() {
  295. let state = store.getState().threads;
  296. assert.equal(state.length, 2, "two thread were loaded into store");
  297. assert.equal(state[0].title, "Test thread", "test thread was loaded");
  298. assert.equal(state[1].title, "Other thread", "new thread was added");
  299. assert.equal($('.subcategories-list li').length, 1,
  300. "categories picker shows one category");
  301. done();
  302. } , 300);
  303. });
  304. });
  305. it("renders for forum index", function(done) {
  306. $.mockjax({
  307. url: '/test-api/threads/?category=&list=all&page=1',
  308. status: 200,
  309. responseText: {
  310. results: [thread],
  311. count: 1,
  312. more: 0,
  313. page: 1,
  314. pages: 1,
  315. subcategories: [2]
  316. }
  317. });
  318. /* jshint ignore:start */
  319. let finalRoute = Object.assign({}, route, {
  320. category: Object.assign({}, route.category, {
  321. special_role: true
  322. })
  323. });
  324. testUtils.render(<Route route={finalRoute} threads={[]} />);
  325. /* jshint ignore:end */
  326. window.setTimeout(function() {
  327. assert.equal($('.page-header h1').text(), "Forum Index",
  328. "forum title is shown in header");
  329. assert.equal(document.title, "Forum Index",
  330. "valid page title is set");
  331. done();
  332. } , 300);
  333. });
  334. });