route.js 10.0 KB

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