v5.page.test.ts 52 KB

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