2
0
Эх сурвалжийг харах

fix non-autofixable biome errors

Futa Arai 7 сар өмнө
parent
commit
197562ee17

+ 1 - 1
apps/app/src/server/service/page-grant.ts

@@ -729,7 +729,7 @@ class PageGrantService implements IPageGrantService {
   /*
   /*
    * get all groups that user is related to
    * get all groups that user is related to
    */
    */
-  async getUserRelatedGroups(user?: IUserHasId | null): Promise<PopulatedGrantedGroup[]> {
+  async getUserRelatedGroups(user?: IUserHasId | HydratedDocument<IUser> | null): Promise<PopulatedGrantedGroup[]> {
     if (user == null) {
     if (user == null) {
       return [];
       return [];
     }
     }

+ 19 - 0
apps/app/src/server/service/page/page-service.ts

@@ -4,6 +4,7 @@ import type {
   HasObjectId,
   HasObjectId,
   IDataWithMeta,
   IDataWithMeta,
   IGrantedGroup,
   IGrantedGroup,
+  IPage,
   IPageInfo, IPageInfoAll, IPageInfoForEntity, IUser,
   IPageInfo, IPageInfoAll, IPageInfoForEntity, IUser,
 } from '@growi/core';
 } from '@growi/core';
 import type { HydratedDocument, Types } from 'mongoose';
 import type { HydratedDocument, Types } from 'mongoose';
@@ -53,4 +54,22 @@ export interface IPageService {
     page: PageDocument, creatorId: ObjectIdLike | null, operator: any | null, userRelatedGroups: PopulatedGrantedGroup[]
     page: PageDocument, creatorId: ObjectIdLike | null, operator: any | null, userRelatedGroups: PopulatedGrantedGroup[]
   ): boolean,
   ): boolean,
   getYjsData(pageId: string, revisionBody?: string): Promise<CurrentPageYjsData>,
   getYjsData(pageId: string, revisionBody?: string): Promise<CurrentPageYjsData>,
+  revertRecursivelyMainOperation(page, user, options, pageOpId: ObjectIdLike, activity?): Promise<void>,
+  revertDeletedPage(page, user, options, isRecursively: boolean, activityParameters?),
+  deleteCompletelyRecursivelyMainOperation(page, user, options, pageOpId: ObjectIdLike, activity?): Promise<void>,
+  deleteCompletely(page, user, options, isRecursively: boolean, preventEmitting: boolean, activityParameters),
+  deleteRecursivelyMainOperation(page, user, pageOpId: ObjectIdLike, activity?): Promise<void>,
+  deletePage(page, user, options, isRecursively: boolean, activityParameters),
+  duplicateRecursivelyMainOperation(
+    page: PageDocument,
+    newPagePath: string,
+    user,
+    pageOpId: ObjectIdLike,
+    onlyDuplicateUserRelatedResources: boolean,
+  ): Promise<void>,
+  duplicate(page: PageDocument, newPagePath: string, user, isRecursively: boolean, onlyDuplicateUserRelatedResources: boolean),
+  renameSubOperation(page, newPagePath: string, user, options, renamedPage, pageOpId: ObjectIdLike, activity?): Promise<void>,
+  renamePage(page: IPage, newPagePath, user, options, activityParameters): Promise<PageDocument | null>,
+  renameMainOperation(page, newPagePath: string, user, options, pageOpId: ObjectIdLike, activity?): Promise<PageDocument | null>,
+  createSubOperation(page, user, options: IOptionsForCreate, pageOpId: ObjectIdLike): Promise<void>,
 }
 }

+ 12 - 13
apps/app/test/integration/service/external-user-group-sync.test.ts

@@ -10,6 +10,7 @@ import {
 import ExternalUserGroup from '../../../src/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroup from '../../../src/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroupRelation from '../../../src/features/external-user-group/server/models/external-user-group-relation';
 import ExternalUserGroupRelation from '../../../src/features/external-user-group/server/models/external-user-group-relation';
 import ExternalUserGroupSyncService from '../../../src/features/external-user-group/server/service/external-user-group-sync';
 import ExternalUserGroupSyncService from '../../../src/features/external-user-group/server/service/external-user-group-sync';
+import type Crowi from '../../../src/server/crowi';
 import ExternalAccount from '../../../src/server/models/external-account';
 import ExternalAccount from '../../../src/server/models/external-account';
 import { configManager } from '../../../src/server/service/config-manager';
 import { configManager } from '../../../src/server/service/config-manager';
 import instanciateExternalAccountService from '../../../src/server/service/external-account';
 import instanciateExternalAccountService from '../../../src/server/service/external-account';
@@ -224,7 +225,7 @@ const checkSync = async (autoGenerateUserOnGroupSync = true) => {
 };
 };
 
 
 describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
 describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
