v5.page.test.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /* eslint-disable no-unused-vars */
  2. import { advanceTo } from 'jest-date-mock';
  3. import mongoose from 'mongoose';
  4. import { getInstance } from '../setup-crowi';
  5. describe('PageService page operations with only public pages', () => {
  6. let dummyUser1;
  7. let dummyUser2;
  8. let crowi;
  9. let Page;
  10. let Revision;
  11. let User;
  12. let Tag;
  13. let PageTagRelation;
  14. let Bookmark;
  15. let Comment;
  16. let ShareLink;
  17. let PageRedirect;
  18. let xssSpy;
  19. let rootPage;
  20. // pass unless the data is one of [false, 0, '', null, undefined, NaN]
  21. const expectAllToBeTruthy = (dataList) => {
  22. dataList.forEach((data) => {
  23. expect(data).toBeTruthy();
  24. });
  25. };
  26. beforeAll(async() => {
  27. crowi = await getInstance();
  28. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', { 'app:isV5Compatible': true });
  29. User = mongoose.model('User');
  30. Page = mongoose.model('Page');
  31. Revision = mongoose.model('Revision');
  32. Tag = mongoose.model('Tag');
  33. PageTagRelation = mongoose.model('PageTagRelation');
  34. Bookmark = mongoose.model('Bookmark');
  35. Comment = mongoose.model('Comment');
  36. ShareLink = mongoose.model('ShareLink');
  37. PageRedirect = mongoose.model('PageRedirect');
  38. /*
  39. * Common
  40. */
  41. await User.insertMany([
  42. { name: 'v5DummyUser1', username: 'v5DummyUser1', email: 'v5DummyUser1@example.com' },
  43. { name: 'v5DummyUser2', username: 'v5DummyUser2', email: 'v5DummyUser2@example.com' },
  44. ]);
  45. dummyUser1 = await User.findOne({ username: 'v5DummyUser1' });
  46. if (dummyUser1 == null) {
  47. dummyUser1 = await User.create({ name: 'v5DummyUser1', username: 'v5DummyUser1', email: 'v5DummyUser1@example.com' });
  48. }
  49. dummyUser2 = await User.findOne({ username: 'v5DummyUser2' });
  50. if (dummyUser2 == null) {
  51. dummyUser2 = await User.create({ name: 'v5DummyUser2', username: 'v5DummyUser2', email: 'v5DummyUser2@example.com' });
  52. }
  53. xssSpy = jest.spyOn(crowi.xss, 'process').mockImplementation(path => path);
  54. rootPage = await Page.findOne({ path: '/' });
  55. if (rootPage == null) {
  56. const pages = await Page.insertMany([{ path: '/', grant: Page.GRANT_PUBLIC }]);
  57. rootPage = pages[0];
  58. }
  59. /*
  60. * Rename
  61. */
  62. const pageIdForRename1 = new mongoose.Types.ObjectId();
  63. const pageIdForRename2 = new mongoose.Types.ObjectId();
  64. const pageIdForRename3 = new mongoose.Types.ObjectId();
  65. const pageIdForRename4 = new mongoose.Types.ObjectId();
  66. const pageIdForRename5 = new mongoose.Types.ObjectId();
  67. const pageIdForRename7 = new mongoose.Types.ObjectId();
  68. const pageIdForRename8 = new mongoose.Types.ObjectId();
  69. const pageIdForRename9 = new mongoose.Types.ObjectId();
  70. const pageIdForRename10 = new mongoose.Types.ObjectId();
  71. const pageIdForRename11 = new mongoose.Types.ObjectId();
  72. const pageIdForRename12 = new mongoose.Types.ObjectId();
  73. const pageIdForRename13 = new mongoose.Types.ObjectId();
  74. const pageIdForRename14 = new mongoose.Types.ObjectId();
  75. const pageIdForRename16 = new mongoose.Types.ObjectId();
  76. // Create Pages
  77. await Page.insertMany([
  78. // parents
  79. {
  80. _id: pageIdForRename1,
  81. path: '/v5_ParentForRename1',
  82. grant: Page.GRANT_PUBLIC,
  83. creator: dummyUser1,
  84. lastUpdateUser: dummyUser1._id,
  85. parent: rootPage._id,
  86. },
  87. {
  88. _id: pageIdForRename2,
  89. path: '/v5_ParentForRename2',
  90. grant: Page.GRANT_PUBLIC,
  91. parent: rootPage._id,
  92. isEmpty: true,
  93. },
  94. {
  95. // id not needed for this data
  96. path: '/v5_ParentForRename2/dummyChild1',
  97. grant: Page.GRANT_PUBLIC,
  98. creator: dummyUser1,
  99. lastUpdateUser: dummyUser1._id,
  100. parent: pageIdForRename2,
  101. },
  102. {
  103. _id: pageIdForRename3,
  104. path: '/v5_ParentForRename3',
  105. grant: Page.GRANT_PUBLIC,
  106. creator: dummyUser1,
  107. lastUpdateUser: dummyUser1._id,
  108. parent: rootPage._id,
  109. },
  110. {
  111. _id: pageIdForRename4,
  112. path: '/v5_ParentForRename4',
  113. grant: Page.GRANT_PUBLIC,
  114. creator: dummyUser1,
  115. lastUpdateUser: dummyUser1._id,
  116. parent: rootPage._id,
  117. },
  118. {
  119. _id: pageIdForRename5,
  120. path: '/v5_ParentForRename5',
  121. grant: Page.GRANT_PUBLIC,
  122. creator: dummyUser1,
  123. lastUpdateUser: dummyUser1._id,
  124. parent: rootPage._id,
  125. },
  126. {
  127. _id: pageIdForRename7,
  128. path: '/v5_ParentForRename7',
  129. grant: Page.GRANT_PUBLIC,
  130. creator: dummyUser1,
  131. lastUpdateUser: dummyUser1._id,
  132. parent: rootPage._id,
  133. },
  134. {
  135. _id: pageIdForRename8,
  136. path: '/v5_ParentForRename8',
  137. grant: Page.GRANT_PUBLIC,
  138. creator: dummyUser1,
  139. lastUpdateUser: dummyUser1._id,
  140. parent: rootPage._id,
  141. },
  142. {
  143. _id: pageIdForRename9,
  144. path: '/v5_ParentForRename9',
  145. grant: Page.GRANT_PUBLIC,
  146. creator: dummyUser1,
  147. lastUpdateUser: dummyUser1._id,
  148. parent: rootPage._id,
  149. },
  150. // children
  151. {
  152. _id: pageIdForRename10,
  153. path: '/v5_ChildForRename1',
  154. grant: Page.GRANT_PUBLIC,
  155. creator: dummyUser1,
  156. lastUpdateUser: dummyUser1._id,
  157. parent: rootPage._id,
  158. },
  159. {
  160. _id: pageIdForRename11,
  161. path: '/v5_ChildForRename2',
  162. grant: Page.GRANT_PUBLIC,
  163. creator: dummyUser1,
  164. lastUpdateUser: dummyUser1._id,
  165. parent: rootPage._id,
  166. },
  167. {
  168. _id: pageIdForRename12,
  169. path: '/v5_ChildForRename3',
  170. grant: Page.GRANT_PUBLIC,
  171. creator: dummyUser1,
  172. lastUpdateUser: dummyUser1._id,
  173. parent: rootPage._id,
  174. updatedAt: new Date('2021'),
  175. },
  176. {
  177. _id: pageIdForRename13,
  178. path: '/v5_ChildForRename4',
  179. grant: Page.GRANT_PUBLIC,
  180. creator: dummyUser1,
  181. lastUpdateUser: dummyUser1._id,
  182. parent: rootPage._id,
  183. },
  184. {
  185. _id: pageIdForRename14,
  186. path: '/v5_ChildForRename5',
  187. grant: Page.GRANT_PUBLIC,
  188. creator: dummyUser1,
  189. lastUpdateUser: dummyUser1._id,
  190. parent: rootPage._id,
  191. },
  192. {
  193. _id: pageIdForRename16,
  194. path: '/v5_ChildForRename7',
  195. grant: Page.GRANT_PUBLIC,
  196. parent: rootPage._id,
  197. isEmpty: true,
  198. },
  199. // Grandchild
  200. {
  201. path: '/v5_ChildForRename5/v5_GrandchildForRename5',
  202. grant: Page.GRANT_PUBLIC,
  203. creator: dummyUser1,
  204. lastUpdateUser: dummyUser1._id,
  205. parent: pageIdForRename14,
  206. updatedAt: new Date('2021'),
  207. },
  208. {
  209. path: '/v5_ChildForRename7/v5_GrandchildForRename7',
  210. grant: Page.GRANT_PUBLIC,
  211. creator: dummyUser1,
  212. lastUpdateUser: dummyUser1._id,
  213. parent: pageIdForRename16,
  214. },
  215. ]);
  216. });
  217. describe('Rename', () => {
  218. const renamePage = async(page, newPagePath, user, options) => {
  219. // mock return value
  220. const mockedResumableRenameDescendants = jest.spyOn(crowi.pageService, 'resumableRenameDescendants').mockReturnValue(null);
  221. const mockedCreateAndSendNotifications = jest.spyOn(crowi.pageService, 'createAndSendNotifications').mockReturnValue(null);
  222. const renamedPage = await crowi.pageService.renamePage(page, newPagePath, user, options);
  223. // retrieve the arguments passed when calling method resumableRenameDescendants inside renamePage method
  224. const argsForResumableRenameDescendants = mockedResumableRenameDescendants.mock.calls[0];
  225. // restores the original implementation
  226. mockedResumableRenameDescendants.mockRestore();
  227. mockedCreateAndSendNotifications.mockRestore();
  228. // rename descendants
  229. await crowi.pageService.resumableRenameDescendants(...argsForResumableRenameDescendants);
  230. return renamedPage;
  231. };
  232. test('Should NOT rename top page', async() => {
  233. expectAllToBeTruthy([rootPage]);
  234. let isThrown = false;
  235. try {
  236. await crowi.pageService.renamePage(rootPage, '/new_root', dummyUser1, {});
  237. }
  238. catch (err) {
  239. isThrown = true;
  240. }
  241. expect(isThrown).toBe(true);
  242. });
  243. test('Should rename/move to under non-empty page', async() => {
  244. const parentPage = await Page.findOne({ path: '/v5_ParentForRename1' });
  245. const childPage = await Page.findOne({ path: '/v5_ChildForRename1' });
  246. expectAllToBeTruthy([childPage, parentPage]);
  247. const newPath = '/v5_ParentForRename1/renamedChildForRename1';
  248. const renamedPage = await renamePage(childPage, newPath, dummyUser1, {});
  249. const childPageBeforeRename = await Page.findOne({ path: '/v5_ChildForRename1' });
  250. expect(xssSpy).toHaveBeenCalled();
  251. expect(renamedPage.path).toBe(newPath);
  252. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  253. expect(childPageBeforeRename).toBeNull();
  254. });
  255. test('Should rename/move to under empty page', async() => {
  256. const parentPage = await Page.findOne({ path: '/v5_ParentForRename2' });
  257. const childPage = await Page.findOne({ path: '/v5_ChildForRename2' });
  258. expectAllToBeTruthy([childPage, parentPage]);
  259. expect(parentPage.isEmpty).toBe(true);
  260. const newPath = '/v5_ParentForRename2/renamedChildForRename2';
  261. const renamedPage = await renamePage(childPage, newPath, dummyUser1, {});
  262. const childPageBeforeRename = await Page.findOne({ path: '/v5_ChildForRename2' });
  263. expect(xssSpy).toHaveBeenCalled();
  264. expect(renamedPage.path).toBe(newPath);
  265. expect(parentPage.isEmpty).toBe(true);
  266. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  267. expect(childPageBeforeRename).toBeNull();
  268. });
  269. test('Should rename/move with option updateMetadata: true', async() => {
  270. const parentPage = await Page.findOne({ path: '/v5_ParentForRename3' });
  271. const childPage = await Page.findOne({ path: '/v5_ChildForRename3' });
  272. expectAllToBeTruthy([childPage, parentPage]);
  273. expect(childPage.lastUpdateUser).toStrictEqual(dummyUser1._id);
  274. const newPath = '/v5_ParentForRename3/renamedChildForRename3';
  275. const oldUdpateAt = childPage.updatedAt;
  276. const renamedPage = await renamePage(childPage, newPath, dummyUser2, { updateMetadata: true });
  277. expect(xssSpy).toHaveBeenCalled();
  278. expect(renamedPage.path).toBe(newPath);
  279. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  280. expect(renamedPage.lastUpdateUser).toStrictEqual(dummyUser2._id);
  281. expect(renamedPage.updatedAt.getFullYear()).toBeGreaterThan(oldUdpateAt.getFullYear());
  282. });
  283. // ****************** TODO ******************
  284. // uncomment the next test when working on 88097
  285. // ******************************************
  286. // test('Should move with option createRedirectPage: true', async() => {
  287. // const parentPage = await Page.findOne({ path: '/v5_ParentForRename4' });
  288. // const childPage = await Page.findOne({ path: '/v5_ChildForRename4' });
  289. // expectAllToBeTruthy([parentPage, childPage]);
  290. // // rename target page
  291. // const newPath = '/v5_ParentForRename4/renamedChildForRename4';
  292. // const renamedPage = await renamePage(childPage, newPath, dummyUser2, { createRedirectPage: true });
  293. // const pageRedirect = await PageRedirect.find({ fromPath: childPage.path, toPath: renamedPage.path });
  294. // expect(xssSpy).toHaveBeenCalled();
  295. // expect(renamedPage.path).toBe(newPath);
  296. // expect(renamedPage.parent).toStrictEqual(parentPage._id);
  297. // expect(pageRedirect.length).toBeGreaterThan(0);
  298. // });
  299. test('Should rename/move with descendants', async() => {
  300. const parentPage = await Page.findOne({ path: '/v5_ParentForRename5' });
  301. const childPage = await Page.findOne({ path: '/v5_ChildForRename5' });
  302. const grandchild = await Page.findOne({ parent: childPage._id, path: '/v5_ChildForRename5/v5_GrandchildForRename5' });
  303. expectAllToBeTruthy([parentPage, childPage, grandchild]);
  304. const newPath = '/v5_ParentForRename5/renamedChildForRename5';
  305. const renamedPage = await renamePage(childPage, newPath, dummyUser1, {});
  306. // find child of renamed page
  307. const renamedGrandchild = await Page.findOne({ parent: renamedPage._id });
  308. const childPageBeforeRename = await Page.findOne({ path: '/v5_ChildForRename5' });
  309. const grandchildBeforeRename = await Page.findOne({ path: grandchild.path });
  310. expect(xssSpy).toHaveBeenCalled();
  311. expect(renamedPage.path).toBe(newPath);
  312. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  313. expect(childPageBeforeRename).toBeNull();
  314. expect(grandchildBeforeRename).toBeNull();
  315. // grandchild's parent should be the renamed page
  316. expect(renamedGrandchild.parent).toStrictEqual(renamedPage._id);
  317. expect(renamedGrandchild.path).toBe('/v5_ParentForRename5/renamedChildForRename5/v5_GrandchildForRename5');
  318. });
  319. test('Should rename/move empty page', async() => {
  320. const parentPage = await Page.findOne({ path: '/v5_ParentForRename7' });
  321. const childPage = await Page.findOne({ path: '/v5_ChildForRename7', isEmpty: true });
  322. const grandchild = await Page.findOne({ parent: childPage._id, path: '/v5_ChildForRename7/v5_GrandchildForRename7' });
  323. expectAllToBeTruthy([parentPage, childPage, grandchild]);
  324. const newPath = '/v5_ParentForRename7/renamedChildForRename7';
  325. const renamedPage = await renamePage(childPage, newPath, dummyUser1, {});
  326. const grandchildAfterRename = await Page.findOne({ parent: renamedPage._id });
  327. const grandchildBeforeRename = await Page.findOne({ path: '/v5_ChildForRename7/v5_GrandchildForRename7' });
  328. expect(xssSpy).toHaveBeenCalled();
  329. expect(renamedPage.path).toBe(newPath);
  330. expect(renamedPage.isEmpty).toBe(true);
  331. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  332. expect(grandchildBeforeRename).toBeNull();
  333. // grandchild's parent should be renamed page
  334. expect(grandchildAfterRename.parent).toStrictEqual(renamedPage._id);
  335. expect(grandchildAfterRename.path).toBe('/v5_ParentForRename7/renamedChildForRename7/v5_GrandchildForRename7');
  336. });
  337. test('Should NOT rename/move with existing path', async() => {
  338. const page = await Page.findOne({ path: '/v5_ParentForRename8' });
  339. expectAllToBeTruthy([page]);
  340. const newPath = '/v5_ParentForRename9';
  341. let isThrown;
  342. try {
  343. await renamePage(page, newPath, dummyUser1, {});
  344. }
  345. catch (err) {
  346. isThrown = true;
  347. }
  348. expect(isThrown).toBe(true);
  349. });
  350. });
  351. });
  352. describe('PageService page operations with non-public pages', () => {
  353. // TODO: write test code
  354. });