v5.page.test.ts 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462
  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. /* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expectAllToBeTruthy"] }] */
  21. // https://github.com/jest-community/eslint-plugin-jest/blob/v24.3.5/docs/rules/expect-expect.md#assertfunctionnames
  22. // pass unless the data is one of [false, 0, '', null, undefined, NaN]
  23. const expectAllToBeTruthy = (dataList) => {
  24. dataList.forEach((data) => {
  25. expect(data).toBeTruthy();
  26. });
  27. };
  28. beforeAll(async() => {
  29. crowi = await getInstance();
  30. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', { 'app:isV5Compatible': true });
  31. User = mongoose.model('User');
  32. Page = mongoose.model('Page');
  33. Revision = mongoose.model('Revision');
  34. Tag = mongoose.model('Tag');
  35. PageTagRelation = mongoose.model('PageTagRelation');
  36. Bookmark = mongoose.model('Bookmark');
  37. Comment = mongoose.model('Comment');
  38. ShareLink = mongoose.model('ShareLink');
  39. PageRedirect = mongoose.model('PageRedirect');
  40. /*
  41. * Common
  42. */
  43. dummyUser1 = await User.findOne({ username: 'v5DummyUser1' });
  44. dummyUser2 = await User.findOne({ username: 'v5DummyUser2' });
  45. xssSpy = jest.spyOn(crowi.xss, 'process').mockImplementation(path => path);
  46. rootPage = await Page.findOne({ path: '/' });
  47. if (rootPage == null) {
  48. const pages = await Page.insertMany([{ path: '/', grant: Page.GRANT_PUBLIC }]);
  49. rootPage = pages[0];
  50. }
  51. /*
  52. * Rename
  53. */
  54. const pageIdForRename1 = new mongoose.Types.ObjectId();
  55. const pageIdForRename2 = new mongoose.Types.ObjectId();
  56. const pageIdForRename3 = new mongoose.Types.ObjectId();
  57. const pageIdForRename4 = new mongoose.Types.ObjectId();
  58. const pageIdForRename5 = new mongoose.Types.ObjectId();
  59. const pageIdForRename7 = new mongoose.Types.ObjectId();
  60. const pageIdForRename8 = new mongoose.Types.ObjectId();
  61. const pageIdForRename9 = new mongoose.Types.ObjectId();
  62. const pageIdForRename10 = new mongoose.Types.ObjectId();
  63. const pageIdForRename11 = new mongoose.Types.ObjectId();
  64. const pageIdForRename12 = new mongoose.Types.ObjectId();
  65. const pageIdForRename13 = new mongoose.Types.ObjectId();
  66. const pageIdForRename14 = new mongoose.Types.ObjectId();
  67. const pageIdForRename16 = new mongoose.Types.ObjectId();
  68. // Create Pages
  69. await Page.insertMany([
  70. // parents
  71. {
  72. _id: pageIdForRename1,
  73. path: '/v5_ParentForRename1',
  74. grant: Page.GRANT_PUBLIC,
  75. creator: dummyUser1,
  76. lastUpdateUser: dummyUser1._id,
  77. parent: rootPage._id,
  78. },
  79. {
  80. _id: pageIdForRename2,
  81. path: '/v5_ParentForRename2',
  82. grant: Page.GRANT_PUBLIC,
  83. parent: rootPage._id,
  84. isEmpty: true,
  85. },
  86. {
  87. // id not needed for this data
  88. path: '/v5_ParentForRename2/dummyChild1',
  89. grant: Page.GRANT_PUBLIC,
  90. creator: dummyUser1,
  91. lastUpdateUser: dummyUser1._id,
  92. parent: pageIdForRename2,
  93. },
  94. {
  95. _id: pageIdForRename3,
  96. path: '/v5_ParentForRename3',
  97. grant: Page.GRANT_PUBLIC,
  98. creator: dummyUser1,
  99. lastUpdateUser: dummyUser1._id,
  100. parent: rootPage._id,
  101. },
  102. {
  103. _id: pageIdForRename4,
  104. path: '/v5_ParentForRename4',
  105. grant: Page.GRANT_PUBLIC,
  106. creator: dummyUser1,
  107. lastUpdateUser: dummyUser1._id,
  108. parent: rootPage._id,
  109. },
  110. {
  111. _id: pageIdForRename5,
  112. path: '/v5_ParentForRename5',
  113. grant: Page.GRANT_PUBLIC,
  114. creator: dummyUser1,
  115. lastUpdateUser: dummyUser1._id,
  116. parent: rootPage._id,
  117. },
  118. {
  119. _id: pageIdForRename7,
  120. path: '/v5_ParentForRename7',
  121. grant: Page.GRANT_PUBLIC,
  122. creator: dummyUser1,
  123. lastUpdateUser: dummyUser1._id,
  124. parent: rootPage._id,
  125. },
  126. {
  127. _id: pageIdForRename8,
  128. path: '/v5_ParentForRename8',
  129. grant: Page.GRANT_PUBLIC,
  130. creator: dummyUser1,
  131. lastUpdateUser: dummyUser1._id,
  132. parent: rootPage._id,
  133. },
  134. {
  135. _id: pageIdForRename9,
  136. path: '/v5_ParentForRename9',
  137. grant: Page.GRANT_PUBLIC,
  138. creator: dummyUser1,
  139. lastUpdateUser: dummyUser1._id,
  140. parent: rootPage._id,
  141. },
  142. // children
  143. {
  144. _id: pageIdForRename10,
  145. path: '/v5_ChildForRename1',
  146. grant: Page.GRANT_PUBLIC,
  147. creator: dummyUser1,
  148. lastUpdateUser: dummyUser1._id,
  149. parent: rootPage._id,
  150. },
  151. {
  152. _id: pageIdForRename11,
  153. path: '/v5_ChildForRename2',
  154. grant: Page.GRANT_PUBLIC,
  155. creator: dummyUser1,
  156. lastUpdateUser: dummyUser1._id,
  157. parent: rootPage._id,
  158. },
  159. {
  160. _id: pageIdForRename12,
  161. path: '/v5_ChildForRename3',
  162. grant: Page.GRANT_PUBLIC,
  163. creator: dummyUser1,
  164. lastUpdateUser: dummyUser1._id,
  165. parent: rootPage._id,
  166. updatedAt: new Date('2021'),
  167. },
  168. {
  169. _id: pageIdForRename13,
  170. path: '/v5_ChildForRename4',
  171. grant: Page.GRANT_PUBLIC,
  172. creator: dummyUser1,
  173. lastUpdateUser: dummyUser1._id,
  174. parent: rootPage._id,
  175. },
  176. {
  177. _id: pageIdForRename14,
  178. path: '/v5_ChildForRename5',
  179. grant: Page.GRANT_PUBLIC,
  180. creator: dummyUser1,
  181. lastUpdateUser: dummyUser1._id,
  182. parent: rootPage._id,
  183. },
  184. {
  185. _id: pageIdForRename16,
  186. path: '/v5_ChildForRename7',
  187. grant: Page.GRANT_PUBLIC,
  188. parent: rootPage._id,
  189. isEmpty: true,
  190. },
  191. // Grandchild
  192. {
  193. path: '/v5_ChildForRename5/v5_GrandchildForRename5',
  194. grant: Page.GRANT_PUBLIC,
  195. creator: dummyUser1,
  196. lastUpdateUser: dummyUser1._id,
  197. parent: pageIdForRename14,
  198. updatedAt: new Date('2021'),
  199. },
  200. {
  201. path: '/v5_ChildForRename7/v5_GrandchildForRename7',
  202. grant: Page.GRANT_PUBLIC,
  203. creator: dummyUser1,
  204. lastUpdateUser: dummyUser1._id,
  205. parent: pageIdForRename16,
  206. },
  207. ]);
  208. /*
  209. * Duplicate
  210. */
  211. // page ids
  212. const pageIdForDuplicate1 = new mongoose.Types.ObjectId();
  213. const pageIdForDuplicate2 = new mongoose.Types.ObjectId();
  214. const pageIdForDuplicate3 = new mongoose.Types.ObjectId();
  215. const pageIdForDuplicate4 = new mongoose.Types.ObjectId();
  216. const pageIdForDuplicate5 = new mongoose.Types.ObjectId();
  217. const pageIdForDuplicate6 = new mongoose.Types.ObjectId();
  218. const pageIdForDuplicate7 = new mongoose.Types.ObjectId();
  219. const pageIdForDuplicate8 = new mongoose.Types.ObjectId();
  220. const pageIdForDuplicate9 = new mongoose.Types.ObjectId();
  221. const pageIdForDuplicate10 = new mongoose.Types.ObjectId();
  222. const pageIdForDuplicate11 = new mongoose.Types.ObjectId();
  223. const pageIdForDuplicate12 = new mongoose.Types.ObjectId();
  224. const pageIdForDuplicate13 = new mongoose.Types.ObjectId();
  225. const pageIdForDuplicate14 = new mongoose.Types.ObjectId();
  226. const pageIdForDuplicate15 = new mongoose.Types.ObjectId();
  227. // revision ids
  228. const revisionIdForDuplicate1 = new mongoose.Types.ObjectId();
  229. const revisionIdForDuplicate2 = new mongoose.Types.ObjectId();
  230. const revisionIdForDuplicate3 = new mongoose.Types.ObjectId();
  231. const revisionIdForDuplicate4 = new mongoose.Types.ObjectId();
  232. const revisionIdForDuplicate5 = new mongoose.Types.ObjectId();
  233. const revisionIdForDuplicate6 = new mongoose.Types.ObjectId();
  234. const revisionIdForDuplicate7 = new mongoose.Types.ObjectId();
  235. const revisionIdForDuplicate8 = new mongoose.Types.ObjectId();
  236. const revisionIdForDuplicate9 = new mongoose.Types.ObjectId();
  237. const revisionIdForDuplicate10 = new mongoose.Types.ObjectId();
  238. const revisionIdForDuplicate11 = new mongoose.Types.ObjectId();
  239. const revisionIdForDuplicate12 = new mongoose.Types.ObjectId();
  240. await Page.insertMany([
  241. {
  242. _id: pageIdForDuplicate1,
  243. path: '/v5_PageForDuplicate1',
  244. grant: Page.GRANT_PUBLIC,
  245. creator: dummyUser1,
  246. lastUpdateUser: dummyUser1._id,
  247. parent: rootPage._id,
  248. revision: revisionIdForDuplicate1,
  249. },
  250. {
  251. _id: pageIdForDuplicate2,
  252. path: '/v5_PageForDuplicate2',
  253. grant: Page.GRANT_PUBLIC,
  254. parent: rootPage._id,
  255. isEmpty: true,
  256. },
  257. {
  258. _id: pageIdForDuplicate3,
  259. path: '/v5_PageForDuplicate2/v5_ChildForDuplicate2',
  260. grant: Page.GRANT_PUBLIC,
  261. creator: dummyUser1,
  262. lastUpdateUser: dummyUser1._id,
  263. parent: pageIdForDuplicate2,
  264. revision: revisionIdForDuplicate2,
  265. },
  266. {
  267. _id: pageIdForDuplicate4,
  268. path: '/v5_PageForDuplicate3',
  269. grant: Page.GRANT_PUBLIC,
  270. creator: dummyUser1,
  271. lastUpdateUser: dummyUser1._id,
  272. parent: rootPage._id,
  273. revision: revisionIdForDuplicate3,
  274. },
  275. {
  276. _id: pageIdForDuplicate5,
  277. path: '/v5_PageForDuplicate3/v5_Child_1_ForDuplicate3',
  278. grant: Page.GRANT_PUBLIC,
  279. creator: dummyUser1,
  280. lastUpdateUser: dummyUser1._id,
  281. parent: pageIdForDuplicate4,
  282. revision: revisionIdForDuplicate4,
  283. },
  284. {
  285. _id: pageIdForDuplicate6,
  286. path: '/v5_PageForDuplicate3/v5_Child_2_ForDuplicate3',
  287. grant: Page.GRANT_PUBLIC,
  288. creator: dummyUser1,
  289. lastUpdateUser: dummyUser1._id,
  290. parent: pageIdForDuplicate4,
  291. revision: revisionIdForDuplicate5,
  292. },
  293. {
  294. _id: pageIdForDuplicate7,
  295. path: '/v5_PageForDuplicate4',
  296. grant: Page.GRANT_PUBLIC,
  297. creator: dummyUser1,
  298. lastUpdateUser: dummyUser1._id,
  299. parent: rootPage._id,
  300. revision: revisionIdForDuplicate6,
  301. },
  302. {
  303. _id: pageIdForDuplicate8,
  304. path: '/v5_PageForDuplicate4/v5_empty_PageForDuplicate4',
  305. grant: Page.GRANT_PUBLIC,
  306. parent: pageIdForDuplicate7,
  307. isEmpty: true,
  308. },
  309. {
  310. _id: pageIdForDuplicate9,
  311. path: '/v5_PageForDuplicate4/v5_empty_PageForDuplicate4/v5_grandchild_PageForDuplicate4',
  312. grant: Page.GRANT_PUBLIC,
  313. creator: dummyUser1,
  314. lastUpdateUser: dummyUser1._id,
  315. parent: pageIdForDuplicate8,
  316. revision: revisionIdForDuplicate7,
  317. },
  318. {
  319. _id: pageIdForDuplicate10,
  320. path: '/v5_PageForDuplicate5',
  321. grant: Page.GRANT_PUBLIC,
  322. creator: dummyUser1,
  323. lastUpdateUser: dummyUser1._id,
  324. parent: rootPage._id,
  325. revision: revisionIdForDuplicate8,
  326. },
  327. {
  328. _id: pageIdForDuplicate11,
  329. path: '/v5_PageForDuplicate6',
  330. grant: Page.GRANT_PUBLIC,
  331. creator: dummyUser1,
  332. lastUpdateUser: dummyUser1._id,
  333. parent: rootPage._id,
  334. revision: revisionIdForDuplicate9,
  335. },
  336. {
  337. _id: pageIdForDuplicate13,
  338. path: '/v5_empty_PageForDuplicate7',
  339. grant: Page.GRANT_PUBLIC,
  340. parent: rootPage._id,
  341. isEmpty: true,
  342. },
  343. {
  344. _id: pageIdForDuplicate14,
  345. path: '/v5_empty_PageForDuplicate7/v5_child_PageForDuplicate7',
  346. grant: Page.GRANT_PUBLIC,
  347. creator: dummyUser1,
  348. lastUpdateUser: dummyUser1._id,
  349. parent: pageIdForDuplicate13,
  350. revision: revisionIdForDuplicate11,
  351. },
  352. {
  353. _id: pageIdForDuplicate15,
  354. path: '/v5_empty_PageForDuplicate7/v5_child_PageForDuplicate7/v5_grandchild_PageForDuplicate7',
  355. grant: Page.GRANT_PUBLIC,
  356. creator: dummyUser1,
  357. lastUpdateUser: dummyUser1._id,
  358. parent: pageIdForDuplicate14,
  359. revision: revisionIdForDuplicate12,
  360. },
  361. ]);
  362. await Revision.insertMany([
  363. {
  364. _id: revisionIdForDuplicate1,
  365. body: 'body1',
  366. format: 'markdown',
  367. pageId: pageIdForDuplicate1,
  368. author: dummyUser1,
  369. },
  370. {
  371. _id: revisionIdForDuplicate2,
  372. body: 'body3',
  373. format: 'markdown',
  374. pageId: pageIdForDuplicate3,
  375. author: dummyUser1,
  376. },
  377. {
  378. _id: revisionIdForDuplicate3,
  379. body: 'parent_page_body4',
  380. format: 'markdown',
  381. pageId: pageIdForDuplicate4,
  382. author: dummyUser1,
  383. },
  384. {
  385. _id: revisionIdForDuplicate4,
  386. body: 'revision_id_4_child_page_body',
  387. format: 'markdown',
  388. pageId: pageIdForDuplicate5,
  389. author: dummyUser1,
  390. },
  391. {
  392. _id: revisionIdForDuplicate5,
  393. body: 'revision_id_5_child_page_body',
  394. format: 'markdown',
  395. pageId: pageIdForDuplicate6,
  396. author: dummyUser1,
  397. },
  398. {
  399. _id: revisionIdForDuplicate6,
  400. body: '/v5_PageForDuplicate4',
  401. format: 'markdown',
  402. pageId: pageIdForDuplicate7,
  403. author: dummyUser1,
  404. },
  405. {
  406. _id: revisionIdForDuplicate7,
  407. body: '/v5_PageForDuplicate4/v5_empty_PageForDuplicate4/v5_grandchild_PageForDuplicate4',
  408. format: 'markdown',
  409. pageId: pageIdForDuplicate9,
  410. author: dummyUser1,
  411. },
  412. {
  413. _id: revisionIdForDuplicate8,
  414. body: '/v5_PageForDuplicate5',
  415. format: 'markdown',
  416. pageId: pageIdForDuplicate10,
  417. author: dummyUser1,
  418. },
  419. {
  420. _id: revisionIdForDuplicate9,
  421. body: '/v5_PageForDuplicate6',
  422. format: 'markdown',
  423. pageId: pageIdForDuplicate11,
  424. author: dummyUser1,
  425. },
  426. {
  427. _id: revisionIdForDuplicate10,
  428. body: '/v5_PageForDuplicate6',
  429. format: 'comment',
  430. pageId: pageIdForDuplicate12,
  431. author: dummyUser1,
  432. },
  433. {
  434. _id: revisionIdForDuplicate11,
  435. body: '/v5_child_PageForDuplicate7',
  436. format: 'markdown',
  437. pageId: pageIdForDuplicate14,
  438. author: dummyUser1,
  439. },
  440. {
  441. _id: revisionIdForDuplicate12,
  442. body: '/v5_grandchild_PageForDuplicate7',
  443. format: 'markdown',
  444. pageId: pageIdForDuplicate15,
  445. author: dummyUser1,
  446. },
  447. ]);
  448. const tagForDuplicate1 = new mongoose.Types.ObjectId();
  449. const tagForDuplicate2 = new mongoose.Types.ObjectId();
  450. await Tag.insertMany([
  451. { _id: tagForDuplicate1, name: 'duplicate_Tag1' },
  452. { _id: tagForDuplicate2, name: 'duplicate_Tag2' },
  453. ]);
  454. await PageTagRelation.insertMany([
  455. { relatedPage: pageIdForDuplicate10, relatedTag: tagForDuplicate1 },
  456. { relatedPage: pageIdForDuplicate10._id, relatedTag: tagForDuplicate2 },
  457. ]);
  458. await Comment.insertMany([
  459. {
  460. commentPosition: -1,
  461. isMarkdown: true,
  462. page: pageIdForDuplicate11,
  463. creator: dummyUser1._id,
  464. revision: revisionIdForDuplicate10,
  465. comment: 'this is comment',
  466. },
  467. ]);
  468. /**
  469. * Delete
  470. */
  471. const pageIdForDelete1 = new mongoose.Types.ObjectId();
  472. const pageIdForDelete2 = new mongoose.Types.ObjectId();
  473. const pageIdForDelete3 = new mongoose.Types.ObjectId();
  474. const pageIdForDelete4 = new mongoose.Types.ObjectId();
  475. const pageIdForDelete5 = new mongoose.Types.ObjectId();
  476. await Page.insertMany([
  477. {
  478. path: '/trash/v5_PageForDelete1',
  479. grant: Page.GRANT_PUBLIC,
  480. creator: dummyUser1,
  481. lastUpdateUser: dummyUser1._id,
  482. status: Page.STATUS_DELETED,
  483. },
  484. {
  485. path: '/v5_PageForDelete2',
  486. grant: Page.GRANT_PUBLIC,
  487. creator: dummyUser1,
  488. lastUpdateUser: dummyUser1._id,
  489. parent: rootPage._id,
  490. status: Page.STATUS_PUBLISHED,
  491. },
  492. {
  493. _id: pageIdForDelete1,
  494. path: '/v5_PageForDelete3',
  495. grant: Page.GRANT_PUBLIC,
  496. creator: dummyUser1,
  497. lastUpdateUser: dummyUser1._id,
  498. parent: rootPage._id,
  499. status: Page.STATUS_PUBLISHED,
  500. },
  501. {
  502. _id: pageIdForDelete2,
  503. path: '/v5_PageForDelete3/v5_PageForDelete4',
  504. grant: Page.GRANT_PUBLIC,
  505. parent: pageIdForDelete1,
  506. status: Page.STATUS_PUBLISHED,
  507. isEmpty: true,
  508. },
  509. {
  510. path: '/v5_PageForDelete3/v5_PageForDelete4/v5_PageForDelete5',
  511. grant: Page.GRANT_PUBLIC,
  512. creator: dummyUser1,
  513. lastUpdateUser: dummyUser1._id,
  514. parent: pageIdForDelete2,
  515. status: Page.STATUS_PUBLISHED,
  516. },
  517. {
  518. _id: pageIdForDelete3,
  519. path: '/v5_PageForDelete6',
  520. grant: Page.GRANT_PUBLIC,
  521. creator: dummyUser1,
  522. lastUpdateUser: dummyUser1._id,
  523. parent: rootPage._id,
  524. status: Page.STATUS_PUBLISHED,
  525. },
  526. {
  527. _id: pageIdForDelete4,
  528. path: '/user',
  529. grant: Page.GRANT_PUBLIC,
  530. parent: rootPage._id,
  531. status: Page.STATUS_PUBLISHED,
  532. isEmpty: true,
  533. },
  534. {
  535. _id: pageIdForDelete5,
  536. path: '/user/v5DummyUser1',
  537. grant: Page.GRANT_PUBLIC,
  538. creator: dummyUser1,
  539. lastUpdateUser: dummyUser1._id,
  540. parent: pageIdForDelete4,
  541. status: Page.STATUS_PUBLISHED,
  542. },
  543. ]);
  544. const tagIdForDelete1 = new mongoose.Types.ObjectId();
  545. const tagIdForDelete2 = new mongoose.Types.ObjectId();
  546. await Tag.insertMany([
  547. { _id: tagIdForDelete1, name: 'TagForDelete1' },
  548. { _id: tagIdForDelete2, name: 'TagForDelete2' },
  549. ]);
  550. await PageTagRelation.insertMany([
  551. { relatedPage: pageIdForDelete3, relatedTag: tagIdForDelete1 },
  552. { relatedPage: pageIdForDelete3, relatedTag: tagIdForDelete2 },
  553. ]);
  554. /**
  555. * Delete completely
  556. */
  557. const pageIdForDeleteCompletely1 = new mongoose.Types.ObjectId();
  558. const pageIdForDeleteCompletely2 = new mongoose.Types.ObjectId();
  559. const pageIdForDeleteCompletely3 = new mongoose.Types.ObjectId();
  560. const pageIdForDeleteCompletely4 = new mongoose.Types.ObjectId();
  561. const pageIdForDeleteCompletely5 = new mongoose.Types.ObjectId();
  562. const pageIdForDeleteCompletely6 = new mongoose.Types.ObjectId();
  563. const pageIdForDeleteCompletely7 = new mongoose.Types.ObjectId();
  564. const pageIdForDeleteCompletely8 = new mongoose.Types.ObjectId();
  565. const revisionIdForDeleteCompletely1 = new mongoose.Types.ObjectId();
  566. const revisionIdForDeleteCompletely2 = new mongoose.Types.ObjectId();
  567. const revisionIdForDeleteCompletely3 = new mongoose.Types.ObjectId();
  568. const revisionIdForDeleteCompletely4 = new mongoose.Types.ObjectId();
  569. await Page.insertMany([
  570. {
  571. _id: pageIdForDeleteCompletely1,
  572. path: '/v5_PageForDeleteCompletely1',
  573. grant: Page.GRANT_PUBLIC,
  574. creator: dummyUser1,
  575. lastUpdateUser: dummyUser1._id,
  576. parent: rootPage._id,
  577. status: Page.STATUS_PUBLISHED,
  578. },
  579. {
  580. _id: pageIdForDeleteCompletely2,
  581. path: '/v5_PageForDeleteCompletely2',
  582. grant: Page.GRANT_PUBLIC,
  583. creator: dummyUser1,
  584. lastUpdateUser: dummyUser1._id,
  585. parent: rootPage._id,
  586. status: Page.STATUS_PUBLISHED,
  587. },
  588. {
  589. _id: pageIdForDeleteCompletely3,
  590. path: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3',
  591. grant: Page.GRANT_PUBLIC,
  592. parent: pageIdForDeleteCompletely2,
  593. status: Page.STATUS_PUBLISHED,
  594. isEmpty: true,
  595. },
  596. {
  597. _id: pageIdForDeleteCompletely4,
  598. path: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3/v5_PageForDeleteCompletely4',
  599. grant: Page.GRANT_PUBLIC,
  600. creator: dummyUser1,
  601. lastUpdateUser: dummyUser1._id,
  602. parent: pageIdForDeleteCompletely3,
  603. status: Page.STATUS_PUBLISHED,
  604. },
  605. {
  606. _id: pageIdForDeleteCompletely5,
  607. path: '/trash/v5_PageForDeleteCompletely5',
  608. grant: Page.GRANT_PUBLIC,
  609. creator: dummyUser1,
  610. lastUpdateUser: dummyUser1._id,
  611. parent: rootPage._id,
  612. status: Page.STATUS_DELETED,
  613. },
  614. {
  615. _id: pageIdForDeleteCompletely6,
  616. path: '/v5_PageForDeleteCompletely6',
  617. grant: Page.GRANT_PUBLIC,
  618. creator: dummyUser1,
  619. lastUpdateUser: dummyUser1._id,
  620. parent: rootPage._id,
  621. status: Page.STATUS_PUBLISHED,
  622. },
  623. {
  624. _id: pageIdForDeleteCompletely7,
  625. path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7',
  626. grant: Page.GRANT_PUBLIC,
  627. creator: dummyUser1,
  628. lastUpdateUser: dummyUser1._id,
  629. parent: pageIdForDeleteCompletely6,
  630. status: Page.STATUS_PUBLISHED,
  631. },
  632. {
  633. _id: pageIdForDeleteCompletely8,
  634. path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7/v5_PageForDeleteCompletely8',
  635. grant: Page.GRANT_PUBLIC,
  636. creator: dummyUser1,
  637. lastUpdateUser: dummyUser1._id,
  638. parent: pageIdForDeleteCompletely7,
  639. status: Page.STATUS_PUBLISHED,
  640. },
  641. ]);
  642. await Revision.insertMany([
  643. {
  644. _id: revisionIdForDeleteCompletely1,
  645. format: 'markdown',
  646. pageId: pageIdForDeleteCompletely2,
  647. body: 'pageIdForDeleteCompletely2',
  648. },
  649. {
  650. _id: revisionIdForDeleteCompletely2,
  651. format: 'markdown',
  652. pageId: pageIdForDeleteCompletely4,
  653. body: 'pageIdForDeleteCompletely4',
  654. },
  655. {
  656. _id: revisionIdForDeleteCompletely3,
  657. format: 'markdown',
  658. pageId: pageIdForDeleteCompletely5,
  659. body: 'pageIdForDeleteCompletely5',
  660. },
  661. {
  662. _id: revisionIdForDeleteCompletely4,
  663. format: 'markdown',
  664. pageId: pageIdForDeleteCompletely2,
  665. body: 'comment_pageIdForDeleteCompletely3',
  666. },
  667. ]);
  668. const tagForDeleteCompletely1 = new mongoose.Types.ObjectId();
  669. const tagForDeleteCompletely2 = new mongoose.Types.ObjectId();
  670. await Tag.insertMany([
  671. { name: 'TagForDeleteCompletely1' },
  672. { name: 'TagForDeleteCompletely2' },
  673. ]);
  674. await PageTagRelation.insertMany([
  675. { relatedPage: pageIdForDeleteCompletely2, relatedTag: tagForDeleteCompletely1 },
  676. { relatedPage: pageIdForDeleteCompletely4, relatedTag: tagForDeleteCompletely2 },
  677. ]);
  678. await Bookmark.insertMany([
  679. {
  680. page: pageIdForDeleteCompletely2,
  681. user: dummyUser1._id,
  682. },
  683. {
  684. page: pageIdForDeleteCompletely2,
  685. user: dummyUser2._id,
  686. },
  687. ]);
  688. await Comment.insertMany([
  689. {
  690. commentPosition: -1,
  691. isMarkdown: true,
  692. page: pageIdForDeleteCompletely2,
  693. creator: dummyUser1._id,
  694. revision: revisionIdForDeleteCompletely4,
  695. comment: 'comment_ForDeleteCompletely4',
  696. },
  697. ]);
  698. await PageRedirect.insertMany([
  699. {
  700. fromPath: '/from/v5_PageForDeleteCompletely2',
  701. toPath: '/v5_PageForDeleteCompletely2',
  702. },
  703. {
  704. fromPath: '/from/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3/v5_PageForDeleteCompletely4',
  705. toPath: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3/v5_PageForDeleteCompletely4',
  706. },
  707. ]);
  708. await ShareLink.insertMany([
  709. {
  710. relatedPage: pageIdForDeleteCompletely2,
  711. expiredAt: null,
  712. description: 'sharlink_v5PageForDeleteCompletely2',
  713. },
  714. {
  715. relatedPage: pageIdForDeleteCompletely4,
  716. expiredAt: null,
  717. description: 'sharlink_v5PageForDeleteCompletely4',
  718. },
  719. ]);
  720. /**
  721. * Revert
  722. */
  723. const pageIdForRevert1 = new mongoose.Types.ObjectId();
  724. const pageIdForRevert2 = new mongoose.Types.ObjectId();
  725. const pageIdForRevert3 = new mongoose.Types.ObjectId();
  726. const revisionIdForRevert1 = new mongoose.Types.ObjectId();
  727. const revisionIdForRevert2 = new mongoose.Types.ObjectId();
  728. const revisionIdForRevert3 = new mongoose.Types.ObjectId();
  729. await Page.insertMany([
  730. {
  731. _id: pageIdForRevert1,
  732. path: '/trash/v5_revert1',
  733. grant: Page.GRANT_PUBLIC,
  734. creator: dummyUser1,
  735. lastUpdateUser: dummyUser1._id,
  736. revision: revisionIdForRevert1,
  737. status: Page.STATUS_DELETED,
  738. },
  739. {
  740. _id: pageIdForRevert2,
  741. path: '/trash/v5_revert2',
  742. grant: Page.GRANT_PUBLIC,
  743. creator: dummyUser1,
  744. lastUpdateUser: dummyUser1._id,
  745. revision: revisionIdForRevert2,
  746. status: Page.STATUS_DELETED,
  747. },
  748. {
  749. _id: pageIdForRevert3,
  750. path: '/trash/v5_revert2/v5_revert3/v5_revert4',
  751. grant: Page.GRANT_PUBLIC,
  752. creator: dummyUser1,
  753. lastUpdateUser: dummyUser1._id,
  754. revision: revisionIdForRevert3,
  755. status: Page.STATUS_DELETED,
  756. },
  757. ]);
  758. await Revision.insertMany([
  759. {
  760. _id: revisionIdForRevert1,
  761. pageId: pageIdForRevert1,
  762. body: 'revert1',
  763. format: 'comment',
  764. author: dummyUser1,
  765. },
  766. {
  767. _id: revisionIdForRevert2,
  768. pageId: pageIdForRevert2,
  769. body: 'revert2',
  770. format: 'comment',
  771. author: dummyUser1,
  772. },
  773. {
  774. _id: revisionIdForRevert3,
  775. pageId: pageIdForRevert3,
  776. body: 'revert3',
  777. format: 'comment',
  778. author: dummyUser1,
  779. },
  780. ]);
  781. const tagIdRevert1 = new mongoose.Types.ObjectId();
  782. await Tag.insertMany([
  783. { _id: tagIdRevert1, name: 'revertTag1' },
  784. ]);
  785. await PageTagRelation.insertMany([
  786. {
  787. relatedPage: pageIdForRevert1,
  788. relatedTag: tagIdRevert1,
  789. isPageTrashed: true,
  790. },
  791. ]);
  792. });
  793. describe('Rename', () => {
  794. const renamePage = async(page, newPagePath, user, options) => {
  795. // mock return value
  796. const mockedRenameSubOperation = jest.spyOn(crowi.pageService, 'renameSubOperation').mockReturnValue(null);
  797. const mockedCreateAndSendNotifications = jest.spyOn(crowi.pageService, 'createAndSendNotifications').mockReturnValue(null);
  798. const renamedPage = await crowi.pageService.renamePage(page, newPagePath, user, options);
  799. // retrieve the arguments passed when calling method renameSubOperation inside renamePage method
  800. const argsForRenameSubOperation = mockedRenameSubOperation.mock.calls[0];
  801. // restores the original implementation
  802. mockedRenameSubOperation.mockRestore();
  803. mockedCreateAndSendNotifications.mockRestore();
  804. // rename descendants
  805. await crowi.pageService.renameSubOperation(...argsForRenameSubOperation);
  806. return renamedPage;
  807. };
  808. test('Should NOT rename top page', async() => {
  809. expectAllToBeTruthy([rootPage]);
  810. let isThrown = false;
  811. try {
  812. await crowi.pageService.renamePage(rootPage, '/new_root', dummyUser1, {});
  813. }
  814. catch (err) {
  815. isThrown = true;
  816. }
  817. expect(isThrown).toBe(true);
  818. });
  819. test('Should rename/move to under non-empty page', async() => {
  820. const parentPage = await Page.findOne({ path: '/v5_ParentForRename1' });
  821. const childPage = await Page.findOne({ path: '/v5_ChildForRename1' });
  822. expectAllToBeTruthy([childPage, parentPage]);
  823. const newPath = '/v5_ParentForRename1/renamedChildForRename1';
  824. const renamedPage = await renamePage(childPage, newPath, dummyUser1, {});
  825. const childPageBeforeRename = await Page.findOne({ path: '/v5_ChildForRename1' });
  826. expect(xssSpy).toHaveBeenCalled();
  827. expect(renamedPage.path).toBe(newPath);
  828. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  829. expect(childPageBeforeRename).toBeNull();
  830. });
  831. test('Should rename/move to under empty page', async() => {
  832. const parentPage = await Page.findOne({ path: '/v5_ParentForRename2' });
  833. const childPage = await Page.findOne({ path: '/v5_ChildForRename2' });
  834. expectAllToBeTruthy([childPage, parentPage]);
  835. expect(parentPage.isEmpty).toBe(true);
  836. const newPath = '/v5_ParentForRename2/renamedChildForRename2';
  837. const renamedPage = await renamePage(childPage, newPath, dummyUser1, {});
  838. const childPageBeforeRename = await Page.findOne({ path: '/v5_ChildForRename2' });
  839. expect(xssSpy).toHaveBeenCalled();
  840. expect(renamedPage.path).toBe(newPath);
  841. expect(parentPage.isEmpty).toBe(true);
  842. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  843. expect(childPageBeforeRename).toBeNull();
  844. });
  845. test('Should rename/move with option updateMetadata: true', async() => {
  846. const parentPage = await Page.findOne({ path: '/v5_ParentForRename3' });
  847. const childPage = await Page.findOne({ path: '/v5_ChildForRename3' });
  848. expectAllToBeTruthy([childPage, parentPage]);
  849. expect(childPage.lastUpdateUser).toStrictEqual(dummyUser1._id);
  850. const newPath = '/v5_ParentForRename3/renamedChildForRename3';
  851. const oldUpdateAt = childPage.updatedAt;
  852. const renamedPage = await renamePage(childPage, newPath, dummyUser2, { updateMetadata: true });
  853. expect(xssSpy).toHaveBeenCalled();
  854. expect(renamedPage.path).toBe(newPath);
  855. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  856. expect(renamedPage.lastUpdateUser).toStrictEqual(dummyUser2._id);
  857. expect(renamedPage.updatedAt.getFullYear()).toBeGreaterThan(oldUpdateAt.getFullYear());
  858. });
  859. test('Should move with option createRedirectPage: true', async() => {
  860. const parentPage = await Page.findOne({ path: '/v5_ParentForRename4' });
  861. const childPage = await Page.findOne({ path: '/v5_ChildForRename4' });
  862. expectAllToBeTruthy([parentPage, childPage]);
  863. const oldPath = childPage.path;
  864. const newPath = '/v5_ParentForRename4/renamedChildForRename4';
  865. const renamedPage = await renamePage(childPage, newPath, dummyUser2, { createRedirectPage: true });
  866. const pageRedirect = await PageRedirect.findOne({ fromPath: oldPath, toPath: renamedPage.path });
  867. expect(xssSpy).toHaveBeenCalled();
  868. expect(renamedPage.path).toBe(newPath);
  869. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  870. expect(pageRedirect).toBeTruthy();
  871. });
  872. test('Should rename/move with descendants', async() => {
  873. const parentPage = await Page.findOne({ path: '/v5_ParentForRename5' });
  874. const childPage = await Page.findOne({ path: '/v5_ChildForRename5' });
  875. const grandchild = await Page.findOne({ parent: childPage._id, path: '/v5_ChildForRename5/v5_GrandchildForRename5' });
  876. expectAllToBeTruthy([parentPage, childPage, grandchild]);
  877. const newPath = '/v5_ParentForRename5/renamedChildForRename5';
  878. const renamedPage = await renamePage(childPage, newPath, dummyUser1, {});
  879. // find child of renamed page
  880. const renamedGrandchild = await Page.findOne({ parent: renamedPage._id });
  881. const childPageBeforeRename = await Page.findOne({ path: '/v5_ChildForRename5' });
  882. const grandchildBeforeRename = await Page.findOne({ path: grandchild.path });
  883. expect(xssSpy).toHaveBeenCalled();
  884. expect(renamedPage.path).toBe(newPath);
  885. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  886. expect(childPageBeforeRename).toBeNull();
  887. expect(grandchildBeforeRename).toBeNull();
  888. // grandchild's parent should be the renamed page
  889. expect(renamedGrandchild.parent).toStrictEqual(renamedPage._id);
  890. expect(renamedGrandchild.path).toBe('/v5_ParentForRename5/renamedChildForRename5/v5_GrandchildForRename5');
  891. });
  892. test('Should rename/move empty page', async() => {
  893. const parentPage = await Page.findOne({ path: '/v5_ParentForRename7' });
  894. const childPage = await Page.findOne({ path: '/v5_ChildForRename7', isEmpty: true });
  895. const grandchild = await Page.findOne({ parent: childPage._id, path: '/v5_ChildForRename7/v5_GrandchildForRename7' });
  896. expectAllToBeTruthy([parentPage, childPage, grandchild]);
  897. const newPath = '/v5_ParentForRename7/renamedChildForRename7';
  898. const renamedPage = await renamePage(childPage, newPath, dummyUser1, {});
  899. const grandchildAfterRename = await Page.findOne({ parent: renamedPage._id });
  900. const grandchildBeforeRename = await Page.findOne({ path: '/v5_ChildForRename7/v5_GrandchildForRename7' });
  901. expect(xssSpy).toHaveBeenCalled();
  902. expect(renamedPage.path).toBe(newPath);
  903. expect(renamedPage.isEmpty).toBe(true);
  904. expect(renamedPage.parent).toStrictEqual(parentPage._id);
  905. expect(grandchildBeforeRename).toBeNull();
  906. // grandchild's parent should be renamed page
  907. expect(grandchildAfterRename.parent).toStrictEqual(renamedPage._id);
  908. expect(grandchildAfterRename.path).toBe('/v5_ParentForRename7/renamedChildForRename7/v5_GrandchildForRename7');
  909. });
  910. test('Should NOT rename/move with existing path', async() => {
  911. const page = await Page.findOne({ path: '/v5_ParentForRename8' });
  912. expectAllToBeTruthy([page]);
  913. const newPath = '/v5_ParentForRename9';
  914. let isThrown;
  915. try {
  916. await renamePage(page, newPath, dummyUser1, {});
  917. }
  918. catch (err) {
  919. isThrown = true;
  920. }
  921. expect(isThrown).toBe(true);
  922. });
  923. });
  924. describe('Duplicate', () => {
  925. const duplicate = async(page, newPagePath, user, isRecursively) => {
  926. // mock return value
  927. const mockedDuplicateRecursivelyMainOperation = jest.spyOn(crowi.pageService, 'duplicateRecursivelyMainOperation').mockReturnValue(null);
  928. const mockedCreateAndSendNotifications = jest.spyOn(crowi.pageService, 'createAndSendNotifications').mockReturnValue(null);
  929. const duplicatedPage = await crowi.pageService.duplicate(page, newPagePath, user, isRecursively);
  930. // retrieve the arguments passed when calling method duplicateRecursivelyMainOperation inside duplicate method
  931. const argsForDuplicateRecursivelyMainOperation = mockedDuplicateRecursivelyMainOperation.mock.calls[0];
  932. // restores the original implementation
  933. mockedDuplicateRecursivelyMainOperation.mockRestore();
  934. mockedCreateAndSendNotifications.mockRestore();
  935. // duplicate descendants
  936. if (isRecursively) {
  937. await crowi.pageService.duplicateRecursivelyMainOperation(...argsForDuplicateRecursivelyMainOperation);
  938. }
  939. return duplicatedPage;
  940. };
  941. test('Should duplicate single page', async() => {
  942. const page = await Page.findOne({ path: '/v5_PageForDuplicate1' });
  943. expectAllToBeTruthy([page]);
  944. const newPagePath = '/duplicatedv5PageForDuplicate1';
  945. const duplicatedPage = await duplicate(page, newPagePath, dummyUser1, false);
  946. const duplicatedRevision = await Revision.findOne({ pageId: duplicatedPage._id });
  947. const baseRevision = await Revision.findOne({ pageId: page._id });
  948. // new path
  949. expect(xssSpy).toHaveBeenCalled();
  950. expect(duplicatedPage.path).toBe(newPagePath);
  951. expect(duplicatedPage._id).not.toStrictEqual(page._id);
  952. expect(duplicatedPage.revision).toStrictEqual(duplicatedRevision._id);
  953. expect(duplicatedRevision.body).toEqual(baseRevision.body);
  954. });
  955. test('Should NOT duplicate single empty page', async() => {
  956. const page = await Page.findOne({ path: '/v5_PageForDuplicate2' });
  957. expectAllToBeTruthy([page]);
  958. let isThrown;
  959. let duplicatedPage;
  960. try {
  961. const newPagePath = '/duplicatedv5PageForDuplicate2';
  962. duplicatedPage = await duplicate(page, newPagePath, dummyUser1, false);
  963. }
  964. catch (err) {
  965. isThrown = true;
  966. }
  967. expect(duplicatedPage).toBeUndefined();
  968. expect(isThrown).toBe(true);
  969. });
  970. test('Should duplicate multiple pages', async() => {
  971. const basePage = await Page.findOne({ path: '/v5_PageForDuplicate3' });
  972. const revision = await Revision.findOne({ pageId: basePage._id });
  973. const childPage1 = await Page.findOne({ path: '/v5_PageForDuplicate3/v5_Child_1_ForDuplicate3' }).populate({ path: 'revision', model: 'Revision' });
  974. const childPage2 = await Page.findOne({ path: '/v5_PageForDuplicate3/v5_Child_2_ForDuplicate3' }).populate({ path: 'revision', model: 'Revision' });
  975. const revisionForChild1 = childPage1.revision;
  976. const revisionForChild2 = childPage2.revision;
  977. expectAllToBeTruthy([basePage, revision, childPage1, childPage2, revisionForChild1, revisionForChild2]);
  978. const newPagePath = '/duplicatedv5PageForDuplicate3';
  979. const duplicatedPage = await duplicate(basePage, newPagePath, dummyUser1, true);
  980. const duplicatedChildPage1 = await Page.findOne({ parent: duplicatedPage._id, path: '/duplicatedv5PageForDuplicate3/v5_Child_1_ForDuplicate3' })
  981. .populate({ path: 'revision', model: 'Revision' });
  982. const duplicatedChildPage2 = await Page.findOne({ parent: duplicatedPage._id, path: '/duplicatedv5PageForDuplicate3/v5_Child_2_ForDuplicate3' })
  983. .populate({ path: 'revision', model: 'Revision' });
  984. const revisionForDuplicatedPage = await Revision.findOne({ pageId: duplicatedPage._id });
  985. const revisionBodyForDupChild1 = duplicatedChildPage1.revision;
  986. const revisionBodyForDupChild2 = duplicatedChildPage2.revision;
  987. expectAllToBeTruthy([duplicatedPage, duplicatedChildPage1, duplicatedChildPage2,
  988. revisionForDuplicatedPage, revisionBodyForDupChild1, revisionBodyForDupChild2]);
  989. expect(xssSpy).toHaveBeenCalled();
  990. expect(duplicatedPage.path).toBe(newPagePath);
  991. expect(duplicatedChildPage1.path).toBe('/duplicatedv5PageForDuplicate3/v5_Child_1_ForDuplicate3');
  992. expect(duplicatedChildPage2.path).toBe('/duplicatedv5PageForDuplicate3/v5_Child_2_ForDuplicate3');
  993. });
  994. test('Should duplicate multiple pages with empty child in it', async() => {
  995. const basePage = await Page.findOne({ path: '/v5_PageForDuplicate4' });
  996. const baseChild = await Page.findOne({ parent: basePage._id, isEmpty: true });
  997. const baseGrandchild = await Page.findOne({ parent: baseChild._id });
  998. expectAllToBeTruthy([basePage, baseChild, baseGrandchild]);
  999. const newPagePath = '/duplicatedv5PageForDuplicate4';
  1000. const duplicatedPage = await duplicate(basePage, newPagePath, dummyUser1, true);
  1001. const duplicatedChild = await Page.findOne({ parent: duplicatedPage._id });
  1002. const duplicatedGrandchild = await Page.findOne({ parent: duplicatedChild._id });
  1003. expect(xssSpy).toHaveBeenCalled();
  1004. expectAllToBeTruthy([duplicatedPage, duplicatedGrandchild]);
  1005. expect(duplicatedPage.path).toBe(newPagePath);
  1006. expect(duplicatedChild.path).toBe('/duplicatedv5PageForDuplicate4/v5_empty_PageForDuplicate4');
  1007. expect(duplicatedGrandchild.path).toBe('/duplicatedv5PageForDuplicate4/v5_empty_PageForDuplicate4/v5_grandchild_PageForDuplicate4');
  1008. expect(duplicatedChild.isEmpty).toBe(true);
  1009. expect(duplicatedGrandchild.parent).toStrictEqual(duplicatedChild._id);
  1010. expect(duplicatedChild.parent).toStrictEqual(duplicatedPage._id);
  1011. });
  1012. test('Should duplicate tags', async() => {
  1013. const basePage = await Page.findOne({ path: '/v5_PageForDuplicate5' });
  1014. const tag1 = await Tag.findOne({ name: 'duplicate_Tag1' });
  1015. const tag2 = await Tag.findOne({ name: 'duplicate_Tag2' });
  1016. const basePageTagRelation1 = await PageTagRelation.findOne({ relatedTag: tag1._id });
  1017. const basePageTagRelation2 = await PageTagRelation.findOne({ relatedTag: tag2._id });
  1018. expectAllToBeTruthy([basePage, tag1, tag2, basePageTagRelation1, basePageTagRelation2]);
  1019. const newPagePath = '/duplicatedv5PageForDuplicate5';
  1020. const duplicatedPage = await duplicate(basePage, newPagePath, dummyUser1, false);
  1021. const duplicatedTagRelations = await PageTagRelation.find({ relatedPage: duplicatedPage._id });
  1022. expect(xssSpy).toHaveBeenCalled();
  1023. expect(duplicatedPage.path).toBe(newPagePath);
  1024. expect(duplicatedTagRelations.length).toBeGreaterThanOrEqual(2);
  1025. });
  1026. test('Should NOT duplicate comments', async() => {
  1027. const basePage = await Page.findOne({ path: '/v5_PageForDuplicate6' });
  1028. const basePageComments = await Comment.find({ page: basePage._id });
  1029. expectAllToBeTruthy([basePage, ...basePageComments]);
  1030. const newPagePath = '/duplicatedv5PageForDuplicate6';
  1031. const duplicatedPage = await duplicate(basePage, newPagePath, dummyUser1, false);
  1032. const duplicatedComments = await Comment.find({ page: duplicatedPage._id });
  1033. expect(xssSpy).toHaveBeenCalled();
  1034. expect(duplicatedPage.path).toBe(newPagePath);
  1035. expect(basePageComments.length).not.toBe(duplicatedComments.length);
  1036. });
  1037. test('Should duplicate empty page with descendants', async() => {
  1038. const basePage = await Page.findOne({ path: '/v5_empty_PageForDuplicate7' });
  1039. const basePageChild = await Page.findOne({ parent: basePage._id }).populate({ path: 'revision', model: 'Revision' });
  1040. const basePageGrandhild = await Page.findOne({ parent: basePageChild._id }).populate({ path: 'revision', model: 'Revision' });
  1041. expectAllToBeTruthy([basePage, basePageChild, basePageGrandhild, basePageChild.revision, basePageGrandhild.revision]);
  1042. const newPagePath = '/duplicatedv5EmptyPageForDuplicate7';
  1043. const duplicatedPage = await duplicate(basePage, newPagePath, dummyUser1, true);
  1044. const duplicatedChild = await Page.findOne({ parent: duplicatedPage._id }).populate({ path: 'revision', model: 'Revision' });
  1045. const duplicatedGrandchild = await Page.findOne({ parent: duplicatedChild._id }).populate({ path: 'revision', model: 'Revision' });
  1046. expectAllToBeTruthy([duplicatedPage, duplicatedChild, duplicatedGrandchild, duplicatedChild.revision, duplicatedGrandchild.revision]);
  1047. expect(xssSpy).toHaveBeenCalled();
  1048. expect(duplicatedPage.path).toBe(newPagePath);
  1049. expect(duplicatedPage.isEmpty).toBe(true);
  1050. expect(duplicatedChild.revision.body).toBe(basePageChild.revision.body);
  1051. expect(duplicatedGrandchild.revision.body).toBe(basePageGrandhild.revision.body);
  1052. expect(duplicatedChild.path).toBe('/duplicatedv5EmptyPageForDuplicate7/v5_child_PageForDuplicate7');
  1053. expect(duplicatedGrandchild.path).toBe('/duplicatedv5EmptyPageForDuplicate7/v5_child_PageForDuplicate7/v5_grandchild_PageForDuplicate7');
  1054. expect(duplicatedGrandchild.parent).toStrictEqual(duplicatedChild._id);
  1055. expect(duplicatedChild.parent).toStrictEqual(duplicatedPage._id);
  1056. });
  1057. });
  1058. describe('Delete', () => {
  1059. const deletePage = async(page, user, options, isRecursively) => {
  1060. const mockedDeleteRecursivelyMainOperation = jest.spyOn(crowi.pageService, 'deleteRecursivelyMainOperation').mockReturnValue(null);
  1061. const mockedCreateAndSendNotifications = jest.spyOn(crowi.pageService, 'createAndSendNotifications').mockReturnValue(null);
  1062. const deletedPage = await crowi.pageService.deletePage(page, user, options, isRecursively);
  1063. const argsForDeleteRecursivelyMainOperation = mockedDeleteRecursivelyMainOperation.mock.calls[0];
  1064. mockedDeleteRecursivelyMainOperation.mockRestore();
  1065. mockedCreateAndSendNotifications.mockRestore();
  1066. if (isRecursively) {
  1067. await crowi.pageService.deleteRecursivelyMainOperation(...argsForDeleteRecursivelyMainOperation);
  1068. }
  1069. return deletedPage;
  1070. };
  1071. test('Should NOT delete root page', async() => {
  1072. let isThrown;
  1073. expectAllToBeTruthy([rootPage]);
  1074. try { await deletePage(rootPage, dummyUser1, {}, false) }
  1075. catch (err) { isThrown = true }
  1076. const page = await Page.findOne({ path: '/' });
  1077. expect(isThrown).toBe(true);
  1078. expect(page).toBeTruthy();
  1079. });
  1080. test('Should NOT delete trashed page', async() => {
  1081. const trashedPage = await Page.findOne({ path: '/trash/v5_PageForDelete1' });
  1082. expectAllToBeTruthy([trashedPage]);
  1083. let isThrown;
  1084. try { await deletePage(trashedPage, dummyUser1, {}, false) }
  1085. catch (err) { isThrown = true }
  1086. const page = await Page.findOne({ path: '/trash/v5_PageForDelete1' });
  1087. expect(page).toBeTruthy();
  1088. expect(isThrown).toBe(true);
  1089. });
  1090. test('Should NOT delete /user/hoge page', async() => {
  1091. const dummyUser1Page = await Page.findOne({ path: '/user/v5DummyUser1' });
  1092. expectAllToBeTruthy([dummyUser1Page]);
  1093. let isThrown;
  1094. try { await deletePage(dummyUser1Page, dummyUser1, {}, false) }
  1095. catch (err) { isThrown = true }
  1096. const page = await Page.findOne({ path: '/user/v5DummyUser1' });
  1097. expect(page).toBeTruthy();
  1098. expect(isThrown).toBe(true);
  1099. });
  1100. test('Should delete single page', async() => {
  1101. const pageToDelete = await Page.findOne({ path: '/v5_PageForDelete2' });
  1102. expectAllToBeTruthy([pageToDelete]);
  1103. const deletedPage = await deletePage(pageToDelete, dummyUser1, {}, false);
  1104. const page = await Page.findOne({ path: '/v5_PageForDelete2' });
  1105. expect(page).toBeNull();
  1106. expect(deletedPage.path).toBe(`/trash${pageToDelete.path}`);
  1107. expect(deletedPage.parent).toBeNull();
  1108. expect(deletedPage.status).toBe(Page.STATUS_DELETED);
  1109. });
  1110. test('Should delete multiple pages including empty child', async() => {
  1111. const parentPage = await Page.findOne({ path: '/v5_PageForDelete3' });
  1112. const childPage = await Page.findOne({ path: '/v5_PageForDelete3/v5_PageForDelete4' });
  1113. const grandchildPage = await Page.findOne({ path: '/v5_PageForDelete3/v5_PageForDelete4/v5_PageForDelete5' });
  1114. expectAllToBeTruthy([parentPage, childPage, grandchildPage]);
  1115. const deletedParentPage = await deletePage(parentPage, dummyUser1, {}, true);
  1116. const deletedChildPage = await Page.findOne({ path: '/trash/v5_PageForDelete3/v5_PageForDelete4' });
  1117. const deletedGrandchildPage = await Page.findOne({ path: '/trash/v5_PageForDelete3/v5_PageForDelete4/v5_PageForDelete5' });
  1118. // originally NOT empty page should exist with status 'deleted' and parent set null
  1119. expect(deletedParentPage._id).toStrictEqual(parentPage._id);
  1120. expect(deletedParentPage.status).toBe(Page.STATUS_DELETED);
  1121. expect(deletedParentPage.parent).toBeNull();
  1122. // originally empty page should NOT exist
  1123. expect(deletedChildPage).toBeNull();
  1124. // originally NOT empty page should exist with status 'deleted' and parent set null
  1125. expect(deletedGrandchildPage._id).toStrictEqual(grandchildPage._id);
  1126. expect(deletedGrandchildPage.status).toBe(Page.STATUS_DELETED);
  1127. expect(deletedGrandchildPage.parent).toBeNull();
  1128. });
  1129. test('Should delete page tag relation', async() => {
  1130. const pageToDelete = await Page.findOne({ path: '/v5_PageForDelete6' });
  1131. const tag1 = await Tag.findOne({ name: 'TagForDelete1' });
  1132. const tag2 = await Tag.findOne({ name: 'TagForDelete2' });
  1133. const pageRelation1 = await PageTagRelation.findOne({ relatedTag: tag1._id });
  1134. const pageRelation2 = await PageTagRelation.findOne({ relatedTag: tag2._id });
  1135. expectAllToBeTruthy([pageToDelete, tag1, tag2, pageRelation1, pageRelation2]);
  1136. const deletedPage = await deletePage(pageToDelete, dummyUser1, {}, false);
  1137. const page = await Page.findOne({ path: '/v5_PageForDelete6' });
  1138. const deletedTagRelation1 = await PageTagRelation.findOne({ _id: pageRelation1._id });
  1139. const deletedTagRelation2 = await PageTagRelation.findOne({ _id: pageRelation2._id });
  1140. expect(page).toBe(null);
  1141. expect(deletedPage.status).toBe(Page.STATUS_DELETED);
  1142. expect(deletedTagRelation1.isPageTrashed).toBe(true);
  1143. expect(deletedTagRelation2.isPageTrashed).toBe(true);
  1144. });
  1145. });
  1146. describe('Delete completely', () => {
  1147. const deleteCompletely = async(page, user, options = {}, isRecursively = false, preventEmitting = false) => {
  1148. const mockedDeleteCompletelyRecursivelyMainOperation = jest.spyOn(crowi.pageService, 'deleteCompletelyRecursivelyMainOperation').mockReturnValue(null);
  1149. const mockedCreateAndSendNotifications = jest.spyOn(crowi.pageService, 'createAndSendNotifications').mockReturnValue(null);
  1150. await crowi.pageService.deleteCompletely(page, user, options, isRecursively, preventEmitting);
  1151. const argsForDeleteCompletelyRecursivelyMainOperation = mockedDeleteCompletelyRecursivelyMainOperation.mock.calls[0];
  1152. mockedDeleteCompletelyRecursivelyMainOperation.mockRestore();
  1153. mockedCreateAndSendNotifications.mockRestore();
  1154. if (isRecursively) {
  1155. await crowi.pageService.deleteCompletelyRecursivelyMainOperation(...argsForDeleteCompletelyRecursivelyMainOperation);
  1156. }
  1157. return;
  1158. };
  1159. test('Should NOT completely delete root page', async() => {
  1160. expectAllToBeTruthy([rootPage]);
  1161. let isThrown;
  1162. try { await deleteCompletely(rootPage, dummyUser1, {}, false) }
  1163. catch (err) { isThrown = true }
  1164. const page = await Page.findOne({ path: '/' });
  1165. expect(page).toBeTruthy();
  1166. expect(isThrown).toBe(true);
  1167. });
  1168. test('Should completely delete single page', async() => {
  1169. const page = await Page.findOne({ path: '/v5_PageForDeleteCompletely1' });
  1170. expectAllToBeTruthy([page]);
  1171. await deleteCompletely(page, dummyUser1, {}, false);
  1172. const deletedPage = await Page.findOne({ _id: page._id, path: '/v5_PageForDeleteCompletely1' });
  1173. expect(deletedPage).toBeNull();
  1174. });
  1175. test('Should completely delete multiple pages', async() => {
  1176. const parentPage = await Page.findOne({ path: '/v5_PageForDeleteCompletely2' });
  1177. const childPage = await Page.findOne({ path: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3' });
  1178. const grandchildPage = await Page.findOne({ path: '/v5_PageForDeleteCompletely2/v5_PageForDeleteCompletely3/v5_PageForDeleteCompletely4' });
  1179. const tag1 = await Tag.findOne({ name: 'TagForDeleteCompletely1' });
  1180. const tag2 = await Tag.findOne({ name: 'TagForDeleteCompletely2' });
  1181. const pageTagRelation1 = await PageTagRelation.findOne({ relatedPage: parentPage._id });
  1182. const pageTagRelation2 = await PageTagRelation.findOne({ relatedPage: grandchildPage._id });
  1183. const bookmark = await Bookmark.findOne({ page: parentPage._id });
  1184. const comment = await Comment.findOne({ page: parentPage._id });
  1185. const pageRedirect1 = await PageRedirect.findOne({ toPath: parentPage.path });
  1186. const pageRedirect2 = await PageRedirect.findOne({ toPath: grandchildPage.path });
  1187. const shareLink1 = await ShareLink.findOne({ relatedPage: parentPage._id });
  1188. const shareLink2 = await ShareLink.findOne({ relatedPage: grandchildPage._id });
  1189. expectAllToBeTruthy(
  1190. [parentPage, childPage, grandchildPage, tag1, tag2,
  1191. pageTagRelation1, pageTagRelation2, bookmark, comment,
  1192. pageRedirect1, pageRedirect2, shareLink1, shareLink2],
  1193. );
  1194. await deleteCompletely(parentPage, dummyUser1, {}, true);
  1195. const deletedPages = await Page.find({ _id: { $in: [parentPage._id, childPage._id, grandchildPage._id] } });
  1196. const deletedRevisions = await Revision.find({ pageId: { $in: [parentPage._id, grandchildPage._id] } });
  1197. const tags = await Tag.find({ _id: { $in: [tag1._id, tag2._id] } });
  1198. const deletedPageTagRelations = await PageTagRelation.find({ _id: { $in: [pageTagRelation1._id, pageTagRelation2._id] } });
  1199. const deletedBookmarks = await Bookmark.find({ _id: bookmark._id });
  1200. const deletedComments = await Comment.find({ _id: comment._id });
  1201. const deletedPageRedirects = await PageRedirect.find({ _id: { $in: [pageRedirect1._id, pageRedirect2._id] } });
  1202. const deletedShareLinks = await ShareLink.find({ _id: { $in: [shareLink1._id, shareLink2._id] } });
  1203. // page should be null
  1204. expect(deletedPages.length).toBe(0);
  1205. // revision should be null
  1206. expect(deletedRevisions.length).toBe(0);
  1207. // tag should be Truthy
  1208. expectAllToBeTruthy(tags);
  1209. // pageTagRelation should be null
  1210. expect(deletedPageTagRelations.length).toBe(0);
  1211. // bookmark should be null
  1212. expect(deletedBookmarks.length).toBe(0);
  1213. // comment should be null
  1214. expect(deletedComments.length).toBe(0);
  1215. // pageRedirect should be null
  1216. expect(deletedPageRedirects.length).toBe(0);
  1217. // sharelink should be null
  1218. expect(deletedShareLinks.length).toBe(0);
  1219. });
  1220. test('Should completely delete trashed page', async() => {
  1221. const page = await Page.findOne({ path: '/trash/v5_PageForDeleteCompletely5' });
  1222. const revision = await Revision.findOne({ pageId: page._id });
  1223. expectAllToBeTruthy([page, revision]);
  1224. await deleteCompletely(page, dummyUser1, {}, false);
  1225. const deltedPage = await Page.findOne({ _id: page._id });
  1226. const deltedRevision = await Revision.findOne({ _id: revision._id });
  1227. expect(deltedPage).toBeNull();
  1228. expect(deltedRevision).toBeNull();
  1229. });
  1230. test('Should completely deleting page in the middle results in having an empty page', async() => {
  1231. const parentPage = await Page.findOne({ path: '/v5_PageForDeleteCompletely6' });
  1232. const childPage = await Page.findOne({ path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7' });
  1233. const grandchildPage = await Page.findOne({ path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7/v5_PageForDeleteCompletely8' });
  1234. expectAllToBeTruthy([parentPage, childPage, grandchildPage]);
  1235. await deleteCompletely(childPage, dummyUser1, {}, false);
  1236. const parentPageAfterDelete = await Page.findOne({ path: '/v5_PageForDeleteCompletely6' });
  1237. const childPageAfterDelete = await Page.findOne({ path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7' });
  1238. const grandchildPageAfterDelete = await Page.findOne({ path: '/v5_PageForDeleteCompletely6/v5_PageForDeleteCompletely7/v5_PageForDeleteCompletely8' });
  1239. const childOfDeletedPage = await Page.findOne({ parent: childPageAfterDelete._id });
  1240. expectAllToBeTruthy([parentPageAfterDelete, childPageAfterDelete, grandchildPageAfterDelete]);
  1241. expect(childPageAfterDelete._id).not.toStrictEqual(childPage._id);
  1242. expect(childPageAfterDelete.isEmpty).toBe(true);
  1243. expect(childPageAfterDelete.parent).toStrictEqual(parentPage._id);
  1244. expect(childOfDeletedPage._id).toStrictEqual(grandchildPage._id);
  1245. });
  1246. });
  1247. describe('revert', () => {
  1248. const revertDeletedPage = async(page, user, options = {}, isRecursively = false) => {
  1249. // mock return value
  1250. const mockedRevertRecursivelyMainOperation = jest.spyOn(crowi.pageService, 'revertRecursivelyMainOperation').mockReturnValue(null);
  1251. const revertedPage = await crowi.pageService.revertDeletedPage(page, user, options, isRecursively);
  1252. const argsForRecursivelyMainOperation = mockedRevertRecursivelyMainOperation.mock.calls[0];
  1253. // restores the original implementation
  1254. mockedRevertRecursivelyMainOperation.mockRestore();
  1255. if (isRecursively) {
  1256. await crowi.pageService.revertRecursivelyMainOperation(...argsForRecursivelyMainOperation);
  1257. }
  1258. return revertedPage;
  1259. };
  1260. test('revert single deleted page', async() => {
  1261. const deletedPage = await Page.findOne({ path: '/trash/v5_revert1', status: Page.STATUS_DELETED });
  1262. const revision = await Revision.findOne({ pageId: deletedPage._id });
  1263. const tag = await Tag.findOne({ name: 'revertTag1' });
  1264. const deletedPageTagRelation = await PageTagRelation.findOne({ relatedPage: deletedPage._id, relatedTag: tag._id, isPageTrashed: true });
  1265. expectAllToBeTruthy([deletedPage, revision, tag, deletedPageTagRelation]);
  1266. const revertedPage = await revertDeletedPage(deletedPage, dummyUser1, {}, false);
  1267. const pageTagRelation = await PageTagRelation.findOne({ relatedPage: deletedPage._id, relatedTag: tag._id });
  1268. expect(revertedPage.parent).toStrictEqual(rootPage._id);
  1269. expect(revertedPage.path).toBe('/v5_revert1');
  1270. expect(revertedPage.status).toBe(Page.STATUS_PUBLISHED);
  1271. expect(pageTagRelation.isPageTrashed).toBe(false);
  1272. });
  1273. test('revert multiple deleted page (has non existent page in the middle)', async() => {
  1274. const deletedPage1 = await Page.findOne({ path: '/trash/v5_revert2', status: Page.STATUS_DELETED });
  1275. const deletedPage2 = await Page.findOne({ path: '/trash/v5_revert2/v5_revert3/v5_revert4', status: Page.STATUS_DELETED });
  1276. const revision1 = await Revision.findOne({ pageId: deletedPage1._id });
  1277. const revision2 = await Revision.findOne({ pageId: deletedPage2._id });
  1278. expectAllToBeTruthy([deletedPage1, deletedPage2, revision1, revision2]);
  1279. const revertedPage1 = await revertDeletedPage(deletedPage1, dummyUser1, {}, true);
  1280. const revertedPage2 = await Page.findOne({ _id: deletedPage2._id });
  1281. const newlyCreatedPage = await Page.findOne({ path: '/v5_revert2/v5_revert3' });
  1282. expectAllToBeTruthy([revertedPage1, revertedPage2, newlyCreatedPage]);
  1283. expect(revertedPage1.parent).toStrictEqual(rootPage._id);
  1284. expect(revertedPage1.path).toBe('/v5_revert2');
  1285. expect(revertedPage2.path).toBe('/v5_revert2/v5_revert3/v5_revert4');
  1286. expect(newlyCreatedPage.parent).toStrictEqual(revertedPage1._id);
  1287. expect(revertedPage2.parent).toStrictEqual(newlyCreatedPage._id);
  1288. expect(revertedPage1.status).toBe(Page.STATUS_PUBLISHED);
  1289. expect(revertedPage2.status).toBe(Page.STATUS_PUBLISHED);
  1290. expect(newlyCreatedPage.status).toBe(Page.STATUS_PUBLISHED);
  1291. });
  1292. });
  1293. });
  1294. describe('PageService page operations with non-public pages', () => {
  1295. // TODO: write test code
  1296. });