-  let crowi;
+  let crowi: Crowi;
 
 
   beforeAll(async () => {
   beforeAll(async () => {
     crowi = await getInstance();
     crowi = await getInstance();
@@ -245,18 +246,16 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
   afterEach(async () => {
   afterEach(async () => {
     await ExternalUserGroup.deleteMany();
     await ExternalUserGroup.deleteMany();
     await ExternalUserGroupRelation.deleteMany();
     await ExternalUserGroupRelation.deleteMany();
-    await mongoose
-      .model('User')
-      .deleteMany({
-        username: {
-          $in: [
-            'childGroupUser',
-            'parentGroupUser',
-            'grandParentGroupUser',
-            'previouslySyncedGroupUser',
-          ],
-        },
-      });
+    await mongoose.model('User').deleteMany({
+      username: {
+        $in: [
+          'childGroupUser',
+          'parentGroupUser',
+          'grandParentGroupUser',
+          'previouslySyncedGroupUser',
+        ],
+      },
+    });
     await ExternalAccount.deleteMany({
     await ExternalAccount.deleteMany({
       accountId: {
       accountId: {
         $in: [
         $in: [

+ 2 - 1
apps/app/test/integration/service/ldap-user-group-sync.test.ts

@@ -1,13 +1,14 @@
 import ldap, { type Client } from 'ldapjs';
 import ldap, { type Client } from 'ldapjs';
 
 
 import { LdapUserGroupSyncService } from '../../../src/features/external-user-group/server/service/ldap-user-group-sync';
 import { LdapUserGroupSyncService } from '../../../src/features/external-user-group/server/service/ldap-user-group-sync';
+import type Crowi from '../../../src/server/crowi';
 import { configManager } from '../../../src/server/service/config-manager';
 import { configManager } from '../../../src/server/service/config-manager';
 import { ldapService } from '../../../src/server/service/ldap';
 import { ldapService } from '../../../src/server/service/ldap';
 import PassportService from '../../../src/server/service/passport';
 import PassportService from '../../../src/server/service/passport';
 import { getInstance } from '../setup-crowi';
 import { getInstance } from '../setup-crowi';
 
 
 describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
 describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
-  let crowi;
+  let crowi: Crowi;
   let ldapUserGroupSyncService: LdapUserGroupSyncService;
   let ldapUserGroupSyncService: LdapUserGroupSyncService;
 
 
   const configParams = {
   const configParams = {

+ 49 - 51
apps/app/test/integration/service/page-grant.test.ts

@@ -1,11 +1,17 @@
-import { GroupType, PageGrant } from '@growi/core';
+import { GroupType, type IPage, PageGrant } from '@growi/core';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 
 
 import { ExternalGroupProviderType } from '../../../src/features/external-user-group/interfaces/external-user-group';
 import { ExternalGroupProviderType } from '../../../src/features/external-user-group/interfaces/external-user-group';
-import ExternalUserGroup from '../../../src/features/external-user-group/server/models/external-user-group';
+import ExternalUserGroup, {
+  type ExternalUserGroupDocument,
+} from '../../../src/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroupRelation from '../../../src/features/external-user-group/server/models/external-user-group-relation';
 import ExternalUserGroupRelation from '../../../src/features/external-user-group/server/models/external-user-group-relation';
 import { UserGroupPageGrantStatus } from '../../../src/interfaces/page';
 import { UserGroupPageGrantStatus } from '../../../src/interfaces/page';
-import UserGroup from '../../../src/server/models/user-group';
+import type Crowi from '../../../src/server/crowi';
+import type { PageDocument, PageModel } from '../../../src/server/models/page';
+import UserGroup, {
+  type UserGroupDocument,
+} from '../../../src/server/models/user-group';
 import UserGroupRelation from '../../../src/server/models/user-group-relation';
 import UserGroupRelation from '../../../src/server/models/user-group-relation';
 import type { IPageGrantService } from '../../../src/server/service/page-grant';
 import type { IPageGrantService } from '../../../src/server/service/page-grant';
 import { getInstance } from '../setup-crowi';
 import { getInstance } from '../setup-crowi';
@@ -18,43 +24,44 @@ describe('PageGrantService', () => {
   /*
   /*
    * models
    * models
    */
    */
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let User;
   let User;
-  let Page;
+  let Page: PageModel;
 
 
   /*
   /*
    * global instances
    * global instances
    */
    */
-  let crowi;
+  let crowi: Crowi;
   let pageGrantService: IPageGrantService;
   let pageGrantService: IPageGrantService;
 
 
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let user1;
   let user1;
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let user2;
   let user2;
 
 
-  let groupParent;
-  let groupChild;
-  let differentTreeGroup;
+  let groupParent: UserGroupDocument;
+  let groupChild: UserGroupDocument;
+  let differentTreeGroup: UserGroupDocument;
 
 
-  let externalGroupParent;
-  let externalGroupChild;
+  let externalGroupParent: ExternalUserGroupDocument;
+  let externalGroupChild: ExternalUserGroupDocument;
 
 
   const userGroupIdParent = new mongoose.Types.ObjectId();
   const userGroupIdParent = new mongoose.Types.ObjectId();
   const externalUserGroupIdParent = new mongoose.Types.ObjectId();
   const externalUserGroupIdParent = new mongoose.Types.ObjectId();
 
 
-  let rootPage;
-  let rootPublicPage;
-  let rootOnlyMePage;
-  let rootOnlyInsideTheGroup;
-  let emptyPage1;
-  let emptyPage2;
-  let emptyPage3;
+  let rootPage: PageDocument;
+  let rootPublicPage: PageDocument;
+  let rootOnlyMePage: PageDocument;
+  let rootOnlyInsideTheGroup: PageDocument;
+  let emptyPage1: PageDocument;
+  let emptyPage2: PageDocument;
+  let emptyPage3: PageDocument;
   const emptyPagePath1 = '/E1';
   const emptyPagePath1 = '/E1';
   const emptyPagePath2 = '/E2';
   const emptyPagePath2 = '/E2';
   const emptyPagePath3 = '/E3';
   const emptyPagePath3 = '/E3';
 
 
-  let multipleGroupTreesAndUsersPage;
+  let multipleGroupTreesAndUsersPage: PageDocument;
 
 
-  let pageRootPublic;
-  let pageRootGroupParent;
   const pageRootPublicPath = '/Public';
   const pageRootPublicPath = '/Public';
   const pageRootGroupParentPath = '/GroupParent';
   const pageRootGroupParentPath = '/GroupParent';
   const pageMultipleGroupTreesAndUsersPath = '/MultipleGroupTreesAndUsers';
   const pageMultipleGroupTreesAndUsersPath = '/MultipleGroupTreesAndUsers';
@@ -74,11 +81,6 @@ describe('PageGrantService', () => {
   const pageOnlyInsideTheGroupPublicPath = `${v4PageRootOnlyInsideTheGroupPagePath}/Public`;
   const pageOnlyInsideTheGroupPublicPath = `${v4PageRootOnlyInsideTheGroupPagePath}/Public`;
   const pageOnlyInsideTheGroupOnlyMePath = `${v4PageRootOnlyInsideTheGroupPagePath}/OnlyMe`;
   const pageOnlyInsideTheGroupOnlyMePath = `${v4PageRootOnlyInsideTheGroupPagePath}/OnlyMe`;
   const pageOnlyInsideTheGroupAnyoneWithTheLinkPath = `${v4PageRootOnlyInsideTheGroupPagePath}/AnyoneWithTheLink`;
   const pageOnlyInsideTheGroupAnyoneWithTheLinkPath = `${v4PageRootOnlyInsideTheGroupPagePath}/AnyoneWithTheLink`;
-  let pageE1Public;
-  let pageE2User1;
-  let pageE3GroupParent;
-  let pageE3GroupChild;
-  let pageE3User1;
   const pageE1PublicPath = '/E1/Public';
   const pageE1PublicPath = '/E1/Public';
   const pageE2User1Path = '/E2/User1';
   const pageE2User1Path = '/E2/User1';
   const pageE3GroupParentPath = '/E3/GroupParent';
   const pageE3GroupParentPath = '/E3/GroupParent';
@@ -86,12 +88,13 @@ describe('PageGrantService', () => {
   const pageE3User1Path = '/E3/User1';
   const pageE3User1Path = '/E3/User1';
 
 
   // getPageGroupGrantData test data
   // getPageGroupGrantData test data
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let user3;
   let user3;
-  let groupGrantDataTestChildPagePath;
-  let groupGrantDataTestParentUserGroupId;
-  let groupGrantDataTestChildUserGroupId;
-  let groupGrantDataTestExternalUserGroupId;
-  let groupGrantDataTestExternalUserGroupId2;
+  let groupGrantDataTestChildPagePath: string;
+  let groupGrantDataTestParentUserGroupId: mongoose.Types.ObjectId;
+  let groupGrantDataTestChildUserGroupId: mongoose.Types.ObjectId;
+  let groupGrantDataTestExternalUserGroupId: mongoose.Types.ObjectId;
+  let groupGrantDataTestExternalUserGroupId2: mongoose.Types.ObjectId;
 
 
   const createDocumentsToTestIsGrantNormalized = async () => {
   const createDocumentsToTestIsGrantNormalized = async () => {
     // Users
     // Users
@@ -119,11 +122,11 @@ describe('PageGrantService', () => {
       },
       },
     ]);
     ]);
 
 
-    groupParent = await UserGroup.findOne({ name: 'GroupParent' });
-    groupChild = await UserGroup.findOne({ name: 'GroupChild' });
-    differentTreeGroup = await UserGroup.findOne({
+    groupParent = (await UserGroup.findOne({ name: 'GroupParent' }))!;
+    groupChild = (await UserGroup.findOne({ name: 'GroupChild' }))!;
+    differentTreeGroup = (await UserGroup.findOne({
       name: 'DifferentTreeGroup',
       name: 'DifferentTreeGroup',
-    });
+    }))!;
 
 
     // UserGroupRelations
     // UserGroupRelations
     await UserGroupRelation.insertMany([
     await UserGroupRelation.insertMany([
@@ -184,7 +187,7 @@ describe('PageGrantService', () => {
     ]);
     ]);
 
 
     // Root page (Depth: 0)
     // Root page (Depth: 0)
-    rootPage = await Page.findOne({ path: '/' });
+    rootPage = (await Page.findOne({ path: '/' }))!;
 
 
     // Empty pages (Depth: 1)
     // Empty pages (Depth: 1)
     await Page.insertMany([
     await Page.insertMany([
@@ -241,9 +244,9 @@ describe('PageGrantService', () => {
       },
       },
     ]);
     ]);
 
 
-    multipleGroupTreesAndUsersPage = await Page.findOne({
+    multipleGroupTreesAndUsersPage = (await Page.findOne({
       path: pageMultipleGroupTreesAndUsersPath,
       path: pageMultipleGroupTreesAndUsersPath,
-    });
+    }))!;
 
 
     await Page.insertMany([
     await Page.insertMany([
       // Root Page
       // Root Page
@@ -277,11 +280,11 @@ describe('PageGrantService', () => {
       },
       },
     ]);
     ]);
 
 
-    rootPublicPage = await Page.findOne({ path: pageRootPublicPath });
-    rootOnlyMePage = await Page.findOne({ path: v4PageRootOnlyMePagePath });
-    rootOnlyInsideTheGroup = await Page.findOne({
+    rootPublicPage = (await Page.findOne({ path: pageRootPublicPath }))!;
+    rootOnlyMePage = (await Page.findOne({ path: v4PageRootOnlyMePagePath }))!;
+    rootOnlyInsideTheGroup = (await Page.findOne({
       path: v4PageRootOnlyInsideTheGroupPagePath,
       path: v4PageRootOnlyInsideTheGroupPagePath,
-    });
+    }))!;
 
 
     // Leaf pages (Depth: 2)
     // Leaf pages (Depth: 2)
     await Page.insertMany([
     await Page.insertMany([
@@ -341,9 +344,9 @@ describe('PageGrantService', () => {
       },
       },
     ]);
     ]);
 
 
-    emptyPage1 = await Page.findOne({ path: emptyPagePath1 });
-    emptyPage2 = await Page.findOne({ path: emptyPagePath2 });
-    emptyPage3 = await Page.findOne({ path: emptyPagePath3 });
+    emptyPage1 = (await Page.findOne({ path: emptyPagePath1 }))!;
+    emptyPage2 = (await Page.findOne({ path: emptyPagePath2 }))!;
+    emptyPage3 = (await Page.findOne({ path: emptyPagePath3 }))!;
 
 
     // Leaf pages (Depth: 2)
     // Leaf pages (Depth: 2)
     await Page.insertMany([
     await Page.insertMany([
@@ -399,11 +402,6 @@ describe('PageGrantService', () => {
         parent: emptyPage3._id,
         parent: emptyPage3._id,
       },
       },
     ]);
     ]);
-    pageE1Public = await Page.findOne({ path: pageE1PublicPath });
-    pageE2User1 = await Page.findOne({ path: pageE2User1Path });
-    pageE3GroupParent = await Page.findOne({ path: pageE3GroupParentPath });
-    pageE3GroupChild = await Page.findOne({ path: pageE3GroupChildPath });
-    pageE3User1 = await Page.findOne({ path: pageE3User1Path });
   };
   };
 
 
   const createDocumentsToTestGetPageGroupGrantData = async () => {
   const createDocumentsToTestGetPageGroupGrantData = async () => {
@@ -522,9 +520,9 @@ describe('PageGrantService', () => {
     pageGrantService = crowi.pageGrantService;
     pageGrantService = crowi.pageGrantService;
 
 
     User = mongoose.model('User');
     User = mongoose.model('User');
-    Page = mongoose.model('Page');
+    Page = mongoose.model<IPage, PageModel>('Page');
 
 
-    rootPage = await Page.findOne({ path: '/' });
+    rootPage = (await Page.findOne({ path: '/' }))!;
 
 
     await createDocumentsToTestIsGrantNormalized();
     await createDocumentsToTestIsGrantNormalized();
     await createDocumentsToTestGetPageGroupGrantData();
     await createDocumentsToTestGetPageGroupGrantData();

+ 4 - 1
apps/app/test/integration/service/user-groups.test.ts

@@ -2,6 +2,7 @@ import type { IGrantedGroup } from '@growi/core';
 import { GroupType, getIdForRef, type IPage, PageGrant } from '@growi/core';
 import { GroupType, getIdForRef, type IPage, PageGrant } from '@growi/core';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 import { PageActionOnGroupDelete } from '../../../src/interfaces/user-group';
 import { PageActionOnGroupDelete } from '../../../src/interfaces/user-group';
+import type Crowi from '../../../src/server/crowi';
 import type { PageDocument, PageModel } from '../../../src/server/models/page';
 import type { PageDocument, PageModel } from '../../../src/server/models/page';
 import UserGroup from '../../../src/server/models/user-group';
 import UserGroup from '../../../src/server/models/user-group';
 import UserGroupRelation from '../../../src/server/models/user-group-relation';
 import UserGroupRelation from '../../../src/server/models/user-group-relation';
@@ -9,7 +10,8 @@ import type { IUserGroupService } from '../../../src/server/service/user-group';
 import { getInstance } from '../setup-crowi';
 import { getInstance } from '../setup-crowi';
 
 
 describe('UserGroupService', () => {
 describe('UserGroupService', () => {
-  let crowi;
+  let crowi: Crowi;
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let User;
   let User;
   let Page: PageModel;
   let Page: PageModel;
 
 
@@ -32,6 +34,7 @@ describe('UserGroupService', () => {
   const groupId15 = new mongoose.Types.ObjectId();
   const groupId15 = new mongoose.Types.ObjectId();
 
 
   const userId1 = new mongoose.Types.ObjectId();
   const userId1 = new mongoose.Types.ObjectId();
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let user1;
   let user1;
 
 
   const pageId1 = new mongoose.Types.ObjectId();
   const pageId1 = new mongoose.Types.ObjectId();

+ 174 - 156
apps/app/test/integration/service/v5.non-public-page.test.ts

@@ -1,12 +1,20 @@
 /* eslint-disable no-unused-vars */
 /* eslint-disable no-unused-vars */
-import { GroupType, type IGrantedGroup } from '@growi/core';
+import {
+  GroupType,
+  type IGrantedGroup,
+  type IPage,
+  type IRevision,
+} from '@growi/core';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 
 
 import { ExternalGroupProviderType } from '../../../src/features/external-user-group/interfaces/external-user-group';
 import { ExternalGroupProviderType } from '../../../src/features/external-user-group/interfaces/external-user-group';
 import ExternalUserGroup from '../../../src/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroup from '../../../src/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroupRelation from '../../../src/features/external-user-group/server/models/external-user-group-relation';
 import ExternalUserGroupRelation from '../../../src/features/external-user-group/server/models/external-user-group-relation';
 import type { IPageTagRelation } from '../../../src/interfaces/page-tag-relation';
 import type { IPageTagRelation } from '../../../src/interfaces/page-tag-relation';
+import type Crowi from '../../../src/server/crowi';
+import type { PageDocument, PageModel } from '../../../src/server/models/page';
 import PageTagRelation from '../../../src/server/models/page-tag-relation';
 import PageTagRelation from '../../../src/server/models/page-tag-relation';
+import type { IRevisionModel } from '../../../src/server/models/revision';
 import Tag from '../../../src/server/models/tag';
 import Tag from '../../../src/server/models/tag';
 import UserGroup from '../../../src/server/models/user-group';
 import UserGroup from '../../../src/server/models/user-group';
 import UserGroupRelation from '../../../src/server/models/user-group-relation';
 import UserGroupRelation from '../../../src/server/models/user-group-relation';
@@ -14,26 +22,32 @@ import { generalXssFilter } from '../../../src/services/general-xss-filter';
 import { getInstance } from '../setup-crowi';
 import { getInstance } from '../setup-crowi';
 
 
 describe('PageService page operations with non-public pages', () => {
 describe('PageService page operations with non-public pages', () => {
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let dummyUser1;
   let dummyUser1;
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let dummyUser2;
   let dummyUser2;
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let npDummyUser1;
   let npDummyUser1;
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let npDummyUser2;
   let npDummyUser2;
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let npDummyUser3;
   let npDummyUser3;
-  let groupIdIsolate;
-  let groupIdA;
-  let groupIdB;
-  let groupIdC;
-  let externalGroupIdIsolate;
-  let externalGroupIdA;
-  let externalGroupIdB;
-  let externalGroupIdC;
-  let crowi;
-  let Page;
-  let Revision;
+  let groupIdIsolate: mongoose.Types.ObjectId;
+  let groupIdA: mongoose.Types.ObjectId;
+  let groupIdB: mongoose.Types.ObjectId;
+  let groupIdC: mongoose.Types.ObjectId;
+  let externalGroupIdIsolate: mongoose.Types.ObjectId;
+  let externalGroupIdA: mongoose.Types.ObjectId;
+  let externalGroupIdB: mongoose.Types.ObjectId;
+  let externalGroupIdC: mongoose.Types.ObjectId;
+  let crowi: Crowi;
+  let Page: PageModel;
+  let Revision: IRevisionModel;
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let User;
   let User;
   let generalXssFilterProcessSpy;
   let generalXssFilterProcessSpy;
 
 
-  let rootPage;
+  let rootPage: PageDocument;
 
 
   /**
   /**
    * Rename
    * Rename
@@ -115,8 +129,10 @@ describe('PageService page operations with non-public pages', () => {
   };
   };
 
 
   // normalize for result comparison
   // normalize for result comparison
-  const normalizeGrantedGroups = (grantedGroups: IGrantedGroup[]) => {
-    return grantedGroups.map((group) => {
+  const normalizeGrantedGroups = (
+    grantedGroups: IGrantedGroup[] | undefined,
+  ) => {
+    return grantedGroups?.map((group) => {
       const itemId =
       const itemId =
         typeof group.item === 'string' ? group.item : group.item._id;
         typeof group.item === 'string' ? group.item : group.item._id;
       return { item: itemId, type: group.type };
       return { item: itemId, type: group.type };
@@ -128,8 +144,8 @@ describe('PageService page operations with non-public pages', () => {
     await crowi.configManager.updateConfig('app:isV5Compatible', true);
     await crowi.configManager.updateConfig('app:isV5Compatible', true);
 
 
     User = mongoose.model('User');
     User = mongoose.model('User');
-    Page = mongoose.model('Page');
-    Revision = mongoose.model('Revision');
+    Page = mongoose.model<IPage, PageModel>('Page');
+    Revision = mongoose.model<IRevision, IRevisionModel>('Revision');
 
 
     /*
     /*
      * Common
      * Common
@@ -315,7 +331,7 @@ describe('PageService page operations with non-public pages', () => {
     npDummyUser2 = await User.findOne({ username: 'npUser2' });
     npDummyUser2 = await User.findOne({ username: 'npUser2' });
     npDummyUser3 = await User.findOne({ username: 'npUser3' });
     npDummyUser3 = await User.findOne({ username: 'npUser3' });
 
 
-    rootPage = await Page.findOne({ path: '/' });
+    rootPage = (await Page.findOne({ path: '/' }))!;
     if (rootPage == null) {
     if (rootPage == null) {
       const pages = await Page.insertMany([
       const pages = await Page.insertMany([
         { path: '/', grant: Page.GRANT_PUBLIC },
         { path: '/', grant: Page.GRANT_PUBLIC },
@@ -1023,7 +1039,7 @@ describe('PageService page operations with non-public pages', () => {
         expect(_page1).toBeTruthy();
         expect(_page1).toBeTruthy();
         expect(_page2).toBeTruthy();
         expect(_page2).toBeTruthy();
         expect(_page3).toBeTruthy();
         expect(_page3).toBeTruthy();
-        expect(_pageT.descendantCount).toBe(1);
+        expect(_pageT?.descendantCount).toBe(1);
         // isGrantNormalized is not called when GRANT RESTRICTED
         // isGrantNormalized is not called when GRANT RESTRICTED
         expect(isGrantNormalizedSpy).toBeCalledTimes(0);
         expect(isGrantNormalizedSpy).toBeCalledTimes(0);
       });
       });
@@ -1072,8 +1088,8 @@ describe('PageService page operations with non-public pages', () => {
         expect(_page1).toBeTruthy();
         expect(_page1).toBeTruthy();
         expect(_page2).toBeTruthy();
         expect(_page2).toBeTruthy();
         expect(_pageN).toBeTruthy();
         expect(_pageN).toBeTruthy();
-        expect(_pageN.parent).toStrictEqual(_page2._id);
-        expect(_pageT.descendantCount).toStrictEqual(1);
+        expect(_pageN?.parent).toStrictEqual(_page2?._id);
+        expect(_pageT?.descendantCount).toStrictEqual(1);
         // isGrantNormalized is called when GRANT PUBLIC
         // isGrantNormalized is called when GRANT PUBLIC
         expect(isGrantNormalizedSpy).toBeCalledTimes(1);
         expect(isGrantNormalizedSpy).toBeCalledTimes(1);
       });
       });
@@ -1098,9 +1114,9 @@ describe('PageService page operations with non-public pages', () => {
           }); // newly crated
           }); // newly crated
           expect(_pageT).toBeTruthy();
           expect(_pageT).toBeTruthy();
           expect(_pageN).toBeTruthy();
           expect(_pageN).toBeTruthy();
-          expect(_pageN.parent).toStrictEqual(_pageT._id);
-          expect(_pageT.descendantCount).toStrictEqual(1);
-          expect(normalizeGrantedGroups(_pageN.grantedGroups)).toStrictEqual([
+          expect(_pageN?.parent).toStrictEqual(_pageT?._id);
+          expect(_pageT?.descendantCount).toStrictEqual(1);
+          expect(normalizeGrantedGroups(_pageN?.grantedGroups)).toStrictEqual([
             { item: groupIdIsolate, type: GroupType.userGroup },
             { item: groupIdIsolate, type: GroupType.userGroup },
           ]);
           ]);
         });
         });
@@ -1125,9 +1141,9 @@ describe('PageService page operations with non-public pages', () => {
           }); // newly crated
           }); // newly crated
           expect(_pageT).toBeTruthy();
           expect(_pageT).toBeTruthy();
           expect(_pageN).toBeTruthy();
           expect(_pageN).toBeTruthy();
-          expect(_pageN.parent).toStrictEqual(_pageT._id);
-          expect(_pageT.descendantCount).toStrictEqual(2);
-          expect(normalizeGrantedGroups(_pageN.grantedGroups)).toStrictEqual([
+          expect(_pageN?.parent).toStrictEqual(_pageT?._id);
+          expect(_pageT?.descendantCount).toStrictEqual(2);
+          expect(normalizeGrantedGroups(_pageN?.grantedGroups)).toStrictEqual([
             { item: groupIdIsolate, type: GroupType.userGroup },
             { item: groupIdIsolate, type: GroupType.userGroup },
             { item: groupIdB, type: GroupType.userGroup },
             { item: groupIdB, type: GroupType.userGroup },
           ]);
           ]);
@@ -1180,7 +1196,7 @@ describe('PageService page operations with non-public pages', () => {
         expect(_page1).toBeTruthy();
         expect(_page1).toBeTruthy();
         expect(_page2).toBeTruthy();
         expect(_page2).toBeTruthy();
         expect(_page3).toBeTruthy();
         expect(_page3).toBeTruthy();
-        expect(_pageT.descendantCount).toBe(1);
+        expect(_pageT?.descendantCount).toBe(1);
         // isGrantNormalized is not called when create by ststem
         // isGrantNormalized is not called when create by ststem
         expect(isGrantNormalizedSpy).toBeCalledTimes(0);
         expect(isGrantNormalizedSpy).toBeCalledTimes(0);
       });
       });
@@ -1229,8 +1245,8 @@ describe('PageService page operations with non-public pages', () => {
         expect(_page1).toBeTruthy();
         expect(_page1).toBeTruthy();
         expect(_page2).toBeTruthy();
         expect(_page2).toBeTruthy();
         expect(_pageN).toBeTruthy();
         expect(_pageN).toBeTruthy();
-        expect(_pageN.parent).toStrictEqual(_page2._id);
-        expect(_pageT.descendantCount).toStrictEqual(1);
+        expect(_pageN?.parent).toStrictEqual(_page2?._id);
+        expect(_pageT?.descendantCount).toStrictEqual(1);
         // isGrantNormalized is not called when create by ststem
         // isGrantNormalized is not called when create by ststem
         expect(isGrantNormalizedSpy).toBeCalledTimes(0);
         expect(isGrantNormalizedSpy).toBeCalledTimes(0);
       });
       });
@@ -1291,7 +1307,7 @@ describe('PageService page operations with non-public pages', () => {
       const _page3 = await Page.findOne({
       const _page3 = await Page.findOne({
         path: _path3,
         path: _path3,
         ..._properties3,
         ..._properties3,
-        parent: _page2._id,
+        parent: _page2?._id,
       });
       });
       expect(_pageD).toBeTruthy();
       expect(_pageD).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page2).toBeTruthy();
@@ -1316,7 +1332,7 @@ describe('PageService page operations with non-public pages', () => {
       const page3 = await Page.findOne({
       const page3 = await Page.findOne({
         path: _path3,
         path: _path3,
         ..._properties3,
         ..._properties3,
-        parent: _page2._id,
+        parent: _page2?._id,
       }); // not exist
       }); // not exist
       const page2Renamed = await Page.findOne({ path: newPathForPage2 }); // renamed
       const page2Renamed = await Page.findOne({ path: newPathForPage2 }); // renamed
       const page3Renamed = await Page.findOne({ path: newPathForPage3 }); // renamed
       const page3Renamed = await Page.findOne({ path: newPathForPage3 }); // renamed
@@ -1325,13 +1341,13 @@ describe('PageService page operations with non-public pages', () => {
       expect(page3).toBeNull();
       expect(page3).toBeNull();
       expect(page2Renamed).toBeTruthy();
       expect(page2Renamed).toBeTruthy();
       expect(page3Renamed).toBeTruthy();
       expect(page3Renamed).toBeTruthy();
-      expect(page2Renamed.parent).toStrictEqual(_pageD._id);
-      expect(page3Renamed.parent).toStrictEqual(page2Renamed._id);
-      expect(normalizeGrantedGroups(page2Renamed.grantedGroups)).toStrictEqual(
-        normalizeGrantedGroups(_page2.grantedGroups),
+      expect(page2Renamed?.parent).toStrictEqual(_pageD?._id);
+      expect(page3Renamed?.parent).toStrictEqual(page2Renamed?._id);
+      expect(normalizeGrantedGroups(page2Renamed?.grantedGroups)).toStrictEqual(
+        normalizeGrantedGroups(_page2?.grantedGroups),
       );
       );
-      expect(normalizeGrantedGroups(page3Renamed.grantedGroups)).toStrictEqual(
-        normalizeGrantedGroups(_page3.grantedGroups),
+      expect(normalizeGrantedGroups(page3Renamed?.grantedGroups)).toStrictEqual(
+        normalizeGrantedGroups(_page3?.grantedGroups),
       );
       );
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
     });
     });
@@ -1433,7 +1449,7 @@ describe('PageService page operations with non-public pages', () => {
       expect(page3).toBeTruthy();
       expect(page3).toBeTruthy();
       expect(page2Renamed).toBeTruthy();
       expect(page2Renamed).toBeTruthy();
       expect(page3Renamed).toBeNull();
       expect(page3Renamed).toBeNull();
-      expect(page2Renamed.parent).toBeNull();
+      expect(page2Renamed?.parent).toBeNull();
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
     });
     });
   });
   });
@@ -1478,7 +1494,7 @@ describe('PageService page operations with non-public pages', () => {
         path: '/np_duplicate1',
         path: '/np_duplicate1',
         grant: Page.GRANT_RESTRICTED,
         grant: Page.GRANT_RESTRICTED,
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const _revision = _page.revision;
+      const _revision = _page?.revision;
       expect(_page).toBeTruthy();
       expect(_page).toBeTruthy();
       expect(_revision).toBeTruthy();
       expect(_revision).toBeTruthy();
 
 
@@ -1487,16 +1503,16 @@ describe('PageService page operations with non-public pages', () => {
 
 
       const duplicatedPage = await Page.findOne({ path: newPagePath });
       const duplicatedPage = await Page.findOne({ path: newPagePath });
       const duplicatedRevision = await Revision.findOne({
       const duplicatedRevision = await Revision.findOne({
-        pageId: duplicatedPage._id,
+        pageId: duplicatedPage?._id,
       });
       });
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(duplicatedPage).toBeTruthy();
       expect(duplicatedPage).toBeTruthy();
-      expect(duplicatedPage._id).not.toStrictEqual(_page._id);
-      expect(duplicatedPage.grant).toBe(_page.grant);
-      expect(duplicatedPage.parent).toBeNull();
-      expect(duplicatedPage.parent).toStrictEqual(_page.parent);
-      expect(duplicatedPage.revision).toStrictEqual(duplicatedRevision._id);
-      expect(duplicatedRevision.body).toBe(_revision.body);
+      expect(duplicatedPage?._id).not.toStrictEqual(_page?._id);
+      expect(duplicatedPage?.grant).toBe(_page?.grant);
+      expect(duplicatedPage?.parent).toBeNull();
+      expect(duplicatedPage?.parent).toStrictEqual(_page?.parent);
+      expect(duplicatedPage?.revision).toStrictEqual(duplicatedRevision?._id);
+      expect(duplicatedRevision?.body).toBe(_revision?.body);
     });
     });
 
 
     test('Should duplicate multiple pages with GRANT_USER_GROUP', async () => {
     test('Should duplicate multiple pages with GRANT_USER_GROUP', async () => {
@@ -1509,11 +1525,11 @@ describe('PageService page operations with non-public pages', () => {
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
       const _page2 = await Page.findOne({
       const _page2 = await Page.findOne({
         path: _path2,
         path: _path2,
-        parent: _page1._id,
+        parent: _page1?._id,
         grantedGroups: { $elemMatch: { item: groupIdB } },
         grantedGroups: { $elemMatch: { item: groupIdB } },
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const _revision1 = _page1.revision;
-      const _revision2 = _page2.revision;
+      const _revision1 = _page1?.revision;
+      const _revision2 = _page2?.revision;
       expect(_page1).toBeTruthy();
       expect(_page1).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_revision1).toBeTruthy();
       expect(_revision1).toBeTruthy();
@@ -1528,31 +1544,31 @@ describe('PageService page operations with non-public pages', () => {
       const duplicatedPage2 = await Page.findOne({
       const duplicatedPage2 = await Page.findOne({
         path: '/dup_np_duplicate2/np_duplicate3',
         path: '/dup_np_duplicate2/np_duplicate3',
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const duplicatedRevision1 = duplicatedPage1.revision;
-      const duplicatedRevision2 = duplicatedPage2.revision;
+      const duplicatedRevision1 = duplicatedPage1?.revision;
+      const duplicatedRevision2 = duplicatedPage2?.revision;
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(duplicatedPage1).toBeTruthy();
       expect(duplicatedPage1).toBeTruthy();
       expect(duplicatedPage2).toBeTruthy();
       expect(duplicatedPage2).toBeTruthy();
       expect(duplicatedRevision1).toBeTruthy();
       expect(duplicatedRevision1).toBeTruthy();
       expect(duplicatedRevision2).toBeTruthy();
       expect(duplicatedRevision2).toBeTruthy();
       expect(
       expect(
-        normalizeGrantedGroups(duplicatedPage1.grantedGroups),
+        normalizeGrantedGroups(duplicatedPage1?.grantedGroups),
       ).toStrictEqual([
       ).toStrictEqual([
         { item: groupIdA, type: GroupType.userGroup },
         { item: groupIdA, type: GroupType.userGroup },
         { item: externalGroupIdA, type: GroupType.externalUserGroup },
         { item: externalGroupIdA, type: GroupType.externalUserGroup },
       ]);
       ]);
       expect(
       expect(
-        normalizeGrantedGroups(duplicatedPage2.grantedGroups),
+        normalizeGrantedGroups(duplicatedPage2?.grantedGroups),
       ).toStrictEqual([
       ).toStrictEqual([
         { item: groupIdB, type: GroupType.userGroup },
         { item: groupIdB, type: GroupType.userGroup },
         { item: externalGroupIdB, type: GroupType.externalUserGroup },
         { item: externalGroupIdB, type: GroupType.externalUserGroup },
       ]);
       ]);
-      expect(duplicatedPage1.parent).toStrictEqual(_page1.parent);
-      expect(duplicatedPage2.parent).toStrictEqual(duplicatedPage1._id);
-      expect(duplicatedRevision1.body).toBe(_revision1.body);
-      expect(duplicatedRevision2.body).toBe(_revision2.body);
-      expect(duplicatedRevision1.pageId).toStrictEqual(duplicatedPage1._id);
-      expect(duplicatedRevision2.pageId).toStrictEqual(duplicatedPage2._id);
+      expect(duplicatedPage1?.parent).toStrictEqual(_page1?.parent);
+      expect(duplicatedPage2?.parent).toStrictEqual(duplicatedPage1?._id);
+      expect(duplicatedRevision1?.body).toBe(_revision1?.body);
+      expect(duplicatedRevision2?.body).toBe(_revision2?.body);
+      expect(duplicatedRevision1?.pageId).toStrictEqual(duplicatedPage1?._id);
+      expect(duplicatedRevision2?.pageId).toStrictEqual(duplicatedPage2?._id);
     });
     });
     test('Should duplicate multiple pages. Page with GRANT_RESTRICTED should NOT be duplicated', async () => {
     test('Should duplicate multiple pages. Page with GRANT_RESTRICTED should NOT be duplicated', async () => {
       const _path1 = '/np_duplicate4';
       const _path1 = '/np_duplicate4';
@@ -1571,9 +1587,9 @@ describe('PageService page operations with non-public pages', () => {
         path: _path3,
         path: _path3,
         grant: Page.GRANT_PUBLIC,
         grant: Page.GRANT_PUBLIC,
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const baseRevision1 = _page1.revision;
-      const baseRevision2 = _page2.revision;
-      const baseRevision3 = _page3.revision;
+      const baseRevision1 = _page1?.revision;
+      const baseRevision2 = _page2?.revision;
+      const baseRevision3 = _page3?.revision;
       expect(_page1).toBeTruthy();
       expect(_page1).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page3).toBeTruthy();
       expect(_page3).toBeTruthy();
@@ -1592,22 +1608,22 @@ describe('PageService page operations with non-public pages', () => {
       const duplicatedPage3 = await Page.findOne({
       const duplicatedPage3 = await Page.findOne({
         path: '/dup_np_duplicate4/np_duplicate6',
         path: '/dup_np_duplicate4/np_duplicate6',
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const duplicatedRevision1 = duplicatedPage1.revision;
-      const duplicatedRevision3 = duplicatedPage3.revision;
+      const duplicatedRevision1 = duplicatedPage1?.revision;
+      const duplicatedRevision3 = duplicatedPage3?.revision;
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(duplicatedPage1).toBeTruthy();
       expect(duplicatedPage1).toBeTruthy();
       expect(duplicatedPage2).toBeNull();
       expect(duplicatedPage2).toBeNull();
       expect(duplicatedPage3).toBeTruthy();
       expect(duplicatedPage3).toBeTruthy();
       expect(duplicatedRevision1).toBeTruthy();
       expect(duplicatedRevision1).toBeTruthy();
       expect(duplicatedRevision3).toBeTruthy();
       expect(duplicatedRevision3).toBeTruthy();
-      expect(duplicatedPage1.grant).toStrictEqual(Page.GRANT_PUBLIC);
-      expect(duplicatedPage3.grant).toStrictEqual(Page.GRANT_PUBLIC);
-      expect(duplicatedPage1.parent).toStrictEqual(_page1.parent);
-      expect(duplicatedPage3.parent).toStrictEqual(duplicatedPage1._id);
-      expect(duplicatedRevision1.body).toBe(baseRevision1.body);
-      expect(duplicatedRevision3.body).toBe(baseRevision3.body);
-      expect(duplicatedRevision1.pageId).toStrictEqual(duplicatedPage1._id);
-      expect(duplicatedRevision3.pageId).toStrictEqual(duplicatedPage3._id);
+      expect(duplicatedPage1?.grant).toStrictEqual(Page.GRANT_PUBLIC);
+      expect(duplicatedPage3?.grant).toStrictEqual(Page.GRANT_PUBLIC);
+      expect(duplicatedPage1?.parent).toStrictEqual(_page1?.parent);
+      expect(duplicatedPage3?.parent).toStrictEqual(duplicatedPage1?._id);
+      expect(duplicatedRevision1?.body).toBe(baseRevision1?.body);
+      expect(duplicatedRevision3?.body).toBe(baseRevision3?.body);
+      expect(duplicatedRevision1?.pageId).toStrictEqual(duplicatedPage1?._id);
+      expect(duplicatedRevision3?.pageId).toStrictEqual(duplicatedPage3?._id);
     });
     });
     test('Should duplicate only user related pages and granted groups when onlyDuplicateUserRelatedResources is true', async () => {
     test('Should duplicate only user related pages and granted groups when onlyDuplicateUserRelatedResources is true', async () => {
       const _path1 = '/np_duplicate7';
       const _path1 = '/np_duplicate7';
@@ -1617,9 +1633,9 @@ describe('PageService page operations with non-public pages', () => {
         path: _path1,
         path: _path1,
         parent: rootPage._id,
         parent: rootPage._id,
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const _page2 = await Page.findOne({ path: _path2, parent: _page1._id });
-      const _page3 = await Page.findOne({ path: _path3, parent: _page1._id });
-      const _revision1 = _page1.revision;
+      const _page2 = await Page.findOne({ path: _path2, parent: _page1?._id });
+      const _page3 = await Page.findOne({ path: _path3, parent: _page1?._id });
+      const _revision1 = _page1?.revision;
       expect(_page1).toBeTruthy();
       expect(_page1).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page3).toBeTruthy();
       expect(_page3).toBeTruthy();
@@ -1637,21 +1653,21 @@ describe('PageService page operations with non-public pages', () => {
       const duplicatedPage3 = await Page.findOne({
       const duplicatedPage3 = await Page.findOne({
         path: '/dup_np_duplicate7/np_duplicate9',
         path: '/dup_np_duplicate7/np_duplicate9',
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const duplicatedRevision1 = duplicatedPage1.revision;
+      const duplicatedRevision1 = duplicatedPage1?.revision;
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(duplicatedPage1).toBeTruthy();
       expect(duplicatedPage1).toBeTruthy();
       expect(duplicatedPage2).toBeFalsy();
       expect(duplicatedPage2).toBeFalsy();
       expect(duplicatedPage3).toBeFalsy();
       expect(duplicatedPage3).toBeFalsy();
       expect(duplicatedRevision1).toBeTruthy();
       expect(duplicatedRevision1).toBeTruthy();
       expect(
       expect(
-        normalizeGrantedGroups(duplicatedPage1.grantedGroups),
+        normalizeGrantedGroups(duplicatedPage1?.grantedGroups),
       ).toStrictEqual([
       ).toStrictEqual([
         { item: groupIdA, type: GroupType.userGroup },
         { item: groupIdA, type: GroupType.userGroup },
         { item: externalGroupIdA, type: GroupType.externalUserGroup },
         { item: externalGroupIdA, type: GroupType.externalUserGroup },
       ]);
       ]);
-      expect(duplicatedPage1.parent).toStrictEqual(_page1.parent);
-      expect(duplicatedRevision1.body).toBe(_revision1.body);
-      expect(duplicatedRevision1.pageId).toStrictEqual(duplicatedPage1._id);
+      expect(duplicatedPage1?.parent).toStrictEqual(_page1?.parent);
+      expect(duplicatedRevision1?.body).toBe(_revision1?.body);
+      expect(duplicatedRevision1?.pageId).toStrictEqual(duplicatedPage1?._id);
     });
     });
     test('Should duplicate all pages and granted groups when onlyDuplicateUserRelatedResources is false', async () => {
     test('Should duplicate all pages and granted groups when onlyDuplicateUserRelatedResources is false', async () => {
       const _path1 = '/np_duplicate7';
       const _path1 = '/np_duplicate7';
@@ -1663,15 +1679,15 @@ describe('PageService page operations with non-public pages', () => {
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
       const _page2 = await Page.findOne({
       const _page2 = await Page.findOne({
         path: _path2,
         path: _path2,
-        parent: _page1._id,
+        parent: _page1?._id,
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
       const _page3 = await Page.findOne({
       const _page3 = await Page.findOne({
         path: _path3,
         path: _path3,
-        parent: _page1._id,
+        parent: _page1?._id,
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const _revision1 = _page1.revision;
-      const _revision2 = _page2.revision;
-      const _revision3 = _page3.revision;
+      const _revision1 = _page1?.revision;
+      const _revision2 = _page2?.revision;
+      const _revision3 = _page3?.revision;
       expect(_page1).toBeTruthy();
       expect(_page1).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page3).toBeTruthy();
       expect(_page3).toBeTruthy();
@@ -1691,9 +1707,9 @@ describe('PageService page operations with non-public pages', () => {
       const duplicatedPage3 = await Page.findOne({
       const duplicatedPage3 = await Page.findOne({
         path: '/dup2_np_duplicate7/np_duplicate9',
         path: '/dup2_np_duplicate7/np_duplicate9',
       }).populate({ path: 'revision', model: 'Revision' });
       }).populate({ path: 'revision', model: 'Revision' });
-      const duplicatedRevision1 = duplicatedPage1.revision;
-      const duplicatedRevision2 = duplicatedPage2.revision;
-      const duplicatedRevision3 = duplicatedPage3.revision;
+      const duplicatedRevision1 = duplicatedPage1?.revision;
+      const duplicatedRevision2 = duplicatedPage2?.revision;
+      const duplicatedRevision3 = duplicatedPage3?.revision;
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(generalXssFilterProcessSpy).toHaveBeenCalled();
       expect(duplicatedPage1).toBeTruthy();
       expect(duplicatedPage1).toBeTruthy();
       expect(duplicatedPage2).toBeTruthy();
       expect(duplicatedPage2).toBeTruthy();
@@ -1702,29 +1718,29 @@ describe('PageService page operations with non-public pages', () => {
       expect(duplicatedRevision2).toBeTruthy();
       expect(duplicatedRevision2).toBeTruthy();
       expect(duplicatedRevision3).toBeTruthy();
       expect(duplicatedRevision3).toBeTruthy();
       expect(
       expect(
-        normalizeGrantedGroups(duplicatedPage1.grantedGroups),
+        normalizeGrantedGroups(duplicatedPage1?.grantedGroups),
       ).toStrictEqual([
       ).toStrictEqual([
         { item: groupIdA, type: GroupType.userGroup },
         { item: groupIdA, type: GroupType.userGroup },
         { item: externalGroupIdA, type: GroupType.externalUserGroup },
         { item: externalGroupIdA, type: GroupType.externalUserGroup },
         { item: groupIdB, type: GroupType.userGroup },
         { item: groupIdB, type: GroupType.userGroup },
         { item: externalGroupIdB, type: GroupType.externalUserGroup },
         { item: externalGroupIdB, type: GroupType.externalUserGroup },
       ]);
       ]);
-      expect(duplicatedPage1.parent).toStrictEqual(_page1.parent);
-      expect(duplicatedRevision1.body).toBe(_revision1.body);
-      expect(duplicatedRevision1.pageId).toStrictEqual(duplicatedPage1._id);
+      expect(duplicatedPage1?.parent).toStrictEqual(_page1?.parent);
+      expect(duplicatedRevision1?.body).toBe(_revision1?.body);
+      expect(duplicatedRevision1?.pageId).toStrictEqual(duplicatedPage1?._id);
       expect(
       expect(
         normalizeGrantedGroups(duplicatedPage2.grantedGroups),
         normalizeGrantedGroups(duplicatedPage2.grantedGroups),
       ).toStrictEqual([
       ).toStrictEqual([
         { item: groupIdC, type: GroupType.userGroup },
         { item: groupIdC, type: GroupType.userGroup },
         { item: externalGroupIdC, type: GroupType.externalUserGroup },
         { item: externalGroupIdC, type: GroupType.externalUserGroup },
       ]);
       ]);
-      expect(duplicatedPage2.parent).toStrictEqual(duplicatedPage1._id);
-      expect(duplicatedRevision2.body).toBe(_revision2.body);
-      expect(duplicatedRevision2.pageId).toStrictEqual(duplicatedPage2._id);
-      expect(duplicatedPage3.grantedUsers).toStrictEqual([npDummyUser2._id]);
-      expect(duplicatedPage3.parent).toStrictEqual(duplicatedPage1._id);
-      expect(duplicatedRevision3.body).toBe(_revision3.body);
-      expect(duplicatedRevision3.pageId).toStrictEqual(duplicatedPage3._id);
+      expect(duplicatedPage2?.parent).toStrictEqual(duplicatedPage1?._id);
+      expect(duplicatedRevision2?.body).toBe(_revision2?.body);
+      expect(duplicatedRevision2?.pageId).toStrictEqual(duplicatedPage2?._id);
+      expect(duplicatedPage3?.grantedUsers).toStrictEqual([npDummyUser2?._id]);
+      expect(duplicatedPage3?.parent).toStrictEqual(duplicatedPage1?._id);
+      expect(duplicatedRevision3?.body).toBe(_revision3?.body);
+      expect(duplicatedRevision3?.pageId).toStrictEqual(duplicatedPage3?._id);
     });
     });
   });
   });
   describe('Delete', () => {
   describe('Delete', () => {
@@ -1779,8 +1795,8 @@ describe('PageService page operations with non-public pages', () => {
         const pageN = await Page.findOne({ path: _pathT }); // should not exist
         const pageN = await Page.findOne({ path: _pathT }); // should not exist
         expect(pageT).toBeTruthy();
         expect(pageT).toBeTruthy();
         expect(pageN).toBeNull();
         expect(pageN).toBeNull();
-        expect(pageT.grant).toBe(Page.GRANT_RESTRICTED);
-        expect(pageT.status).toBe(Page.STATUS_DELETED);
+        expect(pageT?.grant).toBe(Page.GRANT_RESTRICTED);
+        expect(pageT?.status).toBe(Page.STATUS_DELETED);
       });
       });
     });
     });
     describe('Delete single page with grant USER_GROUP', () => {
     describe('Delete single page with grant USER_GROUP', () => {
@@ -1889,15 +1905,15 @@ describe('PageService page operations with non-public pages', () => {
         expect(page1).toBeTruthy();
         expect(page1).toBeTruthy();
         expect(page2).toBeTruthy();
         expect(page2).toBeTruthy();
         expect(pageR).toBeTruthy();
         expect(pageR).toBeTruthy();
-        expect(pageT.status).toBe(Page.STATUS_DELETED);
-        expect(pageT.status).toBe(Page.STATUS_DELETED);
-        expect(page1.status).toBe(Page.STATUS_DELETED);
-        expect(page1.descendantCount).toBe(0);
-        expect(page2.descendantCount).toBe(0);
-        expect(page2.descendantCount).toBe(0);
-        expect(pageT.parent).toBeNull();
-        expect(page1.parent).toBeNull();
-        expect(page2.parent).toBeNull();
+        expect(pageT?.status).toBe(Page.STATUS_DELETED);
+        expect(pageT?.status).toBe(Page.STATUS_DELETED);
+        expect(page1?.status).toBe(Page.STATUS_DELETED);
+        expect(page1?.descendantCount).toBe(0);
+        expect(page2?.descendantCount).toBe(0);
+        expect(page2?.descendantCount).toBe(0);
+        expect(pageT?.parent).toBeNull();
+        expect(page1?.parent).toBeNull();
+        expect(page2?.parent).toBeNull();
       });
       });
     });
     });
   });
   });
@@ -2081,10 +2097,10 @@ describe('PageService page operations with non-public pages', () => {
         status: Page.STATUS_DELETED,
         status: Page.STATUS_DELETED,
         grant: Page.GRANT_RESTRICTED,
         grant: Page.GRANT_RESTRICTED,
       });
       });
-      const revision = await Revision.findOne({ pageId: trashedPage._id });
+      const revision = await Revision.findOne({ pageId: trashedPage?._id });
       const tag = await Tag.findOne({ name: 'np_revertTag1' });
       const tag = await Tag.findOne({ name: 'np_revertTag1' });
       const deletedPageTagRelation = await PageTagRelation.findOne({
       const deletedPageTagRelation = await PageTagRelation.findOne({
-        relatedPage: trashedPage._id,
+        relatedPage: trashedPage?._id,
         relatedTag: tag?._id,
         relatedTag: tag?._id,
         isPageTrashed: true,
         isPageTrashed: true,
       });
       });
@@ -2103,7 +2119,7 @@ describe('PageService page operations with non-public pages', () => {
         path: '/trash/np_revert1',
         path: '/trash/np_revert1',
       });
       });
       const pageTagRelation = await PageTagRelation.findOne<IPageTagRelation>({
       const pageTagRelation = await PageTagRelation.findOne<IPageTagRelation>({
-        relatedPage: revertedPage._id,
+        relatedPage: revertedPage?._id,
         relatedTag: tag?._id,
         relatedTag: tag?._id,
       });
       });
       expect(revertedPage).toBeTruthy();
       expect(revertedPage).toBeTruthy();
@@ -2111,9 +2127,9 @@ describe('PageService page operations with non-public pages', () => {
       expect(deltedPageBeforeRevert).toBeNull();
       expect(deltedPageBeforeRevert).toBeNull();
 
 
       // page with GRANT_RESTRICTED does not have parent
       // page with GRANT_RESTRICTED does not have parent
-      expect(revertedPage.parent).toBeNull();
-      expect(revertedPage.status).toBe(Page.STATUS_PUBLISHED);
-      expect(revertedPage.grant).toBe(Page.GRANT_RESTRICTED);
+      expect(revertedPage?.parent).toBeNull();
+      expect(revertedPage?.status).toBe(Page.STATUS_PUBLISHED);
+      expect(revertedPage?.grant).toBe(Page.GRANT_RESTRICTED);
       expect(pageTagRelation?.isPageTrashed).toBe(false);
       expect(pageTagRelation?.isPageTrashed).toBe(false);
     });
     });
     test('should revert single deleted page with GRANT_USER_GROUP', async () => {
     test('should revert single deleted page with GRANT_USER_GROUP', async () => {
@@ -2124,10 +2140,10 @@ describe('PageService page operations with non-public pages', () => {
         status: Page.STATUS_DELETED,
         status: Page.STATUS_DELETED,
         grant: Page.GRANT_USER_GROUP,
         grant: Page.GRANT_USER_GROUP,
       });
       });
-      const revision = await Revision.findOne({ pageId: trashedPage._id });
+      const revision = await Revision.findOne({ pageId: trashedPage?._id });
       const tag = await Tag.findOne({ name: 'np_revertTag2' });
       const tag = await Tag.findOne({ name: 'np_revertTag2' });
       const deletedPageTagRelation = await PageTagRelation.findOne({
       const deletedPageTagRelation = await PageTagRelation.findOne({
-        relatedPage: trashedPage._id,
+        relatedPage: trashedPage?._id,
         relatedTag: tag?._id,
         relatedTag: tag?._id,
         isPageTrashed: true,
         isPageTrashed: true,
       });
       });
@@ -2144,20 +2160,22 @@ describe('PageService page operations with non-public pages', () => {
       const revertedPage = await Page.findOne({ path: '/np_revert2' });
       const revertedPage = await Page.findOne({ path: '/np_revert2' });
       const trashedPageBR = await Page.findOne({ path: beforeRevertPath });
       const trashedPageBR = await Page.findOne({ path: beforeRevertPath });
       const pageTagRelation = await PageTagRelation.findOne<IPageTagRelation>({
       const pageTagRelation = await PageTagRelation.findOne<IPageTagRelation>({
-        relatedPage: revertedPage._id,
+        relatedPage: revertedPage?._id,
         relatedTag: tag?._id,
         relatedTag: tag?._id,
       });
       });
       expect(revertedPage).toBeTruthy();
       expect(revertedPage).toBeTruthy();
       expect(pageTagRelation).toBeTruthy();
       expect(pageTagRelation).toBeTruthy();
       expect(trashedPageBR).toBeNull();
       expect(trashedPageBR).toBeNull();
 
 
-      expect(revertedPage.parent).toStrictEqual(rootPage._id);
-      expect(revertedPage.status).toBe(Page.STATUS_PUBLISHED);
-      expect(revertedPage.grant).toBe(Page.GRANT_USER_GROUP);
-      expect(normalizeGrantedGroups(revertedPage.grantedGroups)).toStrictEqual([
-        { item: groupIdA, type: GroupType.userGroup },
-        { item: externalGroupIdA, type: GroupType.externalUserGroup },
-      ]);
+      expect(revertedPage?.parent).toStrictEqual(rootPage._id);
+      expect(revertedPage?.status).toBe(Page.STATUS_PUBLISHED);
+      expect(revertedPage?.grant).toBe(Page.GRANT_USER_GROUP);
+      expect(normalizeGrantedGroups(revertedPage?.grantedGroups)).toStrictEqual(
+        [
+          { item: groupIdA, type: GroupType.userGroup },
+          { item: externalGroupIdA, type: GroupType.externalUserGroup },
+        ],
+      );
       expect(pageTagRelation?.isPageTrashed).toBe(false);
       expect(pageTagRelation?.isPageTrashed).toBe(false);
     });
     });
     test(`revert multiple pages: only target page should be reverted.
     test(`revert multiple pages: only target page should be reverted.
@@ -2174,8 +2192,8 @@ describe('PageService page operations with non-public pages', () => {
         status: Page.STATUS_DELETED,
         status: Page.STATUS_DELETED,
         grant: Page.GRANT_RESTRICTED,
         grant: Page.GRANT_RESTRICTED,
       });
       });
-      const revision1 = await Revision.findOne({ pageId: trashedPage1._id });
-      const revision2 = await Revision.findOne({ pageId: trashedPage2._id });
+      const revision1 = await Revision.findOne({ pageId: trashedPage1?._id });
+      const revision2 = await Revision.findOne({ pageId: trashedPage2?._id });
       expect(trashedPage1).toBeTruthy();
       expect(trashedPage1).toBeTruthy();
       expect(trashedPage2).toBeTruthy();
       expect(trashedPage2).toBeTruthy();
       expect(revision1).toBeTruthy();
       expect(revision1).toBeTruthy();
@@ -2194,9 +2212,9 @@ describe('PageService page operations with non-public pages', () => {
       // AR => After Revert
       // AR => After Revert
       const trashedPage1AR = await Page.findOne({ path: beforeRevertPath1 });
       const trashedPage1AR = await Page.findOne({ path: beforeRevertPath1 });
       const trashedPage2AR = await Page.findOne({ path: beforeRevertPath2 });
       const trashedPage2AR = await Page.findOne({ path: beforeRevertPath2 });
-      const revision1AR = await Revision.findOne({ pageId: revertedPage._id });
+      const revision1AR = await Revision.findOne({ pageId: revertedPage?._id });
       const revision2AR = await Revision.findOne({
       const revision2AR = await Revision.findOne({
-        pageId: trashedPage2AR._id,
+        pageId: trashedPage2AR?._id,
       });
       });
 
 
       expect(revertedPage).toBeTruthy();
       expect(revertedPage).toBeTruthy();
@@ -2206,9 +2224,9 @@ describe('PageService page operations with non-public pages', () => {
       expect(trashedPage1AR).toBeNull();
       expect(trashedPage1AR).toBeNull();
       expect(notRestrictedPage).toBeNull();
       expect(notRestrictedPage).toBeNull();
       expect(middlePage).toBeNull();
       expect(middlePage).toBeNull();
-      expect(revertedPage.parent).toStrictEqual(rootPage._id);
-      expect(revertedPage.status).toBe(Page.STATUS_PUBLISHED);
-      expect(revertedPage.grant).toBe(Page.GRANT_PUBLIC);
+      expect(revertedPage?.parent).toStrictEqual(rootPage._id);
+      expect(revertedPage?.status).toBe(Page.STATUS_PUBLISHED);
+      expect(revertedPage?.grant).toBe(Page.GRANT_PUBLIC);
     });
     });
     test('revert multiple pages: target page, initially non-existant page and leaf page with GRANT_USER_GROUP shoud be reverted', async () => {
     test('revert multiple pages: target page, initially non-existant page and leaf page with GRANT_USER_GROUP shoud be reverted', async () => {
       const user = await User.findOne({ _id: npDummyUser3 });
       const user = await User.findOne({ _id: npDummyUser3 });
@@ -2226,8 +2244,8 @@ describe('PageService page operations with non-public pages', () => {
         grantedGroups: { $elemMatch: { item: groupIdB } },
         grantedGroups: { $elemMatch: { item: groupIdB } },
       });
       });
       const nonExistantPage3 = await Page.findOne({ path: beforeRevertPath3 }); // not exist
       const nonExistantPage3 = await Page.findOne({ path: beforeRevertPath3 }); // not exist
-      const revision1 = await Revision.findOne({ pageId: trashedPage1._id });
-      const revision2 = await Revision.findOne({ pageId: trashedPage2._id });
+      const revision1 = await Revision.findOne({ pageId: trashedPage1?._id });
+      const revision2 = await Revision.findOne({ pageId: trashedPage2?._id });
       expect(trashedPage1).toBeTruthy();
       expect(trashedPage1).toBeTruthy();
       expect(trashedPage2).toBeTruthy();
       expect(trashedPage2).toBeTruthy();
       expect(revision1).toBeTruthy();
       expect(revision1).toBeTruthy();
@@ -2256,26 +2274,26 @@ describe('PageService page operations with non-public pages', () => {
       expect(trashedPage1AR).toBeNull();
       expect(trashedPage1AR).toBeNull();
       expect(trashedPage2AR).toBeNull();
       expect(trashedPage2AR).toBeNull();
 
 
-      expect(newlyCreatedPage.isEmpty).toBe(true);
-      expect(revertedPage1.parent).toStrictEqual(rootPage._id);
-      expect(revertedPage2.parent).toStrictEqual(newlyCreatedPage._id);
-      expect(newlyCreatedPage.parent).toStrictEqual(revertedPage1._id);
-      expect(revertedPage1.status).toBe(Page.STATUS_PUBLISHED);
-      expect(revertedPage2.status).toBe(Page.STATUS_PUBLISHED);
-      expect(newlyCreatedPage.status).toBe(Page.STATUS_PUBLISHED);
-      expect(normalizeGrantedGroups(revertedPage1.grantedGroups)).toStrictEqual(
-        [
-          { item: groupIdA, type: GroupType.userGroup },
-          { item: externalGroupIdA, type: GroupType.externalUserGroup },
-        ],
-      );
-      expect(normalizeGrantedGroups(revertedPage2.grantedGroups)).toStrictEqual(
-        [
-          { item: groupIdB, type: GroupType.userGroup },
-          { item: externalGroupIdB, type: GroupType.externalUserGroup },
-        ],
-      );
-      expect(newlyCreatedPage.grant).toBe(Page.GRANT_PUBLIC);
+      expect(newlyCreatedPage?.isEmpty).toBe(true);
+      expect(revertedPage1?.parent).toStrictEqual(rootPage._id);
+      expect(revertedPage2?.parent).toStrictEqual(newlyCreatedPage?._id);
+      expect(newlyCreatedPage?.parent).toStrictEqual(revertedPage1?._id);
+      expect(revertedPage1?.status).toBe(Page.STATUS_PUBLISHED);
+      expect(revertedPage2?.status).toBe(Page.STATUS_PUBLISHED);
+      expect(newlyCreatedPage?.status).toBe(Page.STATUS_PUBLISHED);
+      expect(
+        normalizeGrantedGroups(revertedPage1?.grantedGroups),
+      ).toStrictEqual([
+        { item: groupIdA, type: GroupType.userGroup },
+        { item: externalGroupIdA, type: GroupType.externalUserGroup },
+      ]);
+      expect(
+        normalizeGrantedGroups(revertedPage2?.grantedGroups),
+      ).toStrictEqual([
+        { item: groupIdB, type: GroupType.userGroup },
+        { item: externalGroupIdB, type: GroupType.externalUserGroup },
+      ]);
+      expect(newlyCreatedPage?.grant).toBe(Page.GRANT_PUBLIC);
     });
     });
   });
   });
 });
 });

+ 98 - 110
apps/app/test/integration/service/v5.page.test.ts

@@ -1,52 +1,44 @@
+import type { IPage } from '@growi/core';
 import { addSeconds } from 'date-fns/addSeconds';
 import { addSeconds } from 'date-fns/addSeconds';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
-
 import {
 import {
   PageActionStage,
   PageActionStage,
   PageActionType,
   PageActionType,
 } from '../../../src/interfaces/page-operation';
 } from '../../../src/interfaces/page-operation';
+import type Crowi from '../../../src/server/crowi';
+import type { PageDocument, PageModel } from '../../../src/server/models/page';
+import type {
+  IPageOperation,
+  PageOperationModel,
+} from '../../../src/server/models/page-operation';
 import { getInstance } from '../setup-crowi';
 import { getInstance } from '../setup-crowi';
 
 
 describe('Test page service methods', () => {
 describe('Test page service methods', () => {
-  let crowi;
-  let Page;
-  let Revision;
+  let crowi: Crowi;
+  let Page: PageModel;
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let User;
   let User;
-  let Tag;
-  let Bookmark;
-  let Comment;
-  let ShareLink;
-  let PageRedirect;
-  let PageOperation;
+  let PageOperation: PageOperationModel;
 
 
-  let rootPage;
+  let rootPage: PageDocument;
 
 
+  // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
   let dummyUser1;
   let dummyUser1;
-  let dummyUser2;
-  let globalGroupUser1;
-  let globalGroupUser2;
-  let globalGroupUser3;
-
-  let pageOpId1;
-  let pageOpId2;
-  let pageOpId3;
-  let pageOpId4;
-  let pageOpId5;
-  let pageOpId6;
+
+  let pageOpId1: mongoose.Types.ObjectId;
+  let pageOpId2: mongoose.Types.ObjectId;
+  let pageOpId3: mongoose.Types.ObjectId;
+  let pageOpId4: mongoose.Types.ObjectId;
 
 
   beforeAll(async () => {
   beforeAll(async () => {
     crowi = await getInstance();
     crowi = await getInstance();
     await crowi.configManager.updateConfig('app:isV5Compatible', true);
     await crowi.configManager.updateConfig('app:isV5Compatible', true);
 
 
     User = mongoose.model('User');
     User = mongoose.model('User');
-    Page = mongoose.model('Page');
-    Revision = mongoose.model('Revision');
-    Tag = mongoose.model('Tag');
-    Bookmark = mongoose.model('Bookmark');
-    Comment = mongoose.model('Comment');
-    ShareLink = mongoose.model('ShareLink');
-    PageRedirect = mongoose.model('PageRedirect');
-    PageOperation = mongoose.model('PageOperation');
+    Page = mongoose.model<IPage, PageModel>('Page');
+    PageOperation = mongoose.model<IPageOperation, PageOperationModel>(
+      'PageOperation',
+    );
 
 
     /*
     /*
      * Common
      * Common
@@ -57,12 +49,8 @@ describe('Test page service methods', () => {
     // ***********************************************************************************************************
     // ***********************************************************************************************************
     // users
     // users
     dummyUser1 = await User.findOne({ username: 'v5DummyUser1' });
     dummyUser1 = await User.findOne({ username: 'v5DummyUser1' });
-    dummyUser2 = await User.findOne({ username: 'v5DummyUser2' });
-    globalGroupUser1 = await User.findOne({ username: 'gGroupUser1' });
-    globalGroupUser2 = await User.findOne({ username: 'gGroupUser2' });
-    globalGroupUser3 = await User.findOne({ username: 'gGroupUser3' });
     // page
     // page
-    rootPage = await Page.findOne({ path: '/' });
+    rootPage = (await Page.findOne({ path: '/' }))!;
 
 
     /**
     /**
      * pages
      * pages
@@ -517,10 +505,10 @@ describe('Test page service methods', () => {
       expect(_page2).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page3).toBeTruthy();
       expect(_page3).toBeTruthy();
 
 
-      expect(_page0.descendantCount).toBe(1);
-      expect(_page1.descendantCount).toBe(2);
-      expect(_page2.descendantCount).toBe(1);
-      expect(_page3.descendantCount).toBe(0);
+      expect(_page0?.descendantCount).toBe(1);
+      expect(_page1?.descendantCount).toBe(2);
+      expect(_page2?.descendantCount).toBe(1);
+      expect(_page3?.descendantCount).toBe(0);
 
 
       // page operation
       // page operation
       const fromPath = '/resume_rename_1';
       const fromPath = '/resume_rename_1';
@@ -529,7 +517,7 @@ describe('Test page service methods', () => {
         _id: pageOpId1,
         _id: pageOpId1,
         fromPath,
         fromPath,
         toPath,
         toPath,
-        'page._id': _page1._id,
+        'page._id': _page1?._id,
         actionType: PageActionType.Rename,
         actionType: PageActionType.Rename,
         actionStage: PageActionStage.Sub,
         actionStage: PageActionStage.Sub,
       });
       });
@@ -539,28 +527,28 @@ describe('Test page service methods', () => {
       await resumeRenameSubOperation(_page1, _pageOperation, activity);
       await resumeRenameSubOperation(_page1, _pageOperation, activity);
 
 
       // page
       // page
-      const page0 = await Page.findById(_page0._id);
-      const page1 = await Page.findById(_page1._id);
-      const page2 = await Page.findById(_page2._id);
-      const page3 = await Page.findById(_page3._id);
+      const page0 = await Page.findById(_page0?._id);
+      const page1 = await Page.findById(_page1?._id);
+      const page2 = await Page.findById(_page2?._id);
+      const page3 = await Page.findById(_page3?._id);
       expect(page0).toBeTruthy();
       expect(page0).toBeTruthy();
       expect(page1).toBeTruthy();
       expect(page1).toBeTruthy();
       expect(page2).toBeTruthy();
       expect(page2).toBeTruthy();
       expect(page3).toBeTruthy();
       expect(page3).toBeTruthy();
       // check paths after renaming
       // check paths after renaming
-      expect(page0.path).toBe(path0);
-      expect(page1.path).toBe(path1);
-      expect(page2.path).toBe(path2);
-      expect(page3.path).toBe(path3);
+      expect(page0?.path).toBe(path0);
+      expect(page1?.path).toBe(path1);
+      expect(page2?.path).toBe(path2);
+      expect(page3?.path).toBe(path3);
 
 
       // page operation
       // page operation
-      const pageOperation = await PageOperation.findById(_pageOperation._id);
+      const pageOperation = await PageOperation.findById(_pageOperation?._id);
       expect(pageOperation).toBeNull(); // should not exist
       expect(pageOperation).toBeNull(); // should not exist
 
 
-      expect(page0.descendantCount).toBe(3);
-      expect(page1.descendantCount).toBe(2);
-      expect(page2.descendantCount).toBe(1);
-      expect(page3.descendantCount).toBe(0);
+      expect(page0?.descendantCount).toBe(3);
+      expect(page1?.descendantCount).toBe(2);
+      expect(page2?.descendantCount).toBe(1);
+      expect(page3?.descendantCount).toBe(0);
     });
     });
     test('it should successfully restart rename operation when unprocessableExpiryDate is null', async () => {
     test('it should successfully restart rename operation when unprocessableExpiryDate is null', async () => {
       // paths before renaming
       // paths before renaming
@@ -588,9 +576,9 @@ describe('Test page service methods', () => {
       expect(_page1).toBeTruthy();
       expect(_page1).toBeTruthy();
       expect(_page2).toBeTruthy();
       expect(_page2).toBeTruthy();
 
 
-      expect(_page0.descendantCount).toBe(1);
-      expect(_page1.descendantCount).toBe(1);
-      expect(_page2.descendantCount).toBe(0);
+      expect(_page0?.descendantCount).toBe(1);
+      expect(_page1?.descendantCount).toBe(1);
+      expect(_page2?.descendantCount).toBe(0);
 
 
       // page operation
       // page operation
       const fromPath = '/resume_rename_9';
       const fromPath = '/resume_rename_9';
@@ -599,7 +587,7 @@ describe('Test page service methods', () => {
         _id: pageOpId4,
         _id: pageOpId4,
         fromPath,
         fromPath,
         toPath,
         toPath,
-        'page._id': _page1._id,
+        'page._id': _page1?._id,
         actionType: PageActionType.Rename,
         actionType: PageActionType.Rename,
         actionStage: PageActionStage.Sub,
         actionStage: PageActionStage.Sub,
       });
       });
@@ -613,27 +601,27 @@ describe('Test page service methods', () => {
       );
       );
 
 
       // page
       // page
-      const page0 = await Page.findById(_page0._id);
-      const page1 = await Page.findById(_page1._id);
-      const page2 = await Page.findById(_page2._id);
+      const page0 = await Page.findById(_page0?._id);
+      const page1 = await Page.findById(_page1?._id);
+      const page2 = await Page.findById(_page2?._id);
       expect(page0).toBeTruthy();
       expect(page0).toBeTruthy();
       expect(page1).toBeTruthy();
       expect(page1).toBeTruthy();
       expect(page2).toBeTruthy();
       expect(page2).toBeTruthy();
       // check paths after renaming
       // check paths after renaming
-      expect(page0.path).toBe(path0);
-      expect(page1.path).toBe(path1);
-      expect(page2.path).toBe(path2);
+      expect(page0?.path).toBe(path0);
+      expect(page1?.path).toBe(path1);
+      expect(page2?.path).toBe(path2);
 
 
       // page operation
       // page operation
-      const pageOperation = await PageOperation.findById(_pageOperation._id);
+      const pageOperation = await PageOperation.findById(_pageOperation?._id);
       expect(pageOperation).toBeNull(); // should not exist
       expect(pageOperation).toBeNull(); // should not exist
 
 
       // others
       // others
-      expect(page1.parent).toStrictEqual(page0._id);
-      expect(page2.parent).toStrictEqual(page1._id);
-      expect(page0.descendantCount).toBe(2);
-      expect(page1.descendantCount).toBe(1);
-      expect(page2.descendantCount).toBe(0);
+      expect(page1?.parent).toStrictEqual(page0?._id);
+      expect(page2?.parent).toStrictEqual(page1?._id);
+      expect(page0?.descendantCount).toBe(2);
+      expect(page1?.descendantCount).toBe(1);
+      expect(page2?.descendantCount).toBe(0);
     });
     });
     test('it should fail and throw error if the current time is behind unprocessableExpiryDate', async () => {
     test('it should fail and throw error if the current time is behind unprocessableExpiryDate', async () => {
       // path before renaming
       // path before renaming
@@ -655,7 +643,7 @@ describe('Test page service methods', () => {
         _id: pageOpId2,
         _id: pageOpId2,
         fromPath,
         fromPath,
         toPath,
         toPath,
-        'page._id': _page1._id,
+        'page._id': _page1?._id,
         actionType: PageActionType.Rename,
         actionType: PageActionType.Rename,
         actionStage: PageActionStage.Sub,
         actionStage: PageActionStage.Sub,
       });
       });
@@ -664,7 +652,7 @@ describe('Test page service methods', () => {
       // Make `unprocessableExpiryDate` 15 seconds ahead of current time.
       // Make `unprocessableExpiryDate` 15 seconds ahead of current time.
       // The number 15 seconds has no meaning other than placing time in the furue.
       // The number 15 seconds has no meaning other than placing time in the furue.
       const pageOperation = await PageOperation.findByIdAndUpdate(
       const pageOperation = await PageOperation.findByIdAndUpdate(
-        _pageOperation._id,
+        _pageOperation?._id,
         { unprocessableExpiryDate: addSeconds(new Date(), 15) },
         { unprocessableExpiryDate: addSeconds(new Date(), 15) },
         { new: true },
         { new: true },
       );
       );
@@ -677,7 +665,7 @@ describe('Test page service methods', () => {
       );
       );
 
 
       // cleanup
       // cleanup
-      await PageOperation.findByIdAndDelete(pageOperation._id);
+      await PageOperation.findByIdAndDelete(pageOperation?._id);
     });
     });
     test('Missing property(toPath) for PageOperation should throw error', async () => {
     test('Missing property(toPath) for PageOperation should throw error', async () => {
       // page
       // page
@@ -688,7 +676,7 @@ describe('Test page service methods', () => {
       // page operation
       // page operation
       const pageOperation = await PageOperation.findOne({
       const pageOperation = await PageOperation.findOne({
         _id: pageOpId3,
         _id: pageOpId3,
-        'page._id': _page1._id,
+        'page._id': _page1?._id,
         actionType: PageActionType.Rename,
         actionType: PageActionType.Rename,
         actionStage: PageActionStage.Sub,
         actionStage: PageActionStage.Sub,
       });
       });
@@ -697,12 +685,12 @@ describe('Test page service methods', () => {
       const promise = resumeRenameSubOperation(_page1, pageOperation);
       const promise = resumeRenameSubOperation(_page1, pageOperation);
       await expect(promise).rejects.toThrow(
       await expect(promise).rejects.toThrow(
         new Error(
         new Error(
-          `Property toPath is missing which is needed to resume rename operation(${pageOperation._id})`,
+          `Property toPath is missing which is needed to resume rename operation(${pageOperation?._id})`,
         ),
         ),
       );
       );
 
 
       // cleanup
       // cleanup
-      await PageOperation.findByIdAndDelete(pageOperation._id);
+      await PageOperation.findByIdAndDelete(pageOperation?._id);
     });
     });
   });
   });
   describe('updateDescendantCountOfPagesWithPaths', () => {
   describe('updateDescendantCountOfPagesWithPaths', () => {
@@ -727,23 +715,23 @@ describe('Test page service methods', () => {
       expect(_page4).toBeTruthy();
       expect(_page4).toBeTruthy();
       expect(_page5).toBeTruthy();
       expect(_page5).toBeTruthy();
       // check descendantCount (all broken)
       // check descendantCount (all broken)
-      expect(_page1.descendantCount).toBe(100);
-      expect(_page2.descendantCount).toBe(100);
-      expect(_page3.descendantCount).toBe(100);
-      expect(_page4.descendantCount).toBe(100);
-      expect(_page5.descendantCount).toBe(100);
+      expect(_page1?.descendantCount).toBe(100);
+      expect(_page2?.descendantCount).toBe(100);
+      expect(_page3?.descendantCount).toBe(100);
+      expect(_page4?.descendantCount).toBe(100);
+      expect(_page5?.descendantCount).toBe(100);
       // check isEmpty
       // check isEmpty
-      expect(_page1.isEmpty).toBe(false);
-      expect(_page2.isEmpty).toBe(true);
-      expect(_page3.isEmpty).toBe(false);
-      expect(_page4.isEmpty).toBe(false);
-      expect(_page5.isEmpty).toBe(false);
+      expect(_page1?.isEmpty).toBe(false);
+      expect(_page2?.isEmpty).toBe(true);
+      expect(_page3?.isEmpty).toBe(false);
+      expect(_page4?.isEmpty).toBe(false);
+      expect(_page5?.isEmpty).toBe(false);
       // check parent
       // check parent
-      expect(_page1.parent).toStrictEqual(rootPage._id);
-      expect(_page2.parent).toStrictEqual(_page1._id);
-      expect(_page3.parent).toStrictEqual(_page2._id);
-      expect(_page4.parent).toStrictEqual(rootPage._id);
-      expect(_page5.parent).toStrictEqual(_page4._id);
+      expect(_page1?.parent).toStrictEqual(rootPage._id);
+      expect(_page2?.parent).toStrictEqual(_page1?._id);
+      expect(_page3?.parent).toStrictEqual(_page2?._id);
+      expect(_page4?.parent).toStrictEqual(rootPage._id);
+      expect(_page5?.parent).toStrictEqual(_page4?._id);
 
 
       await crowi.pageService.updateDescendantCountOfPagesWithPaths([
       await crowi.pageService.updateDescendantCountOfPagesWithPaths([
         _path1,
         _path1,
@@ -754,11 +742,11 @@ describe('Test page service methods', () => {
       ]);
       ]);
 
 
       // page
       // page
-      const page1 = await Page.findById(_page1._id);
-      const page2 = await Page.findById(_page2._id);
-      const page3 = await Page.findById(_page3._id);
-      const page4 = await Page.findById(_page4._id);
-      const page5 = await Page.findById(_page5._id);
+      const page1 = await Page.findById(_page1?._id);
+      const page2 = await Page.findById(_page2?._id);
+      const page3 = await Page.findById(_page3?._id);
+      const page4 = await Page.findById(_page4?._id);
+      const page5 = await Page.findById(_page5?._id);
 
 
       // check existance
       // check existance
       expect(page1).toBeTruthy();
       expect(page1).toBeTruthy();
@@ -767,23 +755,23 @@ describe('Test page service methods', () => {
       expect(page4).toBeTruthy();
       expect(page4).toBeTruthy();
       expect(page5).toBeTruthy();
       expect(page5).toBeTruthy();
       // check descendantCount (all fixed)
       // check descendantCount (all fixed)
-      expect(page1.descendantCount).toBe(1);
-      expect(page2.descendantCount).toBe(1);
-      expect(page3.descendantCount).toBe(0);
-      expect(page4.descendantCount).toBe(1);
-      expect(page5.descendantCount).toBe(0);
+      expect(page1?.descendantCount).toBe(1);
+      expect(page2?.descendantCount).toBe(1);
+      expect(page3?.descendantCount).toBe(0);
+      expect(page4?.descendantCount).toBe(1);
+      expect(page5?.descendantCount).toBe(0);
       // check isEmpty
       // check isEmpty
-      expect(page1.isEmpty).toBe(false);
-      expect(page2.isEmpty).toBe(true);
-      expect(page3.isEmpty).toBe(false);
-      expect(page4.isEmpty).toBe(false);
-      expect(page5.isEmpty).toBe(false);
+      expect(page1?.isEmpty).toBe(false);
+      expect(page2?.isEmpty).toBe(true);
+      expect(page3?.isEmpty).toBe(false);
+      expect(page4?.isEmpty).toBe(false);
+      expect(page5?.isEmpty).toBe(false);
       // check parent
       // check parent
-      expect(page1.parent).toStrictEqual(rootPage._id);
-      expect(page2.parent).toStrictEqual(page1._id);
-      expect(page3.parent).toStrictEqual(page2._id);
-      expect(page4.parent).toStrictEqual(rootPage._id);
-      expect(page5.parent).toStrictEqual(page4._id);
+      expect(page1?.parent).toStrictEqual(rootPage._id);
+      expect(page2?.parent).toStrictEqual(page1?._id);
+      expect(page3?.parent).toStrictEqual(page2?._id);
+      expect(page4?.parent).toStrictEqual(rootPage._id);
+      expect(page5?.parent).toStrictEqual(page4?._id);
     });
     });
   });
   });
 });
 });

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 233 - 189
apps/app/test/integration/service/v5.public-page.test.ts


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно