pagev5.test.ts 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  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. let dummyUser1Page;
  21. // parents
  22. let parentForRename1;
  23. let parentForRename2;
  24. let parentForRename3;
  25. let parentForRename4;
  26. let parentForRename5;
  27. let parentForRename6;
  28. let parentForRename7;
  29. let parentForRename8;
  30. // children
  31. let childForRename1;
  32. let childForRename2;
  33. let childForRename3;
  34. let childForRename4;
  35. let childForRename5;
  36. let childForRename6;
  37. let childForRename7;
  38. /**
  39. * Delete
  40. */
  41. let v5PageForDelete1;
  42. let v5PageForDelete2;
  43. let v5PageForDelete3;
  44. let v5PageForDelete4;
  45. let v5PageForDelete5;
  46. let v5PageForDelete6;
  47. let tagForDelete1;
  48. let tagForDelete2;
  49. /**
  50. * Delete completely
  51. */
  52. let v5PageForDeleteCompletely1;
  53. let v5PageForDeleteCompletely2;
  54. let v5PageForDeleteCompletely3;
  55. let v5PageForDeleteCompletely4;
  56. let v5PageForDeleteCompletely5;
  57. let v5PageForDeleteCompletely6;
  58. let v5PageForDeleteCompletely7;
  59. let v5PageForDeleteCompletely8;
  60. let tagForDeleteCompletely1;
  61. let tagForDeleteCompletely2;
  62. beforeAll(async() => {
  63. crowi = await getInstance();
  64. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', { 'app:isV5Compatible': true });
  65. User = mongoose.model('User');
  66. Page = mongoose.model('Page');
  67. Revision = mongoose.model('Revision');
  68. Tag = mongoose.model('Tag');
  69. PageTagRelation = mongoose.model('PageTagRelation');
  70. Bookmark = mongoose.model('Bookmark');
  71. Comment = mongoose.model('Comment');
  72. ShareLink = mongoose.model('ShareLink');
  73. PageRedirect = mongoose.model('PageRedirect');
  74. /*
  75. * Common
  76. */
  77. await User.insertMany([
  78. { name: 'dummyUser1', username: 'dummyUser1', email: 'dummyUser1@example.com' },
  79. { name: 'dummyUser2', username: 'dummyUser2', email: 'dummyUser2@example.com' },
  80. ]);
  81. dummyUser1 = await User.findOne({ username: 'dummyUser1' });
  82. dummyUser2 = await User.findOne({ username: 'dummyUser2' });
  83. xssSpy = jest.spyOn(crowi.xss, 'process').mockImplementation(path => path);
  84. /*
  85. * Rename
  86. */
  87. // delete root page if any created by other test file
  88. const pages = await Page.find({ path: '/' });
  89. if (pages.length > 0) {
  90. await Page.deleteOne({ path: '/' });
  91. }
  92. // then create new root page
  93. rootPage = await Page.create('/', 'body', dummyUser1._id, {});
  94. // then create user's page
  95. await Page.insertMany([
  96. {
  97. path: '/user',
  98. grant: Page.GRANT_PUBLIC,
  99. isEmpty: true,
  100. parent: rootPage._id,
  101. status: Page.STATUS_PUBLISHED,
  102. },
  103. ]);
  104. dummyUser1Page = await Page.create('/user/dummyUser1', 'dummyUser1_page', dummyUser1._id, {});
  105. await Page.create('/user/dummyUser2', 'dummyUser2_page', dummyUser2._id, {});
  106. // Create Pages
  107. await Page.insertMany([
  108. // parents
  109. {
  110. path: '/v5_ParentForRename1',
  111. grant: Page.GRANT_PUBLIC,
  112. creator: dummyUser1,
  113. lastUpdateUser: dummyUser1._id,
  114. parent: rootPage._id,
  115. },
  116. {
  117. path: '/v5_ParentForRename2',
  118. grant: Page.GRANT_PUBLIC,
  119. creator: dummyUser1,
  120. lastUpdateUser: dummyUser1._id,
  121. parent: rootPage._id,
  122. isEmpty: true,
  123. },
  124. {
  125. path: '/v5_ParentForRename3',
  126. grant: Page.GRANT_PUBLIC,
  127. creator: dummyUser1,
  128. lastUpdateUser: dummyUser1._id,
  129. parent: rootPage._id,
  130. },
  131. {
  132. path: '/v5_ParentForRename4',
  133. grant: Page.GRANT_PUBLIC,
  134. creator: dummyUser1,
  135. lastUpdateUser: dummyUser1._id,
  136. parent: rootPage._id,
  137. },
  138. {
  139. path: '/v5_ParentForRename5',
  140. grant: Page.GRANT_PUBLIC,
  141. creator: dummyUser1,
  142. lastUpdateUser: dummyUser1._id,
  143. parent: rootPage._id,
  144. },
  145. {
  146. path: '/v5_ParentForRename6',
  147. grant: Page.GRANT_PUBLIC,
  148. creator: dummyUser1,
  149. lastUpdateUser: dummyUser1._id,
  150. parent: rootPage._id,
  151. },
  152. {
  153. path: '/v5_ParentForRename7',
  154. grant: Page.GRANT_PUBLIC,
  155. creator: dummyUser1,
  156. lastUpdateUser: dummyUser1._id,
  157. parent: rootPage._id,
  158. },
  159. {
  160. path: '/v5_ParentForRename8',
  161. grant: Page.GRANT_PUBLIC,
  162. creator: dummyUser1,
  163. lastUpdateUser: dummyUser1._id,
  164. parent: rootPage._id,
  165. },
  166. {
  167. path: '/v5_ParentForRename9',
  168. grant: Page.GRANT_PUBLIC,
  169. creator: dummyUser1,
  170. lastUpdateUser: dummyUser1._id,
  171. parent: rootPage._id,
  172. },
  173. // children
  174. {
  175. path: '/v5_ChildForRename1',
  176. grant: Page.GRANT_PUBLIC,
  177. creator: dummyUser1,
  178. lastUpdateUser: dummyUser1._id,
  179. parent: rootPage._id,
  180. },
  181. {
  182. path: '/v5_ChildForRename2',
  183. grant: Page.GRANT_PUBLIC,
  184. creator: dummyUser1,
  185. lastUpdateUser: dummyUser1._id,
  186. parent: rootPage._id,
  187. },
  188. {
  189. path: '/v5_ChildForRename3',
  190. grant: Page.GRANT_PUBLIC,
  191. creator: dummyUser1,
  192. lastUpdateUser: dummyUser1._id,
  193. parent: rootPage._id,
  194. updatedAt: new Date('2021'),
  195. },
  196. {
  197. path: '/v5_ChildForRename4',
  198. grant: Page.GRANT_PUBLIC,
  199. creator: dummyUser1,
  200. lastUpdateUser: dummyUser1._id,
  201. parent: rootPage._id,
  202. },
  203. {
  204. path: '/v5_ChildForRename5',
  205. grant: Page.GRANT_PUBLIC,
  206. creator: dummyUser1,
  207. lastUpdateUser: dummyUser1._id,
  208. parent: rootPage._id,
  209. },
  210. {
  211. path: '/v5_ChildForRename6',
  212. grant: Page.GRANT_RESTRICTED,
  213. creator: dummyUser1,
  214. lastUpdateUser: dummyUser1._id,
  215. parent: rootPage._id,
  216. },
  217. {
  218. path: '/v5_ChildForRename7',
  219. grant: Page.GRANT_PUBLIC,
  220. parent: rootPage._id,
  221. isEmpty: true,
  222. },
  223. ]);
  224. // Find pages as Parent
  225. parentForRename1 = await Page.findOne({ path: '/v5_ParentForRename1' });
  226. parentForRename2 = await Page.findOne({ path: '/v5_ParentForRename2' });
  227. parentForRename3 = await Page.findOne({ path: '/v5_ParentForRename3' });
  228. parentForRename4 = await Page.findOne({ path: '/v5_ParentForRename4' });
  229. parentForRename5 = await Page.findOne({ path: '/v5_ParentForRename5' });
  230. parentForRename6 = await Page.findOne({ path: '/v5_ParentForRename6' });
  231. parentForRename7 = await Page.findOne({ path: '/v5_ParentForRename7' });
  232. parentForRename8 = await Page.findOne({ path: '/v5_ParentForRename8' });
  233. // Find pages as Child
  234. childForRename1 = await Page.findOne({ path: '/v5_ChildForRename1' });
  235. childForRename2 = await Page.findOne({ path: '/v5_ChildForRename2' });
  236. childForRename3 = await Page.findOne({ path: '/v5_ChildForRename3' });
  237. childForRename4 = await Page.findOne({ path: '/v5_ChildForRename4' });
  238. childForRename5 = await Page.findOne({ path: '/v5_ChildForRename5' });
  239. childForRename6 = await Page.findOne({ path: '/v5_ChildForRename6' });
  240. childForRename7 = await Page.findOne({ path: '/v5_ChildForRename7' });
  241. // create grandchild
  242. await Page.insertMany([
  243. // Grandchild
  244. {
  245. path: '/v5_ChildForRename5/v5_GrandchildForRename5',
  246. grant: Page.GRANT_PUBLIC,
  247. creator: dummyUser1,
  248. lastUpdateUser: dummyUser1._id,
  249. parent: childForRename5._id,
  250. updatedAt: new Date('2021'),
  251. },
  252. {
  253. path: '/v5_ChildForRename7/v5_GrandchildForRename7',
  254. grant: Page.GRANT_PUBLIC,
  255. creator: dummyUser1,
  256. lastUpdateUser: dummyUser1._id,
  257. parent: childForRename7._id,
  258. },
  259. ]);
  260. /**
  261. * Delete
  262. */
  263. const pageIdForDelete1 = new mongoose.Types.ObjectId();
  264. const pageIdForDelete2 = new mongoose.Types.ObjectId();
  265. const pageIdForDelete3 = new mongoose.Types.ObjectId();
  266. await Page.insertMany([
  267. {
  268. path: '/trash/v5_PageForDelete1',
  269. grant: Page.GRANT_PUBLIC,
  270. creator: dummyUser1,
  271. lastUpdateUser: dummyUser1._id,
  272. parent: rootPage._id,
  273. status: Page.STATUS_DELETED,
  274. },
  275. {
  276. path: '/v5_PageForDelete2',
  277. grant: Page.GRANT_PUBLIC,
  278. creator: dummyUser1,
  279. lastUpdateUser: dummyUser1._id,
  280. parent: rootPage._id,
  281. status: Page.STATUS_PUBLISHED,
  282. },
  283. {
  284. _id: pageIdForDelete1,
  285. path: '/v5_PageForDelete3',
  286. grant: Page.GRANT_PUBLIC,
  287. creator: dummyUser1,
  288. lastUpdateUser: dummyUser1._id,
  289. parent: rootPage._id,
  290. status: Page.STATUS_PUBLISHED,
  291. },
  292. {
  293. _id: pageIdForDelete2,
  294. path: '/v5_PageForDelete3/v5_PageForDelete4',
  295. grant: Page.GRANT_PUBLIC,
  296. parent: pageIdForDelete1,
  297. status: Page.STATUS_PUBLISHED,
  298. isEmpty: true,
  299. },
  300. {
  301. path: '/v5_PageForDelete3/v5_PageForDelete4/v5_PageForDelete5',
  302. grant: Page.GRANT_PUBLIC,
  303. creator: dummyUser1,
  304. lastUpdateUser: dummyUser1._id,
  305. parent: pageIdForDelete2,
  306. status: Page.STATUS_PUBLISHED,
  307. },
  308. {
  309. _id: pageIdForDelete3,
  310. path: '/v5_PageForDelete6',
  311. grant: Page.GRANT_PUBLIC,
  312. creator: dummyUser1,
  313. lastUpdateUser: dummyUser1._id,
  314. parent: rootPage._id,
  315. status: Page.STATUS_PUBLISHED,
  316. },
  317. ]);
  318. v5PageForDelete1 = await Page.findOne({ path: '/trash/v5_PageForDelete1' });
  319. v5PageForDelete2 = await Page.findOne({ path: '/v5_PageForDelete2' });
  320. v5PageForDelete3 = await Page.findOne({ path: '/v5_PageForDelete3' });
  321. v5PageForDelete4 = await Page.findOne({ path: '/v5_PageForDelete3/v5_PageForDelete4' });
  322. v5PageForDelete5 = await Page.findOne({ path: '/v5_PageForDelete3/v5_PageForDelete4/v5_PageForDelete5' });
  323. v5PageForDelete6 = await Page.findOne({ path: '/v5_PageForDelete6' });
  324. await Tag.insertMany([
  325. { name: 'TagForDelete1' },
  326. { name: 'TagForDelete2' },
  327. ]);
  328. tagForDelete1 = await Tag.findOne({ name: 'TagForDelete1' });
  329. tagForDelete2 = await Tag.findOne({ name: 'TagForDelete2' });
  330. await PageTagRelation.insertMany([
  331. { relatedPage: v5PageForDelete6._id, relatedTag: tagForDelete1 },
  332. { relatedPage: v5PageForDelete6._id, relatedTag: tagForDelete2 },
  333. ]);
  334. /**
  335. * Delete completely
  336. */
  337. const pageIdForDeleteCompletely1 = new mongoose.Types.ObjectId();
  338. const pageIdForDeleteCompletely2 = new mongoose.Types.ObjectId();
  339. const pageIdForDeleteCompletely3 = new mongoose.Types.ObjectId();
  340. const pageIdForDeleteCompletely4 = new mongoose.Types.ObjectId();
  341. const pageIdForDeleteCompletely5 = new mongoose.Types.ObjectId();
  342. const pageIdForDeleteCompletely6 = new mongoose.Types.ObjectId();
  343. const pageIdForDeleteCompletely7 = new mongoose.Types.ObjectId();
  344. const revisionIdForDeleteCompletely1 = new mongoose.Types.ObjectId();
  345. const revisionIdForDeleteCompletely2 = new mongoose.Types.ObjectId();
  346. const revisionIdForDeleteCompletely3 = new mongoose.Types.ObjectId();
  347. const revisionIdForDeleteCompletely4 = new mongoose.Types.ObjectId();
  348. await Page.insertMany([
  349. {
  350. path: '/v5_PageForDeleteCompletely1',
  351. grant: Page.GRANT_PUBLIC,
  352. creator: dummyUser1,
  353. lastUpdateUser: dummyUser1._id,
  354. parent: rootPage._id,
  355. status: Page.STATUS_PUBLISHED,
  356. },
  357. {
  358. _id: pageIdForDeleteCompletely1,
  359. path: '/v5_PageForDeleteCompletely2',
  360. grant: Page.GRANT_PUBLIC,
  361. creator: dummyUser1,
  362. lastUpdateUser: dummyUser1._id,
  363. parent: rootPage._id,
  364. status: Page.STATUS_PUBLISHED,
  365. },
  366. {
  367. _id: pageIdForDeleteCompletely2,
  368. path: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3',
  369. grant: Page.GRANT_PUBLIC,
  370. parent: pageIdForDeleteCompletely1,
  371. status: Page.STATUS_PUBLISHED,
  372. isEmpty: true,
  373. },
  374. {
  375. _id: pageIdForDeleteCompletely3,
  376. path: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3/v5_PageForDeleteCompletely4',
  377. grant: Page.GRANT_PUBLIC,
  378. creator: dummyUser1,
  379. lastUpdateUser: dummyUser1._id,
  380. parent: pageIdForDeleteCompletely2,
  381. status: Page.STATUS_PUBLISHED,
  382. },
  383. {
  384. _id: pageIdForDeleteCompletely4,
  385. path: '/trash/v5_PageForDeleteCompletely5',
  386. grant: Page.GRANT_PUBLIC,
  387. creator: dummyUser1,
  388. lastUpdateUser: dummyUser1._id,
  389. parent: rootPage._id,
  390. status: Page.STATUS_DELETED,
  391. },
  392. {
  393. _id: pageIdForDeleteCompletely5,
  394. path: '/v5_PageForDeleteCompletely6',
  395. grant: Page.GRANT_PUBLIC,
  396. creator: dummyUser1,
  397. lastUpdateUser: dummyUser1._id,
  398. parent: rootPage._id,
  399. status: Page.STATUS_PUBLISHED,
  400. },
  401. {
  402. _id: pageIdForDeleteCompletely6,
  403. path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7',
  404. grant: Page.GRANT_PUBLIC,
  405. creator: dummyUser1,
  406. lastUpdateUser: dummyUser1._id,
  407. parent: pageIdForDeleteCompletely5,
  408. status: Page.STATUS_PUBLISHED,
  409. },
  410. {
  411. _id: pageIdForDeleteCompletely7,
  412. path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7/v5_PageForDeleteCompletely8',
  413. grant: Page.GRANT_PUBLIC,
  414. creator: dummyUser1,
  415. lastUpdateUser: dummyUser1._id,
  416. parent: pageIdForDeleteCompletely6,
  417. status: Page.STATUS_PUBLISHED,
  418. },
  419. ]);
  420. v5PageForDeleteCompletely1 = await Page.findOne({ path: '/v5_PageForDeleteCompletely1' });
  421. v5PageForDeleteCompletely2 = await Page.findOne({ path: '/v5_PageForDeleteCompletely2' });
  422. v5PageForDeleteCompletely3 = await Page.findOne({ path: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3' });
  423. v5PageForDeleteCompletely4 = await Page.findOne({ path: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3/v5_PageForDeleteCompletely4' });
  424. v5PageForDeleteCompletely5 = await Page.findOne({ path: '/trash/v5_PageForDeleteCompletely5' });
  425. v5PageForDeleteCompletely6 = await Page.findOne({ path: '/v5_PageForDeleteCompletely6' });
  426. v5PageForDeleteCompletely7 = await Page.findOne({ path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7' });
  427. v5PageForDeleteCompletely8 = await Page.findOne({ path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7/v5_PageForDeleteCompletely8' });
  428. await Revision.insertMany([
  429. {
  430. _id: revisionIdForDeleteCompletely1,
  431. format: 'markdown',
  432. pageId: pageIdForDeleteCompletely1,
  433. body: 'pageIdForDeleteCompletely1',
  434. },
  435. {
  436. _id: revisionIdForDeleteCompletely2,
  437. format: 'markdown',
  438. pageId: pageIdForDeleteCompletely3,
  439. body: 'pageIdForDeleteCompletely3',
  440. },
  441. {
  442. _id: revisionIdForDeleteCompletely3,
  443. format: 'markdown',
  444. pageId: pageIdForDeleteCompletely4,
  445. body: 'pageIdForDeleteCompletely4',
  446. },
  447. {
  448. _id: revisionIdForDeleteCompletely4,
  449. format: 'markdown',
  450. pageId: pageIdForDeleteCompletely1,
  451. body: 'comment_pageIdForDeleteCompletely2',
  452. },
  453. ]);
  454. await Tag.insertMany([
  455. { name: 'TagForDeleteCompletely1' },
  456. { name: 'TagForDeleteCompletely2' },
  457. ]);
  458. tagForDeleteCompletely1 = await Tag.findOne({ name: 'TagForDeleteCompletely1' });
  459. tagForDeleteCompletely2 = await Tag.findOne({ name: 'TagForDeleteCompletely2' });
  460. await PageTagRelation.insertMany([
  461. { relatedPage: v5PageForDeleteCompletely2._id, relatedTag: tagForDeleteCompletely1 },
  462. { relatedPage: v5PageForDeleteCompletely4._id, relatedTag: tagForDeleteCompletely2 },
  463. ]);
  464. await Bookmark.insertMany([
  465. {
  466. page: v5PageForDeleteCompletely2._id,
  467. user: dummyUser1._id,
  468. },
  469. ]);
  470. await Comment.insertMany([
  471. {
  472. commentPosition: -1,
  473. isMarkdown: true,
  474. page: v5PageForDeleteCompletely2._id,
  475. creator: dummyUser1._id,
  476. revision: revisionIdForDeleteCompletely4,
  477. comment: 'comment_ForDeleteCompletely4',
  478. },
  479. ]);
  480. await PageRedirect.insertMany([
  481. {
  482. fromPath: `/from${v5PageForDeleteCompletely2.path}`,
  483. toPath: v5PageForDeleteCompletely2.path,
  484. },
  485. {
  486. fromPath: `/from${v5PageForDeleteCompletely4.path}`,
  487. toPath: v5PageForDeleteCompletely4.path,
  488. },
  489. ]);
  490. await ShareLink.insertMany([
  491. {
  492. relatedPage: v5PageForDeleteCompletely2._id,
  493. expiredAt: null,
  494. description: 'sharlink_v5PageForDeleteCompletely2',
  495. },
  496. {
  497. relatedPage: v5PageForDeleteCompletely4._id,
  498. expiredAt: null,
  499. description: 'sharlink_v5PageForDeleteCompletely4',
  500. },
  501. ]);
  502. });
  503. describe('Rename', () => {
  504. const renamePage = async(page, newPagePath, user, options) => {
  505. // mock return value
  506. const mockedResumableRenameDescendants = jest.spyOn(crowi.pageService, 'resumableRenameDescendants').mockReturnValue(null);
  507. const mockedCreateAndSendNotifications = jest.spyOn(crowi.pageService, 'createAndSendNotifications').mockReturnValue(null);
  508. const renamedPage = await crowi.pageService.renamePage(page, newPagePath, user, options);
  509. // retrieve the arguments passed when calling method resumableRenameDescendants inside renamePage method
  510. const argsForResumableRenameDescendants = mockedResumableRenameDescendants.mock.calls[0];
  511. // restores the original implementation
  512. mockedResumableRenameDescendants.mockRestore();
  513. mockedCreateAndSendNotifications.mockRestore();
  514. // rename descendants
  515. await crowi.pageService.resumableRenameDescendants(...argsForResumableRenameDescendants);
  516. return renamedPage;
  517. };
  518. test('Should NOT rename top page', async() => {
  519. let isThrown = false;
  520. try {
  521. await crowi.pageService.renamePage(rootPage, '/new_root', dummyUser1, {});
  522. }
  523. catch (err) {
  524. isThrown = true;
  525. }
  526. expect(isThrown).toBe(true);
  527. });
  528. test('Should rename/move to under non-empty page', async() => {
  529. // rename target page
  530. const newPath = '/v5_ParentForRename1/renamedChildForRename1';
  531. const renamedPage = await renamePage(childForRename1, newPath, dummyUser1, {});
  532. expect(xssSpy).toHaveBeenCalled();
  533. expect(renamedPage.path).toBe(newPath);
  534. expect(renamedPage.parent).toStrictEqual(parentForRename1._id);
  535. });
  536. test('Should rename/move to under empty page', async() => {
  537. // rename target page
  538. const newPath = '/v5_ParentForRename2/renamedChildForRename2';
  539. const renamedPage = await renamePage(childForRename2, newPath, dummyUser1, {});
  540. expect(xssSpy).toHaveBeenCalled();
  541. expect(renamedPage.path).toBe(newPath);
  542. expect(parentForRename2.isEmpty).toBe(true);
  543. expect(renamedPage.parent).toStrictEqual(parentForRename2._id);
  544. });
  545. test('Should rename/move with option updateMetadata: true', async() => {
  546. // rename target page
  547. const newPath = '/v5_ParentForRename3/renamedChildForRename3';
  548. const oldUdpateAt = childForRename3.updatedAt;
  549. const renamedPage = await renamePage(childForRename3, newPath, dummyUser2, { updateMetadata: true });
  550. expect(xssSpy).toHaveBeenCalled();
  551. expect(renamedPage.path).toBe(newPath);
  552. expect(renamedPage.parent).toStrictEqual(parentForRename3._id);
  553. expect(renamedPage.lastUpdateUser).toStrictEqual(dummyUser2._id);
  554. expect(renamedPage.updatedAt.getFullYear()).toBeGreaterThan(oldUdpateAt.getFullYear());
  555. });
  556. // ****************** TODO ******************
  557. // uncomment the next test when working on 88097
  558. // ******************************************
  559. // test('Should move with option createRedirectPage: true', async() => {
  560. // // rename target page
  561. // const newPath = '/v5_ParentForRename4/renamedChildForRename4';
  562. // const renamedPage = await renamePage(childForRename4, newPath, dummyUser2, { createRedirectPage: true });
  563. // const pageRedirect = await PageRedirect.find({ fromPath: childForRename4.path, toPath: renamedPage.path });
  564. // expect(xssSpy).toHaveBeenCalled();
  565. // expect(renamedPage.path).toBe(newPath);
  566. // expect(renamedPage.parent).toStrictEqual(parentForRename4._id);
  567. // expect(pageRedirect.length).toBeGreaterThan(0);
  568. // });
  569. test('Should rename/move with descendants', async() => {
  570. // rename target page
  571. const newPath = '/v5_ParentForRename5/renamedChildForRename5';
  572. const renamedPage = await renamePage(childForRename5, newPath, dummyUser1, {});
  573. // find child of renamed page
  574. const grandchildren = await Page.find({ parent: renamedPage._id });
  575. const grandchild = grandchildren[0];
  576. expect(xssSpy).toHaveBeenCalled();
  577. expect(renamedPage.path).toBe(newPath);
  578. expect(renamedPage.parent).toStrictEqual(parentForRename5._id);
  579. // grandchild's parent should be renamed page
  580. expect(grandchild.parent).toStrictEqual(renamedPage._id);
  581. expect(grandchild.path).toBe('/v5_ParentForRename5/renamedChildForRename5/v5_GrandchildForRename5');
  582. });
  583. test('Should rename/move with same grant', async() => {
  584. // rename target page
  585. const newPath = '/v5_ParentForRename6/renamedChildForRename6';
  586. expect(childForRename6.grant).toBe(Page.GRANT_RESTRICTED);
  587. const renamedPage = await renamePage(childForRename6, newPath, dummyUser1, {});
  588. expect(xssSpy).toHaveBeenCalled();
  589. expect(renamedPage.path).toBe(newPath);
  590. expect(renamedPage.parent).toStrictEqual(parentForRename6._id);
  591. expect(renamedPage.grant).toBe(Page.GRANT_RESTRICTED);
  592. });
  593. test('Should rename/move empty page', async() => {
  594. // rename target page
  595. const newPath = '/v5_ParentForRename7/renamedChildForRename7';
  596. const renamedPage = await renamePage(childForRename7, newPath, dummyUser1, {});
  597. // find child of renamed page
  598. const grandchild = await Page.findOne({ parent: renamedPage._id });
  599. expect(xssSpy).toHaveBeenCalled();
  600. expect(renamedPage.path).toBe(newPath);
  601. expect(renamedPage.isEmpty).toBe(true);
  602. expect(renamedPage.parent).toStrictEqual(parentForRename7._id);
  603. // grandchild's parent should be renamed page
  604. expect(grandchild.parent).toStrictEqual(renamedPage._id);
  605. expect(grandchild.path).toBe('/v5_ParentForRename7/renamedChildForRename7/v5_GrandchildForRename7');
  606. });
  607. test('Should NOT rename/move with existing path', async() => {
  608. // rename target page
  609. const newPath = '/v5_ParentForRename9';
  610. let isThrown;
  611. try {
  612. await renamePage(parentForRename8, newPath, dummyUser1, {});
  613. }
  614. catch (err) {
  615. isThrown = true;
  616. }
  617. expect(isThrown).toBe(true);
  618. });
  619. });
  620. describe('Delete', () => {
  621. const deletePage = async(page, user, options, isRecursively) => {
  622. const mockedResumableDeleteDescendants = jest.spyOn(crowi.pageService, 'resumableDeleteDescendants').mockReturnValue(null);
  623. const mockedCreateAndSendNotifications = jest.spyOn(crowi.pageService, 'createAndSendNotifications').mockReturnValue(null);
  624. const deletedPage = await crowi.pageService.deletePage(page, user, options, isRecursively);
  625. const argsForResumableDeleteDescendants = mockedResumableDeleteDescendants.mock.calls[0];
  626. mockedResumableDeleteDescendants.mockRestore();
  627. mockedCreateAndSendNotifications.mockRestore();
  628. if (isRecursively) {
  629. await crowi.pageService.resumableDeleteDescendants(...argsForResumableDeleteDescendants);
  630. }
  631. return deletedPage;
  632. };
  633. test('Should NOT delete root page', async() => {
  634. let isThrown;
  635. try {
  636. await deletePage(rootPage, dummyUser1, {}, false);
  637. }
  638. catch (err) {
  639. isThrown = true;
  640. }
  641. expect(isThrown).toBe(true);
  642. });
  643. test('Should NOT delete trashed page', async() => {
  644. let isThrown;
  645. try {
  646. await deletePage(v5PageForDelete1, dummyUser1, {}, false);
  647. }
  648. catch (err) {
  649. isThrown = true;
  650. }
  651. expect(isThrown).toBe(true);
  652. });
  653. test('Should NOT delete /user/hoge page', async() => {
  654. let isThrown;
  655. try {
  656. await deletePage(dummyUser1Page, dummyUser1, {}, false);
  657. }
  658. catch (err) {
  659. isThrown = true;
  660. }
  661. expect(isThrown).toBe(true);
  662. });
  663. test('Should delete single page', async() => {
  664. const oldPath = v5PageForDelete2.path;
  665. const deletedPage = await deletePage(v5PageForDelete2, dummyUser1, {}, false);
  666. expect(deletedPage.path).toBe(`/trash${oldPath}`);
  667. expect(deletedPage.parent).toBeNull();
  668. expect(deletedPage.status).toBe(Page.STATUS_DELETED);
  669. });
  670. test('Should delete multiple pages including empty child', async() => {
  671. const deletedPage = await deletePage(v5PageForDelete3, dummyUser1, {}, true);
  672. const deletedV5PageForDelete4 = await Page.findOne({ path: `/trash${v5PageForDelete4.path}` });
  673. const deletedV5PageForDelete5 = await Page.findOne({ path: `/trash${v5PageForDelete5.path}` });
  674. // originally NOT empty page should exist with status 'deleted' and parent set null
  675. expect(deletedPage._id).toStrictEqual(v5PageForDelete3._id);
  676. expect(deletedPage.status).toBe(Page.STATUS_DELETED);
  677. expect(deletedPage.parent).toBeNull();
  678. // originally empty page should NOT exist
  679. expect(deletedV5PageForDelete4).toBeNull();
  680. // originally NOT empty page should exist with status 'deleted' and parent set null
  681. expect(deletedV5PageForDelete5._id).toStrictEqual(v5PageForDelete5._id);
  682. expect(deletedV5PageForDelete5.status).toBe(Page.STATUS_DELETED);
  683. expect(deletedV5PageForDelete5.parent).toBeNull();
  684. });
  685. test('Should delete page tags', async() => {
  686. const deletedPage = await deletePage(v5PageForDelete6, dummyUser1, {}, false);
  687. const deletedTag1 = await PageTagRelation.findOne({ relatedpage: deletedPage._id, relatedTag: tagForDelete1 });
  688. const deletedTag2 = await PageTagRelation.findOne({ relatedpage: deletedPage._id, relatedTag: tagForDelete2 });
  689. expect(deletedPage.status).toBe(Page.STATUS_DELETED);
  690. expect(deletedTag1.isPageTrashed).toBe(true);
  691. expect(deletedTag2.isPageTrashed).toBe(true);
  692. });
  693. });
  694. describe('Delete completely', () => {
  695. const deleteCompletely = async(page, user, options = {}, isRecursively = false, preventEmitting = false) => {
  696. const mockedResumableDeleteCompletelyDescendants = jest.spyOn(crowi.pageService, 'resumableDeleteCompletelyDescendants').mockReturnValue(null);
  697. const mockedCreateAndSendNotifications = jest.spyOn(crowi.pageService, 'createAndSendNotifications').mockReturnValue(null);
  698. await crowi.pageService.deleteCompletely(page, user, options, isRecursively, preventEmitting);
  699. const argsForResumableDeleteDescendants = mockedResumableDeleteCompletelyDescendants.mock.calls[0];
  700. mockedResumableDeleteCompletelyDescendants.mockRestore();
  701. mockedCreateAndSendNotifications.mockRestore();
  702. if (isRecursively) {
  703. await crowi.pageService.resumableDeleteCompletelyDescendants(...argsForResumableDeleteDescendants);
  704. }
  705. return;
  706. };
  707. test('Should NOT completely delete root page', async() => {
  708. let isThrown;
  709. try {
  710. await deleteCompletely(rootPage, dummyUser1, {}, false);
  711. }
  712. catch (err) {
  713. isThrown = true;
  714. }
  715. expect(isThrown).toBe(true);
  716. });
  717. test('Should completely delete single page', async() => {
  718. await deleteCompletely(v5PageForDeleteCompletely1, dummyUser1, {}, false);
  719. const deletedPage = await Page.findOne({ _id: v5PageForDeleteCompletely1._id });
  720. expect(deletedPage).toBeNull();
  721. });
  722. test('Should completely delete multiple pages', async() => {
  723. await deleteCompletely(v5PageForDeleteCompletely2, dummyUser1, {}, true);
  724. const deletedPages = await Page.find({ _id: { $in: [v5PageForDeleteCompletely2._id, v5PageForDeleteCompletely3._id, v5PageForDeleteCompletely4._id] } });
  725. const deletedRevisions = await Revision.find({ pageId: { $in: [v5PageForDeleteCompletely2._id, v5PageForDeleteCompletely4._id] } });
  726. const tags = await Tag.find({ name: { $in: [tagForDeleteCompletely1.name, tagForDeleteCompletely2.name] } });
  727. const deletedPageTagRelations = await PageTagRelation.find({ relatedPage: { $in: [v5PageForDeleteCompletely2._id, v5PageForDeleteCompletely4._id] } });
  728. const deletedBookmarks = await Bookmark.find({ page: v5PageForDeleteCompletely2._id });
  729. const deletedComments = await Comment.find({ page: v5PageForDeleteCompletely2._id });
  730. const deletedPageRedirects = await PageRedirect.find({ toPath: { $in: [v5PageForDeleteCompletely2.toPath, v5PageForDeleteCompletely4.path] } });
  731. const deletedShareLinks = await ShareLink.find({ pageId: { $in: [v5PageForDeleteCompletely2._id, v5PageForDeleteCompletely4._id] } });
  732. // page should be null
  733. deletedPages.forEach((deletedPage) => {
  734. expect(deletedPage).toBeNull();
  735. });
  736. // revision should be null
  737. deletedRevisions.forEach((revision) => {
  738. expect(revision).toBeNull();
  739. });
  740. // tag should exist
  741. tags.forEach((tag) => {
  742. expect(tag).toBeTruthy();
  743. });
  744. // pageTagRelation should be null
  745. deletedPageTagRelations.forEach((PTRelation) => {
  746. expect(PTRelation).toBeNull();
  747. });
  748. // bookmark should be null
  749. deletedBookmarks.forEach((bookmark) => {
  750. expect(bookmark).toBeNull();
  751. });
  752. // comment should be null
  753. deletedComments.forEach((comment) => {
  754. expect(comment).toBeNull();
  755. });
  756. // pageRedirect should be null
  757. deletedPageRedirects.forEach((pRedirect) => {
  758. expect(pRedirect).toBeNull();
  759. });
  760. // sharelink should be null
  761. deletedShareLinks.forEach((sharelnk) => {
  762. expect(sharelnk).toBeNull();
  763. });
  764. });
  765. test('Should completely delete trashed page', async() => {
  766. await deleteCompletely(v5PageForDeleteCompletely5, dummyUser1, {}, false);
  767. const deltedPage = await Page.findOne({ _id: v5PageForDeleteCompletely5._id });
  768. const deltedRevision = await Revision.findOne({ pageId: v5PageForDeleteCompletely5._id });
  769. expect(deltedPage).toBeNull();
  770. expect(deltedRevision).toBeNull();
  771. });
  772. test('Should completely deleting page in the middle results in empty page', async() => {
  773. await deleteCompletely(v5PageForDeleteCompletely7, dummyUser1, {}, false);
  774. const deletedPage = await Page.findOne({ path: v5PageForDeleteCompletely7.path });
  775. const childOfDeletedPage = await Page.findOne({ parent: deletedPage._id });
  776. expect(deletedPage).toBeTruthy();
  777. expect(deletedPage._id).not.toStrictEqual(v5PageForDeleteCompletely7._id);
  778. expect(deletedPage.isEmpty).toBe(true);
  779. expect(deletedPage.parent).toStrictEqual(v5PageForDeleteCompletely6._id);
  780. expect(childOfDeletedPage._id).toStrictEqual(v5PageForDeleteCompletely8._id);
  781. });
  782. });
  783. afterAll(async() => {
  784. await Page.deleteMany({});
  785. await User.deleteMany({});
  786. await Bookmark.deleteMany({});
  787. await Comment.deleteMany({});
  788. await ShareLink.deleteMany({});
  789. });
  790. });
  791. describe('PageService page operations with non-public pages', () => {
  792. // TODO: write test code
  793. });