Просмотр исходного кода

Merge branch 'master' into support/107699-next-PageStatusAlert

Yuken Tezuka 3 лет назад
Родитель
Сommit
a48a254c20
32 измененных файлов с 596 добавлено и 337 удалено
  1. 2 2
      packages/app/public/static/locales/en_US/admin.json
  2. 1 0
      packages/app/public/static/locales/en_US/commons.json
  3. 0 2
      packages/app/public/static/locales/en_US/translation.json
  4. 2 2
      packages/app/public/static/locales/ja_JP/admin.json
  5. 1 0
      packages/app/public/static/locales/ja_JP/commons.json
  6. 0 1
      packages/app/public/static/locales/ja_JP/translation.json
  7. 2 2
      packages/app/public/static/locales/zh_CN/admin.json
  8. 1 0
      packages/app/public/static/locales/zh_CN/commons.json
  9. 0 2
      packages/app/public/static/locales/zh_CN/translation.json
  10. 2 2
      packages/app/src/components/Admin/Security/LocalSecuritySettingContents.jsx
  11. 1 1
      packages/app/src/components/BookmarkButtons.tsx
  12. 17 2
      packages/app/src/components/EventListeneres/HashChanged.tsx
  13. 1 1
      packages/app/src/components/LikeButtons.tsx
  14. 1 1
      packages/app/src/components/Navbar/GrowiNavbar.tsx
  15. 3 3
      packages/app/src/components/Navbar/PersonalDropdown.jsx
  16. 23 4
      packages/app/src/components/PageEditor.tsx
  17. 4 2
      packages/app/src/components/PageEditor/CodeMirrorEditor.jsx
  18. 1 1
      packages/app/src/components/SubscribeButton.tsx
  19. 1 1
      packages/app/src/components/User/SeenUserInfo.tsx
  20. 8 3
      packages/app/src/pages/[[...path]].page.tsx
  21. 13 3
      packages/app/src/pages/_document.page.tsx
  22. 20 0
      packages/app/src/pages/tags.page.tsx
  23. 6 5
      packages/app/src/stores/context.tsx
  24. 14 5
      packages/app/src/stores/ui.tsx
  25. 45 34
      packages/app/test/cypress/integration/20-basic-features/access-to-page.spec.ts
  26. 23 4
      packages/app/test/cypress/integration/20-basic-features/access-to-pagelist.spec.ts
  27. 121 59
      packages/app/test/cypress/integration/20-basic-features/click-page-icons.spec.ts
  28. 31 11
      packages/app/test/cypress/integration/30-search/search.spec.ts
  29. 2 0
      packages/app/test/cypress/integration/40-admin/access-to-admin-page.spec.ts
  30. 234 176
      packages/app/test/cypress/integration/50-sidebar/access-to-side-bar.spec.ts
  31. 8 4
      packages/app/test/cypress/integration/50-sidebar/switching-sidebar-mode.spec.ts
  32. 8 4
      packages/app/test/cypress/integration/60-home/home.spec.ts

+ 2 - 2
packages/app/public/static/locales/en_US/admin.json

@@ -16,8 +16,8 @@
     "always_displayed": "Always displayed",
     "displayed_or_hidden": "Displayed / Hidden",
     "Fixed by env var": "This is fixed by the env var <code>{{key}}={{value}}</code>.",
-    "Register limitation": "Register limitation",
-    "Register limitation desc": "Restriction of new users' registration",
+    "register_limitation": "Register limitation",
+    "register_limitation_desc": "Restriction of new users' registration",
     "The whitelist of registration permission E-mail address": "The whitelist of registration permission E-mail address",
     "users_without_account": "Users without account is not accessible",
     "example": "Example",

+ 1 - 0
packages/app/public/static/locales/en_US/commons.json

@@ -4,6 +4,7 @@
   "Add": "Add",
   "Reset": "Reset",
   "Sign out": "Logout",
+  "New": "New",
 
   "meta": {
     "display_name": "English"

+ 0 - 2
packages/app/public/static/locales/en_US/translation.json

@@ -28,7 +28,6 @@
   "administrator": "Admin",
   "Tag": "Tag",
   "Tags": "Tags",
-  "New": "New",
   "Close": "Close",
   "Shortcuts": "Shortcuts",
   "CustomSidebar": "Custom Sidebar",
@@ -117,7 +116,6 @@
   "UserGroup": "UserGroup",
   "Basic Settings": "Basic Settings",
   "Basic authentication": "Basic authentication",
-  "Register limitation": "Register limitation",
   "The contents entered here will be shown in the header etc": "The contents entered here will be shown in the header etc",
   "Public": "Public",
   "Anyone with the link": "Anyone with the link",

+ 2 - 2
packages/app/public/static/locales/ja_JP/admin.json

@@ -26,8 +26,8 @@
     "always_displayed": "表示 (固定)",
     "displayed_or_hidden": "表示 / 非表示",
     "Fixed by env var": "環境変数 <code>{{forcewikimode}}={{wikimode}}</code> により固定されています。",
-    "Register limitation": "登録の制限",
-    "Register limitation desc": "新しいユーザーを登録する方法を制限します.",
+    "register_limitation": "登録の制限",
+    "register_limitation_desc": "新しいユーザーを登録する方法を制限します。",
     "The whitelist of registration permission E-mail address": "登録許可メールアドレスの<br>ホワイトリスト",
     "users_without_account": "アカウントを持たないユーザーはアクセス不可",
     "example": "例",

+ 1 - 0
packages/app/public/static/locales/ja_JP/commons.json

@@ -4,6 +4,7 @@
   "Add": "追加",
   "Reset": "リセット",
   "Sign out": "ログアウト",
+  "New": "作成",
 
   "meta": {
     "display_name": "日本語"

+ 0 - 1
packages/app/public/static/locales/ja_JP/translation.json

@@ -28,7 +28,6 @@
   "administrator": "管理者",
   "Tag": "タグ",
   "Tags": "タグ",
-  "New": "作成",
   "Close": "閉じる",
   "Shortcuts": "ショートカット",
   "CustomSidebar": "カスタムサイドバー",

+ 2 - 2
packages/app/public/static/locales/zh_CN/admin.json

@@ -25,8 +25,8 @@
     "displayed_or_hidden": "显示/隐藏",
     "Guest Users Access": "来宾用户访问",
 		"Fixed by env var": "这是由env var<code>%s=%s</code>修复的。",
-		"Register limitation": "注册限制",
-		"Register limitation desc": "限制新用户注册",
+		"register_limitation": "注册限制",
+		"register_limitation_desc": "限制新用户注册",
 		"The whitelist of registration permission E-mail address": "注册许可电子邮件地址的白名单",
 		"users_without_account": "无法访问没有帐户的用户",
 		"example": "例子",

+ 1 - 0
packages/app/public/static/locales/zh_CN/commons.json

@@ -4,6 +4,7 @@
   "Add": "添加",
   "Reset": "重启",
 	"Sign out": "退出",
+  "New": "新建",
 
   "meta": {
     "display_name": "简体中文"

+ 0 - 2
packages/app/public/static/locales/zh_CN/translation.json

@@ -28,7 +28,6 @@
 	"Admin": "管理",
 	"administrator": "管理员",
 	"Tags": "Tags",
-  "New": "新建",
   "Close": "Close",
 	"Shortcuts": "快捷方式",
   "CustomSidebar": "Custom Sidebar",
@@ -124,7 +123,6 @@
   "ChildUserGroup": "儿童用户组",
 	"Basic Settings": "基础设置",
 	"Basic authentication": "基本身份验证",
-	"Register limitation": "注册限制",
 	"The contents entered here will be shown in the header etc": "此处输入的内容将显示在标题等中",
 	"Public": "公共",
 	"Anyone with the link": "任何人",

+ 2 - 2
packages/app/src/components/Admin/Security/LocalSecuritySettingContents.jsx

@@ -100,7 +100,7 @@ class LocalSecuritySettingContents extends React.Component {
 
             <div className="row">
               <div className="col-12 col-md-3 text-left text-md-right py-2">
-                <strong>{t('security_settings.Register limitation')}</strong>
+                <strong>{t('security_settings.register_limitation')}</strong>
               </div>
               <div className="col-12 col-md-6">
                 <div className="dropdown">
@@ -147,7 +147,7 @@ class LocalSecuritySettingContents extends React.Component {
                   </div>
                 </div>
 
-                <p className="form-text text-muted small">{t('security_settings.Register limitation desc')}</p>
+                <p className="form-text text-muted small">{t('security_settings.register_limitation_desc')}</p>
               </div>
             </div>
             <div className="row">

+ 1 - 1
packages/app/src/components/BookmarkButtons.tsx

@@ -63,7 +63,7 @@ const BookmarkButtons: FC<Props> = (props: Props) => {
         <i className={`fa ${isBookmarked ? 'fa-bookmark' : 'fa-bookmark-o'}`}></i>
       </button>
 
-      <UncontrolledTooltip placement="top" target="bookmark-button" fade={false}>
+      <UncontrolledTooltip data-testid="bookmark-button-tooltip" placement="top" target="bookmark-button" fade={false}>
         {t(getTooltipMessage())}
       </UncontrolledTooltip>
 

+ 17 - 2
packages/app/src/components/EventListeneres/HashChanged.tsx

@@ -1,12 +1,15 @@
 import React, { useCallback, useEffect } from 'react';
 
-import { useEditorMode, determineEditorModeByHash } from '~/stores/ui';
+import { useRouter } from 'next/router';
+
 import { useIsEditable } from '~/stores/context';
+import { useEditorMode, determineEditorModeByHash } from '~/stores/ui';
 
 /**
  * Change editorMode by browser forward/back operation
  */
 const HashChanged = (): JSX.Element => {
+  const router = useRouter();
   const { data: isEditable } = useIsEditable();
   const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
 
@@ -31,7 +34,19 @@ const HashChanged = (): JSX.Element => {
       window.removeEventListener('hashchange', hashchangeHandler);
     };
 
-  }, [hashchangeHandler, isEditable, mutateEditorMode]);
+  }, [hashchangeHandler, isEditable]);
+
+  /*
+  * Route changes by Next Router
+  * https://nextjs.org/docs/api-reference/next/router
+  */
+  useEffect(() => {
+    router.events.on('routeChangeComplete', hashchangeHandler);
+
+    return () => {
+      router.events.off('routeChangeComplete', hashchangeHandler);
+    };
+  }, [hashchangeHandler, router.events]);
 
   return <></>;
 };

+ 1 - 1
packages/app/src/components/LikeButtons.tsx

@@ -56,7 +56,7 @@ const LikeButtons: FC<LikeButtonsProps> = (props: LikeButtonsProps) => {
         <i className={`fa ${isLiked ? 'fa-heart' : 'fa-heart-o'}`}></i>
       </button>
 
-      <UncontrolledTooltip placement="top" target="like-button" fade={false}>
+      <UncontrolledTooltip data-testid="like-button-tooltip" placement="top" target="like-button" autohide={false} fade={false}>
         {t(getTooltipMessage())}
       </UncontrolledTooltip>
 

+ 1 - 1
packages/app/src/components/Navbar/GrowiNavbar.tsx

@@ -59,7 +59,7 @@ const NavbarRight = memo((): JSX.Element => {
             onClick={() => openCreateModal(currentPagePath || '')}
           >
             <i className="icon-pencil mr-2"></i>
-            <span className="d-none d-lg-block">{ t('New') }</span>
+            <span className="d-none d-lg-block">{ t('commons:New') }</span>
           </button>
         </li>
 

+ 3 - 3
packages/app/src/components/Navbar/PersonalDropdown.jsx

@@ -39,7 +39,7 @@ const PersonalDropdown = () => {
       </button>
 
       {/* Menu */}
-      <div className="dropdown-menu dropdown-menu-right">
+      <div className="dropdown-menu dropdown-menu-right" data-testid="personal-dropdown-menu">
 
         <div className="px-4 pt-3 pb-2 text-center">
           <UserPicture user={user} size="lg" noLink noTooltip />
@@ -55,12 +55,12 @@ const PersonalDropdown = () => {
 
           <div className="btn-group btn-block mt-2" role="group">
             <Link href={`/user/${user.username}`}>
-              <a className="btn btn-sm btn-outline-secondary col">
+              <a className="btn btn-sm btn-outline-secondary col" data-testid="grw-personal-dropdown-menu-user-home">
                 <i className="icon-fw icon-home"></i>{t('personal_dropdown.home')}
               </a>
             </Link>
             <Link href="/me">
-              <a className="btn btn-sm btn-outline-secondary col">
+              <a className="btn btn-sm btn-outline-secondary col" data-testid="grw-personal-dropdown-menu-user-settings">
                 <i className="icon-fw icon-wrench"></i>{t('personal_dropdown.settings')}
               </a>
             </Link>

+ 23 - 4
packages/app/src/components/PageEditor.tsx

@@ -4,7 +4,9 @@ import React, {
 
 import EventEmitter from 'events';
 
-import { envUtils, IPageHasId, PageGrant } from '@growi/core';
+import {
+  envUtils, IPageHasId, PageGrant, pathUtils,
+} from '@growi/core';
 import detectIndent from 'detect-indent';
 import { useTranslation } from 'next-i18next';
 import { useRouter } from 'next/router';
@@ -17,8 +19,8 @@ import { getOptionsToSave } from '~/client/util/editor';
 import { IEditorMethods } from '~/interfaces/editor-methods';
 import { SocketEventName } from '~/interfaces/websocket';
 import {
-  useCurrentPathname, useCurrentPageId,
-  useIsEditable, useIsIndentSizeForced, useIsUploadableFile, useIsUploadableImage, useIsNotFound, useEditingMarkdown,
+  useCurrentPathname, useCurrentPageId, useIsEnabledAttachTitleHeader, useTemplateBodyData,
+  useIsEditable, useIsIndentSizeForced, useIsUploadableFile, useIsUploadableImage, useEditingMarkdown, useIsNotFound,
 } from '~/stores/context';
 import {
   useCurrentIndentSize, useSWRxSlackChannels, useIsSlackEnabled, useIsTextlintEnabled, usePageTagsForEditors,
@@ -65,6 +67,8 @@ const PageEditor = React.memo((): JSX.Element => {
   const { data: grantData, mutate: mutateGrant } = useSelectedGrant();
   const { data: pageTags } = usePageTagsForEditors(pageId);
   const { data: editingMarkdown } = useEditingMarkdown();
+  const { data: isEnabledAttachTitleHeader } = useIsEnabledAttachTitleHeader();
+  const { data: templateBodyData } = useTemplateBodyData();
   const { data: isEditable } = useIsEditable();
   const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
   const { data: isMobile } = useIsMobile();
@@ -80,7 +84,22 @@ const PageEditor = React.memo((): JSX.Element => {
   const { data: rendererOptions } = usePreviewOptions();
 
   const currentRevisionId = currentPage?.revision?._id;
-  const initialValue = editingMarkdown ?? '';
+
+  const initialValue = useMemo(() => {
+    if (!isNotFound) {
+      return editingMarkdown ?? '';
+    }
+
+    let initialValue = '';
+    if (isEnabledAttachTitleHeader && currentPathname != null) {
+      initialValue += `${pathUtils.attachTitleHeader(currentPathname)}\n`;
+    }
+    if (templateBodyData != null) {
+      initialValue += `${templateBodyData}\n`;
+    }
+    return initialValue;
+
+  }, [isNotFound, currentPathname, editingMarkdown, isEnabledAttachTitleHeader, templateBodyData]);
 
   const markdownToSave = useRef<string>(initialValue);
   const [markdownToPreview, setMarkdownToPreview] = useState<string>(initialValue);

+ 4 - 2
packages/app/src/components/PageEditor/CodeMirrorEditor.jsx

@@ -131,8 +131,10 @@ class CodeMirrorEditor extends AbstractEditor {
     this.handleCtrlEnterKey = this.handleCtrlEnterKey.bind(this);
 
     this.scrollCursorIntoViewHandler = this.scrollCursorIntoViewHandler.bind(this);
+    this.scrollCursorIntoViewHandlerThrottled = throttle(500, this.scrollCursorIntoViewHandler);
     this.pasteHandler = this.pasteHandler.bind(this);
     this.cursorHandler = this.cursorHandler.bind(this);
+    this.cursorHandlerDebounced = debounce(200, throttle(500, this.cursorHandler));
     this.changeHandler = this.changeHandler.bind(this);
     this.turnOnEmojiPickerMode = this.turnOnEmojiPickerMode.bind(this);
     this.turnOffEmojiPickerMode = this.turnOffEmojiPickerMode.bind(this);
@@ -1091,7 +1093,7 @@ class CodeMirrorEditor extends AbstractEditor {
             },
             lint,
           }}
-          onCursor={this.cursorHandler}
+          onCursor={this.cursorHandlerDebounced}
           onScroll={(editor, data) => {
             if (this.props.onScroll != null) {
             // add line data
@@ -1109,7 +1111,7 @@ class CodeMirrorEditor extends AbstractEditor {
           onKeyPress={this.keyPressHandler}
           onKeyDown={this.keyDownHandler}
           onPasteFiles={this.pasteHandler}
-          onScrollCursorIntoView={this.scrollCursorIntoViewHandler}
+          onScrollCursorIntoView={this.scrollCursorIntoViewHandlerThrottled}
         />
 
         { this.renderLoadingKeymapOverlay() }

+ 1 - 1
packages/app/src/components/SubscribeButton.tsx

@@ -42,7 +42,7 @@ const SubscribeButton: FC<Props> = (props: Props) => {
         <i className={`fa ${isSubscribing ? 'fa-bell' : 'fa-bell-slash-o'}`}></i>
       </button>
 
-      <UncontrolledTooltip placement="top" target="subscribe-button" fade={false}>
+      <UncontrolledTooltip data-testid="subscribe-button-tooltip" placement="top" target="subscribe-button" fade={false}>
         {t(getTooltipMessage())}
       </UncontrolledTooltip>
     </>

+ 1 - 1
packages/app/src/components/User/SeenUserInfo.tsx

@@ -41,7 +41,7 @@ const SeenUserInfo: FC<Props> = (props: Props) => {
           </div>
         </PopoverBody>
       </Popover>
-      <UncontrolledTooltip placement="top" target="btn-seen-user" fade={false}>
+      <UncontrolledTooltip data-testid="seen-user-info-tooltip" placement="top" target="btn-seen-user" fade={false}>
         {t('tooltip.footprints')}
       </UncontrolledTooltip>
     </div>

+ 8 - 3
packages/app/src/pages/[[...path]].page.tsx

@@ -63,7 +63,7 @@ import {
   useIsEnabledStaleNotification, useIsIdenticalPath,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing,
   useDrawioUri, useHackmdUri, useDefaultIndentSize, useIsIndentSizeForced,
-  useIsAclEnabled, useIsSearchPage, useTemplateTagData,
+  useIsAclEnabled, useIsSearchPage, useTemplateTagData, useTemplateBodyData, useIsEnabledAttachTitleHeader,
   useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname,
   useIsSlackConfigured, useRendererConfig, useEditingMarkdown,
   useEditorConfig, useIsAllReplyShown, useIsUploadableFile, useIsUploadableImage, useCustomizedLogoSrc, useIsContainerFluid,
@@ -164,6 +164,7 @@ type Props = CommonProps & {
   isContainerFluid: boolean,
   editorConfig: EditorConfig,
   isEnabledStaleNotification: boolean,
+  isEnabledAttachTitleHeader: boolean,
   // isEnabledLinebreaks: boolean,
   // isEnabledLinebreaksInComments: boolean,
   adminPreferredIndentSize: number,
@@ -218,7 +219,9 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   useIsSearchPage(false);
 
   useTemplateTagData(props.templateTagData);
+  useTemplateBodyData(props.templateBodyData);
 
+  useIsEnabledAttachTitleHeader(props.isEnabledAttachTitleHeader);
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
@@ -251,7 +254,8 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   useCurrentPathname(props.currentPathname);
 
   const { data: currentPage } = useSWRxCurrentPage(undefined, pageWithMeta?.data ?? null); // store initial data
-  useEditingMarkdown(pageWithMeta?.data.revision?.body ?? props.templateBodyData ?? '');
+
+  useEditingMarkdown(pageWithMeta?.data.revision?.body);
 
   const { data: grantData } = useSWRxIsGrantNormalized(pageId);
   const { mutate: mutateSelectedGrant } = useSelectedGrant();
@@ -474,7 +478,6 @@ async function injectRoutingInformation(context: GetServerSidePropsContext, prop
   }
   else {
     props.isNotFound = page.isEmpty;
-
     // /62a88db47fed8b2d94f30000 ==> /path/to/page
     if (isPermalink && page.isEmpty) {
       props.currentPathname = page.path;
@@ -538,6 +541,8 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
   props.adminPreferredIndentSize = configManager.getConfig('markdown', 'markdown:adminPreferredIndentSize');
   props.isIndentSizeForced = configManager.getConfig('markdown', 'markdown:isIndentSizeForced');
 
+  props.isEnabledAttachTitleHeader = configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader');
+
   props.rendererConfig = {
     isEnabledLinebreaks: configManager.getConfig('markdown', 'markdown:isEnabledLinebreaks'),
     isEnabledLinebreaksInComments: configManager.getConfig('markdown', 'markdown:isEnabledLinebreaksInComments'),

+ 13 - 3
packages/app/src/pages/_document.page.tsx

@@ -6,25 +6,35 @@ import Document, {
   Html, Head, Main, NextScript,
 } from 'next/document';
 
+import { CrowiRequest } from '~/interfaces/crowi-request';
+
 
 // type GrowiDocumentProps = {};
 // declare type GrowiDocumentInitialProps = GrowiDocumentProps & DocumentInitialProps;
-declare type GrowiDocumentInitialProps = DocumentInitialProps;
+declare type GrowiDocumentInitialProps = DocumentInitialProps & { customCss: string };
 
 
-class GrowiDocument extends Document {
+class GrowiDocument extends Document<GrowiDocumentInitialProps> {
 
   static override async getInitialProps(ctx: DocumentContext): Promise<GrowiDocumentInitialProps> {
     const initialProps: DocumentInitialProps = await Document.getInitialProps(ctx);
+    const { crowi } = ctx.req as CrowiRequest<any>;
+    const { customizeService } = crowi;
+    const customCss: string = customizeService.getCustomCss();
 
-    return initialProps;
+    const props = { ...initialProps, customCss };
+    return props;
   }
 
   override render(): JSX.Element {
+    const { customCss } = this.props;
 
     return (
       <Html>
         <Head>
+          <style>
+            {customCss}
+          </style>
           {/*
           {renderScriptTagsByGroup('basis')}
           {renderStyleTagsByGroup('basis')}

+ 20 - 0
packages/app/src/pages/tags.page.tsx

@@ -7,10 +7,14 @@ import dynamic from 'next/dynamic';
 import Head from 'next/head';
 
 import type { CrowiRequest } from '~/interfaces/crowi-request';
+import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 import type { IDataTagCount } from '~/interfaces/tag';
 import type { IUserUISettings } from '~/interfaces/user-ui-settings';
 import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import { useSWRxTagsList } from '~/stores/tag';
+import {
+  usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
+} from '~/stores/ui';
 
 import { BasicLayout } from '../components/Layout/BasicLayout';
 import {
@@ -30,7 +34,12 @@ type Props = CommonProps & {
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,
   isSearchScopeChildrenAsDefault: boolean,
+
+  // ui
   userUISettings?: IUserUISettings
+
+  // sidebar
+  sidebarConfig: ISidebarConfig,
 };
 
 const TagList = dynamic(() => import('~/components/TagList'), { ssr: false });
@@ -58,6 +67,12 @@ const TagPage: NextPage<CommonProps> = (props: Props) => {
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
 
+  usePreferDrawerModeByUser(props.userUISettings?.preferDrawerModeByUser ?? props.sidebarConfig.isSidebarDrawerMode);
+  usePreferDrawerModeOnEditByUser(props.userUISettings?.preferDrawerModeOnEditByUser);
+  useSidebarCollapsed(props.userUISettings?.isSidebarCollapsed ?? props.sidebarConfig.isSidebarClosedAtDockMode);
+  useCurrentSidebarContents(props.userUISettings?.currentSidebarContents);
+  useCurrentProductNavWidth(props.userUISettings?.currentProductNavWidth);
+
   return (
     <>
       <Head>
@@ -117,6 +132,11 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
   props.isSearchServiceConfigured = searchService.isConfigured;
   props.isSearchServiceReachable = searchService.isReachable;
   props.isSearchScopeChildrenAsDefault = configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault');
+
+  props.sidebarConfig = {
+    isSidebarDrawerMode: configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
+    isSidebarClosedAtDockMode: configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
+  };
 }
 
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {

+ 6 - 5
packages/app/src/stores/context.tsx

@@ -1,12 +1,9 @@
-import { IUser, pagePathUtils } from '@growi/core';
-import { HtmlElementNode } from 'rehype-toc';
-import { Key, SWRResponse, useSWRConfig } from 'swr';
+import { IUser } from '@growi/core';
+import { Key, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
-
 import { SupportedActionType } from '~/interfaces/activity';
 import { EditorConfig } from '~/interfaces/editor-settings';
-// import { CustomWindow } from '~/interfaces/global';
 import { RendererConfig } from '~/interfaces/services/renderer';
 import { GrowiThemes } from '~/interfaces/theme';
 import InterceptorManager from '~/services/interceptor-manager';
@@ -72,6 +69,10 @@ export const useTemplateTagData = (initialData?: string[]): SWRResponse<string[]
   return useContextSWR<string[], Error>('templateTagData', initialData);
 };
 
+export const useTemplateBodyData = (initialData?: string): SWRResponse<string, Error> => {
+  return useContextSWR<string, Error>('templateBodyData', initialData);
+};
+
 export const useIsSharedUser = (initialData?: boolean): SWRResponse<boolean, Error> => {
   return useContextSWR<boolean, Error>('isSharedUser', initialData);
 };

+ 14 - 5
packages/app/src/stores/ui.tsx

@@ -96,18 +96,27 @@ const getClassNamesByEditorMode = (editorMode: EditorMode | undefined, isSidebar
   return classNames;
 };
 
+export const EditorModeHash = {
+  View: '',
+  Edit: '#edit',
+  HackMD: '#hackmd',
+} as const;
+export type EditorModeHash = typeof EditorModeHash[keyof typeof EditorModeHash];
+
+export const isEditorModeHash = (hash: string): hash is EditorModeHash => Object.values<string>(EditorModeHash).includes(hash);
+
 const updateHashByEditorMode = (newEditorMode: EditorMode) => {
   const { pathname, search } = window.location;
 
   switch (newEditorMode) {
     case EditorMode.View:
-      window.history.replaceState(null, '', `${pathname}${search}`);
+      window.history.replaceState(null, '', `${pathname}${search}${EditorModeHash.View}`);
       break;
     case EditorMode.Editor:
-      window.history.replaceState(null, '', `${pathname}${search}#edit`);
+      window.history.replaceState(null, '', `${pathname}${search}${EditorModeHash.Edit}`);
       break;
     case EditorMode.HackMD:
-      window.history.replaceState(null, '', `${pathname}${search}#hackmd`);
+      window.history.replaceState(null, '', `${pathname}${search}${EditorModeHash.HackMD}`);
       break;
   }
 };
@@ -120,9 +129,9 @@ export const determineEditorModeByHash = (): EditorMode => {
   const { hash } = window.location;
 
   switch (hash) {
-    case '#edit':
+    case EditorModeHash.Edit:
       return EditorMode.Editor;
-    case '#hackmd':
+    case EditorModeHash.HackMD:
       return EditorMode.HackMD;
     default:
       return EditorMode.View;

+ 45 - 34
packages/app/test/cypress/integration/20-basic-features/access-to-page.spec.ts

@@ -11,27 +11,33 @@ context('Access to page', () => {
   });
 
   it('/Sandbox is successfully loaded', () => {
-    cy.visit('/Sandbox', {  });
-    cy.screenshot(`${ssPrefix}-sandbox`);
-  });
-
-  it('/Sandbox with anchor hash is successfully loaded', () => {
-    cy.visit('/Sandbox#Headers');
+    cy.visit('/Sandbox');
     cy.waitUntilSkeletonDisappear();
 
     // for check download toc data
     cy.get('.toc-link').should('be.visible');
 
-    // hide fab // disable fab for sticky-events warning
-    // cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
+    cy.screenshot(`${ssPrefix}-sandbox`);
+  });
 
-    // remove animation for screenshot
-    // remove 'blink' class because ::after element cannot be operated
-    // https://stackoverflow.com/questions/5041494/selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-usin/21709814#21709814
-    cy.get('#mdcont-headers').invoke('removeClass', 'blink');
+  // TODO: https://redmine.weseek.co.jp/issues/109939
+  // it('/Sandbox with anchor hash is successfully loaded', () => {
+  //   cy.visit('/Sandbox#Headers');
+  //   cy.waitUntilSkeletonDisappear();
 
-    cy.screenshot(`${ssPrefix}-sandbox-headers`);
-  });
+  //   // for check download toc data
+  //   cy.get('.toc-link').should('be.visible');
+
+  //   // hide fab // disable fab for sticky-events warning
+  //   // cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
+
+  //   // remove animation for screenshot
+  //   // remove 'blink' class because ::after element cannot be operated
+  //   // https://stackoverflow.com/questions/5041494/selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-usin/21709814#21709814
+  //   cy.get('#mdcont-headers').invoke('removeClass', 'blink');
+
+  //   cy.screenshot(`${ssPrefix}-sandbox-headers`);
+  // });
 
   it('/Sandbox/Math is successfully loaded', () => {
     cy.visit('/Sandbox/Math');
@@ -47,18 +53,20 @@ context('Access to page', () => {
     cy.visit('/Sandbox');
     cy.waitUntilSkeletonDisappear();
 
-    cy.get('#grw-subnav-container', { timeout: 30000 }).should('be.visible').within(() => {
+    cy.get('#grw-subnav-container').should('be.visible').within(() => {
 
       // eslint-disable-next-line cypress/no-unnecessary-waiting
       cy.wait(2000);
-      cy.getByTestid('editor-button', { timeout: 30000 }).should('be.visible').click();
+      cy.getByTestid('editor-button').should('be.visible').click();
     })
-    cy.getByTestid('navbar-editor', { timeout: 30000 }).should('be.visible');
+    cy.getByTestid('navbar-editor').should('be.visible');
+    cy.get('.grw-editor-navbar-bottom').should('be.visible');
+
     cy.screenshot(`${ssPrefix}-Sandbox-edit-page`);
   })
 
   it('/user/admin is successfully loaded', () => {
-    cy.visit('/user/admin', {  });
+    cy.visit('/user/admin');
 
     cy.waitUntilSkeletonDisappear();
     // for check download toc data
@@ -86,7 +94,7 @@ context('Access to /me page', () => {
   });
 
   it('/me is successfully loaded', () => {
-    cy.visit('/me', {  });
+    cy.visit('/me');
     // eslint-disable-next-line cypress/no-unnecessary-waiting
     cy.wait(500); // wait loading image
     cy.screenshot(`${ssPrefix}-me`);
@@ -99,8 +107,6 @@ context('Access to /me page', () => {
 
 });
 
-
-
 context('Access to special pages', () => {
   const ssPrefix = 'access-to-special-pages-';
 
@@ -114,24 +120,29 @@ context('Access to special pages', () => {
   });
 
   it('/trash is successfully loaded', () => {
-    cy.visit('/trash', {  });
-    cy.getByTestid('trash-page-list').should('be.visible');
+    cy.visit('/trash');
+
+    cy.getByTestid('trash-page-list').contains('There are no pages under this page.');
+
     cy.screenshot(`${ssPrefix}-trash`);
   });
 
-  it('/tags is successfully loaded', () => {
-
+  it('/tags is successfully loaded', { scrollBehavior: false } ,() => {
     // open sidebar
-    cy.collapseSidebar(false);
+    // cy.collapseSidebar(false);
 
     cy.visit('/tags');
-    // select tags
-    cy.getByTestid('grw-sidebar-nav-primary-tags').click();
-    cy.getByTestid('grw-sidebar-content-tags').should('be.visible');
-    cy.getByTestid('grw-tags-list').should('be.visible');
-    cy.getByTestid('grw-tags-list').contains('You have no tag, You can set tags on pages');
 
-    cy.getByTestid('tags-page').should('be.visible');
+    // cy.getByTestid('grw-sidebar-content-tags').within(() => {
+    //   cy.getByTestid('grw-tags-list').should('be.visible');
+    //   cy.getByTestid('grw-tags-list').contains('You have no tag, You can set tags on pages');
+    // })
+
+    cy.getByTestid('tags-page').within(() => {
+      cy.getByTestid('grw-tags-list').should('be.visible');
+      cy.getByTestid('grw-tags-list').contains('You have no tag, You can set tags on pages');
+    });
+
     cy.screenshot(`${ssPrefix}-tags`);
   });
 
@@ -223,12 +234,12 @@ context('Access to /me/all-in-app-notifications', () => {
     cy.getByTestid('grw-in-app-notification-page').should('be.visible');
     cy.getByTestid('grw-in-app-notification-page-spinner').should('not.exist');
 
-    cy.screenshot(`${ssPrefix}-see-all`, { capture: 'viewport' });
+    cy.screenshot(`${ssPrefix}-see-all`);
 
     cy.get('.grw-custom-nav-tab > div > ul > li:nth-child(2) > a').click();
     cy.getByTestid('grw-in-app-notification-page-spinner').should('not.exist');
 
-    cy.screenshot(`${ssPrefix}-see-unread`, { capture: 'viewport' });
+    cy.screenshot(`${ssPrefix}-see-unread`);
    });
 
 })

+ 23 - 4
packages/app/test/cypress/integration/20-basic-features/access-to-pagelist.spec.ts

@@ -11,8 +11,18 @@ context('Access to pagelist', () => {
 
   it('Page list modal is successfully opened ', () => {
     cy.visit('/');
+    cy.waitUntilSkeletonDisappear();
+
     cy.getByTestid('pageListButton').click({force: true});
-    cy.getByTestid('page-accessories-modal').should('be.visible').screenshot(`${ssPrefix}1-open-pagelist-modal`);
+    cy.getByTestid('page-accessories-modal').parent().should('have.class','show');
+    cy.getByTestid('page-list-item-L').should('be.visible');
+
+    // Wait until the string "You cannot see this page" is no longer displayed
+    cy.getByTestid('page-list-item-L').eq(0).within(() => {
+      cy.get('.icon-exclamation').should('not.exist');
+    });
+
+    cy.screenshot(`${ssPrefix}1-open-pagelist-modal`);
   });
 
   it('Successfully duplicate a page from page list', () => {
@@ -45,20 +55,29 @@ context('Access to pagelist', () => {
 
   it('Successfully expand and close modal', () => {
     cy.visit('/');
+
+    cy.waitUntilSkeletonDisappear();
     cy.getByTestid('pageListButton').click({force: true});
     cy.getByTestid('page-accessories-modal').parent().should('have.class','show');
-    cy.screenshot(`${ssPrefix}6-page-list-modal-size-normal`, {capture: 'viewport'});
+    cy.getByTestid('page-list-item-L').should('be.visible');
+
+    // Wait until the string "You cannot see this page" is no longer displayed
+    cy.getByTestid('page-list-item-L').eq(0).within(() => {
+      cy.get('.icon-exclamation').should('not.exist');
+    });
+
+    cy.screenshot(`${ssPrefix}6-page-list-modal-size-normal`);
     cy.getByTestid('page-accessories-modal').parent().should('have.class','show').within(() => {
       cy.get('button.close').eq(0).click();
     });
 
-    cy.screenshot(`${ssPrefix}7-page-list-modal-size-fullscreen`, {capture: 'viewport'});
+    cy.screenshot(`${ssPrefix}7-page-list-modal-size-fullscreen`);
 
     cy.getByTestid('page-accessories-modal').parent().should('have.class','show').within(() => {
       cy.get('button.close').eq(1).click();
     });
 
-    cy.screenshot(`${ssPrefix}8-close-page-list-modal`, {capture: 'viewport'});
+    cy.screenshot(`${ssPrefix}8-close-page-list-modal`);
   });
 });
 

+ 121 - 59
packages/app/test/cypress/integration/20-basic-features/click-page-icons.spec.ts

@@ -12,83 +12,145 @@ context('Click page icons button', () => {
 
   it('Successfully subscribe/unsubscribe a page', () => {
     cy.visit('/Sandbox');
-    cy.get('#grw-subnav-container').within(() => {
-      // Subscribe
-      cy.get('#subscribe-button').click({force: true});
-      cy.get('#subscribe-button').should('have.class', 'active');
-      cy.screenshot(`${ssPrefix}1-subscribe-page`);
-
-      // Unsubscribe
-      cy.get('#subscribe-button.active').click({force: true});
-      cy.get('#subscribe-button').should('not.have.class', 'active');
-      cy.screenshot(`${ssPrefix}2-unsubscribe-page`);
-    });
+    cy.waitUntilSkeletonDisappear();
+
+    // Subscribe
+    cy.get('#subscribe-button').click({force: true});
+    cy.get('#subscribe-button').should('have.class', 'active');
+
+    // position of the element is not fixed to be displayed, so the element is removed
+    cy.get('body').then($body => {
+      if ($body.find('[data-testid="subscribe-button-tooltip"]').length > 0) {
+        cy.getByTestid('subscribe-button-tooltip').invoke('remove');
+      }
+    })
+    cy.getByTestid('subscribe-button-tooltip').should('not.exist');
+
+    cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}1-subscribe-page`) })
+
+    // Unsubscribe
+    cy.get('#subscribe-button').click({force: true});
+    cy.get('#subscribe-button').should('not.have.class', 'active');
+
+    // position of the element is not fixed to be displayed, so the element is removed
+    cy.get('body').then($body => {
+      if ($body.find('[data-testid="subscribe-button-tooltip"]').length > 0) {
+        cy.getByTestid('subscribe-button-tooltip').invoke('remove');
+      }
+    })
+    cy.getByTestid('subscribe-button-tooltip').should('not.exist');
+
+    cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}2-unsubscribe-page`) })
   });
 
   it('Successfully Like / Dislike a page', () => {
     cy.visit('/Sandbox');
-    cy.get('#grw-subnav-container').within(() => {
-      cy.get('#like-button').click({force: true});
-      cy.get('#like-button').should('have.class', 'active');
-      cy.screenshot(`${ssPrefix}3-like-page`);
-      cy.get('#po-total-likes').click({force: true});
-    });
-    cy.get('.user-list-popover').should('be.visible');
+    cy.waitUntilSkeletonDisappear();
 
-    cy.get('#grw-subnav-container').within(() => {
-      cy.screenshot(`${ssPrefix}4-likes-counter`);
-      cy.get('#like-button.active').click({force: true});
-      cy.get('#like-button').should('not.have.class', 'active');
-      cy.screenshot(`${ssPrefix}5-dislike-page`);
-      cy.get('#po-total-likes').click({force: true});
-    });
+    // like
+    cy.get('#like-button').click({force: true});
+    cy.get('#like-button').should('have.class', 'active');
 
-    cy.get('.user-list-popover').should('be.visible');
+    // position of the element is not fixed to be displayed, so the element is removed
+    cy.get('body').then($body => {
+      if ($body.find('[data-testid="like-button-tooltip"]').length > 0) {
+        cy.getByTestid('like-button-tooltip').invoke('remove');
+      }
+    })
+    cy.getByTestid('like-button-tooltip').should('not.exist');
 
-    cy.get('#grw-subnav-container').within(() => {
-      cy.screenshot(`${ssPrefix}6-likes-counter`);
-    });
+    cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}3-like-page`) });
+
+    // total liker (user-list-popover is commented out because it is sometimes displayed and sometimes not.)
+    // cy.get('#po-total-likes').click({force: true});
+    // cy.get('.user-list-popover').should('be.visible')
+    // cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}4-likes-counter`) });
+
+    // unlike
+    cy.get('#like-button').click({force: true});
+    cy.get('#like-button').should('not.have.class', 'active');
+
+    // position of the element is not fixed to be displayed, so the element is removed
+    cy.get('body').then($body => {
+      if ($body.find('[data-testid="like-button-tooltip"]').length > 0) {
+        cy.getByTestid('like-button-tooltip').invoke('remove');
+      }
+    })
+    cy.getByTestid('like-button-tooltip').should('not.exist');
+
+    cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}5-dislike-page`) });
+
+    // total liker (user-list-popover is commented out because it is sometimes displayed and sometimes not.)
+    // cy.get('#po-total-likes').click({force: true});
+    // cy.get('.user-list-popover').should('be.visible');
+    // cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}6-likes-counter`) });
   });
 
   it('Successfully Bookmark / Unbookmark a page', () => {
     cy.visit('/Sandbox');
-    cy.get('#grw-subnav-container').within(() => {
-      cy.get('#bookmark-button').click({force: true});
-      cy.get('#bookmark-button').should('have.class', 'active');
-      cy.screenshot(`${ssPrefix}7-bookmark-page`);
-      cy.get('#po-total-bookmarks').click({force: true});
-    });
-    cy.get('.user-list-popover').should('be.visible');
+    cy.waitUntilSkeletonDisappear();
 
-    cy.get('#grw-subnav-container').within(() => {
-      cy.screenshot(`${ssPrefix}8-bookmarks-counter`);
-      cy.get('#bookmark-button.active').click({force: true});
-      cy.get('#bookmark-button').should('not.have.class', 'active');
-      cy.screenshot(`${ssPrefix}9-unbookmark-page`);
-      cy.get('#po-total-bookmarks').click({force: true});
-    });
+    // bookmark
+    cy.get('#bookmark-button').click({force: true});
+    cy.get('#bookmark-button').should('have.class', 'active');
+
+    // position of the element is not fixed to be displayed, so the element is removed
+    cy.get('body').then($body => {
+      if ($body.find('[data-testid="bookmark-button-tooltip"]').length > 0) {
+        cy.getByTestid('bookmark-button-tooltip').invoke('remove');
+      }
+    })
+    cy.getByTestid('bookmark-button-tooltip').should('not.exist');
 
+    cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}7-bookmark-page`) });
+
+    // total bookmarker
+    cy.get('#po-total-bookmarks').click({force: true});
     cy.get('.user-list-popover').should('be.visible');
+    cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}8-bookmarks-counter`) });
 
-    cy.get('#grw-subnav-container').within(() => {
-      cy.screenshot(`${ssPrefix}10-bookmarks-counter`);
-    });
-  });
+    // unbookmark
+    cy.get('#bookmark-button').click({force: true});
+    cy.get('#bookmark-button').should('not.have.class', 'active');
 
-  it('Successfully display list of "seen by user"', () => {
-    cy.visit('/Sandbox');
-    cy.waitUntilSkeletonDisappear();
-    // eslint-disable-next-line cypress/no-unnecessary-waiting
-    cy.wait(2000); // wait for get method
-    cy.get('#grw-subnav-container').within(() => {
-      cy.get('div.grw-seen-user-info').find('button#btn-seen-user').click({force: true});
-    });
+    // position of the element is not fixed to be displayed, so the element is removed
+    cy.get('body').then($body => {
+      if ($body.find('[data-testid="bookmark-button-tooltip"]').length > 0) {
+        cy.getByTestid('bookmark-button-tooltip').invoke('remove');
+      }
+    })
+    cy.getByTestid('bookmark-button-tooltip').should('not.exist');
 
-    cy.get('.user-list-popover').should('be.visible')
+    cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}9-unbookmark-page`) });
 
-    cy.get('#grw-subnav-container').within(() => {
-      cy.screenshot(`${ssPrefix}11-seen-user-list`);
-    });
+    // total bookmarker
+    cy.get('#po-total-bookmarks').click({force: true});
+    cy.get('.user-list-popover').should('be.visible');
+    cy.get('#grw-subnav-container').within(() => { cy.screenshot(`${ssPrefix}10-bookmarks-counter`) });
   });
 
+  // user-list-popover is commented out because it is sometimes displayed and sometimes not
+  // it('Successfully display list of "seen by user"', () => {
+  //   cy.visit('/Sandbox');
+  //   cy.waitUntilSkeletonDisappear();
+
+  //   cy.get('#grw-subnav-container').within(() => {
+  //     cy.get('div.grw-seen-user-info').find('button#btn-seen-user').click({force: true});
+  //   });
+
+  //   // position of the element is not fixed to be displayed, so the element is removed
+  //   cy.get('body').then($body => {
+  //     if ($body.find('[data-testid="seen-user-info-tooltip"]').length > 0) {
+  //       cy.getByTestid('seen-user-info-tooltip').invoke('remove');
+  //     }
+  //   })
+  //   cy.getByTestid('seen-user-info-tooltip').should('not.exist');
+
+  //   cy.get('.user-list-popover').should('be.visible')
+
+  //   cy.get('#grw-subnav-container').within(() => {
+  //     cy.screenshot(`${ssPrefix}11-seen-user-list`);
+  //   });
+  // });
+
 });

+ 31 - 11
packages/app/test/cypress/integration/30-search/search.spec.ts

@@ -92,6 +92,8 @@ context('Search all pages', () => {
     const searchText = 'help';
 
     cy.visit('/');
+    cy.waitUntilSkeletonDisappear();
+
     cy.get('.rbt-input').click();
     cy.get('.rbt-menu.dropdown-menu.show').should('be.visible').within(() => {
       cy.screenshot(`${ssPrefix}1-search-input-focused`);
@@ -158,14 +160,17 @@ context('Search all pages', () => {
     cy.waitUntilSkeletonDisappear();
 
     // Add tag
-    cy.get('#edit-tags-btn-wrapper-for-tooltip > a').click({force: true});
-    cy.get('#edit-tag-modal').should('be.visible');
+    cy.get('#edit-tags-btn-wrapper-for-tooltip').as('edit-tag-tooltip').should('be.visible');
+    cy.get('@edit-tag-tooltip').within(()=>{
+      cy.get('a').should('be.visible').click();
+    })
+    cy.get('#edit-tag-modal').as('tag-modal').should('be.visible');
 
-    cy.get('#edit-tag-modal').within(() => {
+    cy.get('@tag-modal').within(() => {
       cy.get('.rbt-input-main').type(tag);
       cy.get('#tag-typeahead-asynctypeahead').should('be.visible');
       cy.get('#tag-typeahead-asynctypeahead-item-0').should('be.visible');
-      cy.get('a#tag-typeahead-asynctypeahead-item-0').click({force: true})
+      cy.get('a#tag-typeahead-asynctypeahead-item-0').click()
     });
 
     cy.get('#edit-tag-modal').within(() => {
@@ -173,7 +178,10 @@ context('Search all pages', () => {
     });
 
     cy.visit('/');
-    cy.get('.rbt-input').click({force: true});
+    cy.waitUntilSkeletonDisappear();
+
+    cy.get('.rbt-input').should('be.visible');
+    cy.get('.rbt-input').click();
     cy.get('.rbt-input-main').type(`${searchText}`);
     cy.screenshot(`${ssPrefix}1-insert-search-text-with-tag`, { capture: 'viewport'});
     cy.get('.rbt-input-main').type('{enter}');
@@ -182,11 +190,12 @@ context('Search all pages', () => {
     cy.getByTestid('search-result-list').should('be.visible');
     cy.getByTestid('search-result-content').should('be.visible');
     cy.get('.wiki').should('be.visible');
+    cy.waitUntilSpinnerDisappear();
+
     // force to add 'active' to pass VRT: https://github.com/weseek/growi/pull/6603
     cy.getByTestid('page-list-item-L').first().invoke('addClass', 'active');
     cy.screenshot(`${ssPrefix}2-search-with-tag-result`, {capture: 'viewport'});
 
-    cy.getByTestid('open-page-item-control-btn').first().click();
     cy.getByTestid('search-result-content').should('be.visible');
     cy.get('.wiki').should('be.visible');
     cy.screenshot(`${ssPrefix}3-click-three-dots-menu-search-with-tag`, {capture: 'viewport'});
@@ -202,6 +211,8 @@ context('Search all pages', () => {
     cy.getByTestid('search-result-list').should('be.visible');
     cy.getByTestid('search-result-content').should('be.visible');
     cy.get('.wiki').should('be.visible');
+    cy.waitUntilSpinnerDisappear();
+
     // force to add 'active' to pass VRT: https://github.com/weseek/growi/pull/6603
     cy.getByTestid('page-list-item-L').first().invoke('addClass', 'active');
     cy.screenshot(`${ssPrefix}1-tag-order-click-tag-name`, {capture: 'viewport'});
@@ -237,6 +248,8 @@ context('Search all pages', () => {
     cy.getByTestid('search-result-list').should('be.visible');
     cy.getByTestid('search-result-content').should('be.visible');
     cy.get('.wiki').should('be.visible');
+    cy.waitUntilSpinnerDisappear();
+
     cy.screenshot(`${ssPrefix}4-tag-order-by-last-update-date`);
   });
 
@@ -257,8 +270,10 @@ context('Search current tree with "prefix":', () => {
   it(`Search current tree by word is successfully loaded`, () => {
     const searchText = 'help';
     cy.visit('/');
-    cy.getByTestid('select-search-scope').first().click({force: true});
-    cy.get('.input-group-prepend.show > div > button:nth-child(2)').click({force: true});
+    cy.waitUntilSkeletonDisappear();
+
+    cy.getByTestid('select-search-scope').click();
+    cy.get('.input-group-prepend.show > div > button:nth-child(2)').click();
     cy.get('.rbt-input').click();
     cy.get('.rbt-menu.dropdown-menu.show').should('be.visible').within(() => {
       cy.screenshot(`${ssPrefix}1-search-input-focused`);
@@ -271,6 +286,8 @@ context('Search current tree with "prefix":', () => {
     cy.getByTestid('search-result-list').should('be.visible');
     cy.getByTestid('search-result-content').should('be.visible');
     cy.get('.wiki').should('be.visible');
+    cy.waitUntilSpinnerDisappear();
+
     // force to add 'active' to pass VRT: https://github.com/weseek/growi/pull/6603
     cy.getByTestid('page-list-item-L').first().invoke('addClass', 'active');
     // for avoid mismatch by auto scrolling
@@ -279,11 +296,14 @@ context('Search current tree with "prefix":', () => {
     cy.wait(1500);
     cy.screenshot(`${ssPrefix}3-search-page-results`, { capture: 'viewport'});
 
-    cy.getByTestid('open-page-item-control-btn').first().click();
-    cy.getByTestid('search-result-content').should('be.visible');
-    cy.get('.wiki').should('be.visible');
+    cy.getByTestid('search-result-list').within(() => {
+      cy.getByTestid('open-page-item-control-btn').first().click();
+    })
+
     // for avoid mismatch by auto scrolling
     cy.get('.search-result-content-body-container').scrollTo('top');
+    // eslint-disable-next-line cypress/no-unnecessary-waiting
+    cy.wait(1500);
     cy.screenshot(`${ssPrefix}4-click-three-dots-menu`, {capture: 'viewport'});
   });
 

+ 2 - 0
packages/app/test/cypress/integration/40-admin/access-to-admin-page.spec.ts

@@ -39,6 +39,8 @@ context('Access to Admin page', () => {
   it('/admin/security is successfully loaded', () => {
     cy.visit('/admin/security');
     cy.getByTestid('admin-security').should('be.visible');
+    cy.get('#isShowRestrictedByOwner').should('be.checked')
+    cy.get('#isShowRestrictedByGroup').should('be.checked')
     cy.screenshot(`${ssPrefix}-admin-security`);
   });
 

+ 234 - 176
packages/app/test/cypress/integration/50-sidebar/access-to-side-bar.spec.ts

@@ -1,191 +1,249 @@
-context('Access to sidebar', () => {
+describe('Access to sidebar', () => {
   const ssPrefix = 'access-to-sidebar-';
 
-  beforeEach(() => {
-    // login
-    cy.fixture("user-admin.json").then(user => {
-      cy.login(user.username, user.password);
-    });
-    // collapse sidebar
-    cy.collapseSidebar(false);
-  });
-
-  it('Successfully show/collapse sidebar', () => {
-    cy.visit('/');
-    cy.screenshot(`${ssPrefix}-1-sidebar-shown`, {capture: 'viewport'});
-    cy.getByTestid('grw-navigation-resize-button').click({force: true});
-    cy.screenshot(`${ssPrefix}-2-sidebar-collapsed`, {capture: 'viewport'});
-
-  });
-  it('Successfully access recent changes side bar ', () => {
-    cy.visit('/');
-    cy.getByTestid('grw-sidebar-nav-primary-recent-changes').click();
-    cy.getByTestid('grw-contextual-navigation-sub').then(($el) => {
-      if($el.hasClass('d-none')){
-        cy.getByTestid('grw-navigation-resize-button').click({force: true});
-      }
-    });
-
-    cy.getByTestid('grw-recent-changes').should('be.visible');
-    cy.get('.list-group-item').should('be.visible');
-
-    cy.scrollTo('top');
-    cy.screenshot(`${ssPrefix}recent-changes-1-page-list`);
-
-    cy.get('#grw-sidebar-contents-wrapper').within(() => {
-      cy.get('#recentChangesResize').click({force: true});
-      cy.get('.list-group-item').should('be.visible');
-    });
-
-    cy.scrollTo('top');
-    cy.screenshot(`${ssPrefix}recent-changes-2-switch-sidebar-size`);
-  });
-
-  it('Successfully create a custom sidebar page', () => {
-    cy.visit('/');
-    cy.getByTestid('grw-sidebar-nav-primary-custom-sidebar').click();
-    cy.getByTestid('grw-contextual-navigation-sub').then(($el) => {
-      if($el.hasClass('d-none')){
-        cy.getByTestid('grw-navigation-resize-button').click({force: true});
-      }
-    });
-
-    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}custom-sidebar-1-click-on-custom-sidebar`);
-
-    // create /Sidebar contents
-    const content = '# HELLO \n ## Hello\n ### Hello';
-    cy.get('.grw-sidebar-content-header.h5').find('a').click();
-    cy.get('.CodeMirror textarea').type(content, {force: true});
-    cy.screenshot(`${ssPrefix}custom-sidebar-2-custom-sidebar-editor`);
-    cy.getByTestid('save-page-btn').click();
-    cy.get('.layout-root', { timeout: 10000 }).should('not.have.class', 'editing');
-
-    // What to do when UserUISettings is not saved in time
-    cy.getByTestid('grw-sidebar-nav-primary-custom-sidebar').then(($el) => {
-      if (!$el.hasClass('active')) {
-        cy.wrap($el).click();
-      }
-    });
-
-    cy.get('.grw-custom-sidebar-content').should('be.visible');
-    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}custom-sidebar-3-custom-sidebar-created`);
-  });
-
-  it('Successfully performed page operation from "page tree"', () => {
-    cy.visit('/');
-    cy.getByTestid('grw-sidebar-nav-primary-page-tree').click();
-    cy.getByTestid('grw-contextual-navigation-sub').then(($el) => {
-      if($el.hasClass('d-none')){
-        cy.getByTestid('grw-navigation-resize-button').click({force: true});
-      }
-    });
-
-    cy.getByTestid('grw-contextual-navigation-sub').should('be.visible')
-    cy.get('.grw-pagetree-item-children').eq(0).should('be.visible');
-    cy.screenshot(`${ssPrefix}page-tree-1-access-to-page-tree`);
-
-    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}page-tree-2-hide-page-tree-item`);
-    cy.get('.grw-pagetree-triangle-btn').eq(0).click();
-
-    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
-      cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click();
-    });
-
-    cy.screenshot(`${ssPrefix}page-tree-3-click-three-dots-menu`);
-    cy.get('.dropdown-menu.show').should('be.visible').within(() => {
-      cy.getByTestid('add-remove-bookmark-btn').click();
-    });
-    cy.screenshot(`${ssPrefix}page-tree-4-add-bookmark`);
-
-
-    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
-      cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click();
-    });
-    cy.get('.dropdown-menu.show').should('be.visible').within(() => {
-      cy.getByTestid('open-page-duplicate-modal-btn').click();
-    });
-
-    cy.getByTestid('page-duplicate-modal').should('be.visible').within(() => {
-      cy.get('.rbt-input-main').type('_test');
-      cy.screenshot(`${ssPrefix}page-tree-5-duplicate-page`);
-      cy.get('.modal-header > button').click();
-    });
-
-    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
-      cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click()
-    });
-    cy.get('.dropdown-menu.show').should('be.visible').within(() => {
-      cy.getByTestid('open-page-move-rename-modal-btn').click();
-    });
-
-    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
-      cy.getByTestid('closable-text-input').type('_newname');
-    });
-
-    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}page-tree-6-rename-page`);
-    cy.get('body').click(0,0);
-
-    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
-      cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click()
-    });
-    cy.get('.dropdown-menu.show').should('be.visible').within(() => {
-      cy.getByTestid('open-page-delete-modal-btn').click();
+  context('when logged in', () => {
+    beforeEach(() => {
+      // login
+      cy.fixture("user-admin.json").then(user => {
+        cy.login(user.username, user.password);
+      });
     });
 
-    cy.getByTestid('page-delete-modal').should('be.visible').within(() => {
-      cy.screenshot(`${ssPrefix}page-tree-7-delete-page`);
-      cy.get('.modal-header > button').click();
-    });
+    context('when access to root page', { scrollBehavior: false }, () => {
+      beforeEach(() => {
+        cy.visit('/');
+        cy.waitUntilSkeletonDisappear();
+        cy.collapseSidebar(false);
+      });
 
-  });
+      describe('Test show/collapse button', () => {
+        it('Successfully show sidebar', () => {
+          cy.get('.grw-pagetree').should('be.visible');
+          cy.screenshot(`${ssPrefix}1-sidebar-shown`, {
+            capture: 'viewport',
+            // Blackout for recalculation of toc content hight
+            blackout: ['.grw-side-contents-container', '[data-hide-in-vrt=true]'],
+          });
+        });
+
+        it('Successfully collapse sidebar', () => {
+          cy.getByTestid('grw-navigation-resize-button').click({force: true});
+          cy.screenshot(`${ssPrefix}2-sidebar-collapsed`, {
+            capture: 'viewport',
+            // Blackout for recalculation of toc content hight
+            blackout: ['.grw-side-contents-container', '[data-hide-in-vrt=true]'],
+          });
+        });
+      });
 
-  it('Successfully performed page operation from "Tags" ', () => {
-    cy.visit('/');
-    cy.getByTestid('grw-sidebar-nav-primary-tags').click();
-    cy.getByTestid('grw-contextual-navigation-sub').then(($el) => {
-      if($el.hasClass('d-none')){
-        cy.getByTestid('grw-navigation-resize-button').click({force: true});
-      }
-    });
-    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}tags-1-access-to-tags`);
+      describe('Test page tree tab', () => {
+        it('Successfully access to page tree', () => {
+          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.get('.grw-pagetree').should('be.visible');
+            cy.screenshot(`${ssPrefix}page-tree-1-access-to-page-tree`);
+          });
+        });
+
+        it('Successfully hide page tree items', () => {
+          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.get('.grw-pagetree-open').should('be.visible');
+
+            // hide page tree tiems
+            cy.get('.grw-pagetree-triangle-btn').eq(0).click();
+            cy.screenshot(`${ssPrefix}page-tree-2-hide-page-tree-items`);
+          });
+        });
+
+        it('Successfully click Add to Bookmarks button', () => {
+          // click three dots
+          cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+            cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click();
+          });
+
+          cy.getByTestid('page-item-control-menu').should('have.class', 'show');
+          cy.screenshot(`${ssPrefix}page-tree-3-before-click-button`, {
+            // Blackout for recalculation of toc content hight
+            blackout: ['.grw-side-contents-container', '[data-hide-in-vrt=true]'],
+          });
+
+          // click add remove bookmark btn
+          cy.getByTestid('page-item-control-menu').should('have.class', 'show').within(() => {
+            cy.getByTestid('add-remove-bookmark-btn').click();
+          });
+
+          // show dropdown again
+          cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+            cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click();
+          });
+
+          cy.getByTestid('page-item-control-menu').should('have.class', 'show');
+          cy.screenshot(`${ssPrefix}page-tree-4-after-click-button`, {
+            // Blackout for recalculation of toc content hight
+            blackout: ['.grw-side-contents-container', '[data-hide-in-vrt=true]'],
+          });
+        });
+
+        it('Successfully show duplicate page modal', () => {
+          cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+            cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click();
+          });
+          cy.get('.dropdown-menu.show').should('be.visible').within(() => {
+            cy.getByTestid('open-page-duplicate-modal-btn').click();
+          });
+          cy.getByTestid('page-duplicate-modal').should('be.visible').within(() => {
+            cy.get('.rbt-input-main').type('_test');
+            cy.screenshot(`${ssPrefix}page-tree-5-duplicate-page-modal`);
+            cy.get('.modal-header > button').click();
+          });
+        });
+
+        it('Successfully rename page', () => {
+          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+              cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click()
+            });
+            cy.get('.dropdown-menu.show').should('be.visible').within(() => {
+              cy.getByTestid('open-page-move-rename-modal-btn').click();
+            });
+            cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+              cy.getByTestid('closable-text-input').type('_newname');
+            });
+            cy.screenshot(`${ssPrefix}page-tree-6-rename-page`);
+          });
+        });
+
+        it('Successfully show delete page modal', () => {
+          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+              cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click()
+            });
+            cy.get('.dropdown-menu.show').should('be.visible').within(() => {
+              cy.getByTestid('open-page-delete-modal-btn').click();
+            });
+          });
+          cy.getByTestid('page-delete-modal').should('be.visible').within(() => {
+            cy.screenshot(`${ssPrefix}page-tree-7-delete-page-modal`);
+            cy.get('.modal-header > button').click();
+          });
+        });
+      });
 
-    cy.get('.grw-container-convertible > div > .btn-primary').click({force: true});
+      describe('Test custom sidebar tab', () => {
+        it('Successfully access to custom sidebar', () => {
+          cy.getByTestid('grw-sidebar-nav-primary-custom-sidebar').click();
+
+          // eslint-disable-next-line cypress/no-unnecessary-waiting
+          cy.wait(1500); // Wait debounce for UserUISettings update
+
+          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.get('.grw-sidebar-content-header.h5').find('a');
+            cy.screenshot(`${ssPrefix}custom-sidebar-1-access-to-custom-sidebar`);
+          });
+        });
+
+        it('Successfully redirect to editor', () => {
+          const content = '# HELLO \n ## Hello\n ### Hello';
+
+          cy.get('.grw-sidebar-content-header.h5').find('a').click();
+          cy.get('.CodeMirror textarea').type(content, {force: true});
+          cy.screenshot(`${ssPrefix}custom-sidebar-2-redirect-to-editor`);
+          cy.getByTestid('save-page-btn').click();
+          cy.get('.layout-root').should('not.have.class', 'editing');
+        });
+
+        it('Successfully create custom sidebar content', () => {
+          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.get('.grw-custom-sidebar-content').should('be.visible');
+            cy.screenshot(`${ssPrefix}custom-sidebar-3-content-created`);
+          });
+        });
+      });
 
-    // collapse sidebar
-    cy.collapseSidebar(true);
+      describe('Test recent changes tab', () => {
+        it('Successfully access to recent changes', () => {
+          cy.getByTestid('grw-sidebar-nav-primary-recent-changes').click();
+
+          // eslint-disable-next-line cypress/no-unnecessary-waiting
+          cy.wait(1500); // Wait debounce for UserUISettings update
+
+          cy.getByTestid('grw-recent-changes').should('be.visible');
+          cy.get('.list-group-item').should('be.visible');
+
+          // The scope of the capture is not narrowed because the blackout is shifted
+          cy.screenshot(`${ssPrefix}recent-changes-1-access-to-recent-changes`, {
+            // Blackout for recalculation of toc content hight
+            blackout: ['.grw-side-contents-container', '[data-hide-in-vrt=true]'],
+          });
+        });
+
+        it('Successfully switch content size', () => {
+          cy.get('#grw-sidebar-contents-wrapper').within(() => {
+            cy.get('#recentChangesResize').click({force: true});
+            cy.get('.list-group-item').should('be.visible');
+          });
+
+          // The scope of the capture is not narrowed because the blackout is shifted
+          cy.screenshot(`${ssPrefix}recent-changes-2-switch-content-size`, {
+            // Blackout for recalculation of toc content hight
+            blackout: ['.grw-side-contents-container', '[data-hide-in-vrt=true]'],
+          });
+        });
+      });
 
-    cy.screenshot(`${ssPrefix}tags-2-check-all-tags`);
-  });
+      describe('Test tags tab', () => {
+        it('Successfully access to tags', () => {
+          cy.getByTestid('grw-sidebar-nav-primary-tags').click();
+
+          // eslint-disable-next-line cypress/no-unnecessary-waiting
+          cy.wait(1500); // Wait debounce for UserUISettings update
+
+          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.getByTestid('grw-tags-list').should('be.visible');
+            cy.screenshot(`${ssPrefix}tags-1-access-to-tags`);
+          });
+        });
+
+        it('Succesfully click all tags button', () => {
+          cy.get('.grw-container-convertible > div > .btn-primary').click({force: true});
+          cy.collapseSidebar(true);
+          cy.getByTestid('grw-tags-list').should('be.visible');
+          cy.screenshot(`${ssPrefix}tags-2-click-all-tags-button`);
+        });
+      });
 
-  // it('Successfully access to My Drafts page', () => {
-  //   cy.visit('/');
-  //   cy.collapseSidebar(true);
-  //   cy.get('.grw-sidebar-nav-secondary-container').within(() => {
-  //     cy.get('a[href*="/me/drafts"]').click();
-  //   });
-  //   cy.screenshot(`${ssPrefix}access-to-drafts-page`);
-  // });
-  it('Successfully access to GROWI Docs page', () => {
-    cy.visit('/');
-    cy.get('.grw-sidebar-nav-secondary-container').within(() => {
-      cy.get('a[href*="https://docs.growi.org"]').then(($a) => {
-        const url = $a.prop('href')
-        cy.request(url).its('body').should('include', '</html>');
+      // TODO: No Drafts pages on GROWI version 6
+      // it('Successfully access to My Drafts page', () => {
+      //   cy.visit('/');
+      //   cy.collapseSidebar(true);
+      //   cy.get('.grw-sidebar-nav-secondary-container').within(() => {
+      //     cy.get('a[href*="/me/drafts"]').click();
+      //   });
+      //   cy.screenshot(`${ssPrefix}access-to-drafts-page`);
+      // });
+
+      describe('Test access to GROWI Docs page', () => {
+        it('Successfully access to GROWI Docs page', () => {
+          cy.get('.grw-sidebar-nav-secondary-container').within(() => {
+            cy.get('a[href*="https://docs.growi.org"]').then(($a) => {
+              const url = $a.prop('href')
+              cy.request(url).its('body').should('include', '</html>');
+            });
+          });
+        });
       });
-    });
-  });
 
-  it('Successfully access to trash page', () => {
-    cy.visit('/');
-    cy.collapseSidebar(true);
-    cy.get('.grw-sidebar-nav-secondary-container').within(() => {
-      cy.get('a[href*="/trash"]').click();
+      describe('Test access to trash page', () => {
+        it('Successfully access to trash page', () => {
+          cy.collapseSidebar(true);
+          cy.get('.grw-sidebar-nav-secondary-container').within(() => {
+            cy.get('a[href*="/trash"]').click();
+          });
+
+          cy.get('.grw-page-path-hierarchical-link').should('be.visible');
+          cy.get('.grw-custom-nav-tab').should('be.visible');
+          cy.screenshot(`${ssPrefix}access-to-trash-page`);
+        });
+      });
     });
-
-    cy.get('.grw-page-path-hierarchical-link').should('be.visible');
-
-    cy.get('.grw-custom-nav-tab').should('be.visible');
-
-    cy.screenshot(`${ssPrefix}access-to-trash-page`);
   });
 });

+ 8 - 4
packages/app/test/cypress/integration/50-sidebar/switching-sidebar-mode.spec.ts

@@ -25,14 +25,18 @@ context('Switch sidebar mode', () => {
     cy.get('.grw-apperance-mode-dropdown').first().click();
 
     cy.get('[for="swSidebarMode"]').click({force: true});
+    cy.get('.grw-sidebar-nav').should('not.be.visible');
     cy.screenshot(`${ssPrefix}-switch-sidebar-mode`, {
-      blackout: ['#revision-toc', '[data-hide-in-vrt=true]'],
-    })
+      // Blackout for recalculation of toc content hight
+      blackout: ['.grw-side-contents-container', '[data-hide-in-vrt=true]'],
+    });
 
     cy.get('[for="swSidebarMode"]').click({force: true});
+    cy.get('.grw-sidebar-nav').should('be.visible');
     cy.screenshot(`${ssPrefix}-switch-sidebar-mode-back`, {
-      blackout: ['#revision-toc', '[data-hide-in-vrt=true]'],
-    })
+      // Blackout for recalculation of toc content hight
+      blackout: ['.grw-side-contents-container','[data-hide-in-vrt=true]'],
+    });
   });
 
 });

+ 8 - 4
packages/app/test/cypress/integration/60-home/home.spec.ts

@@ -12,15 +12,19 @@ context('Access Home', () => {
 
   it('Visit home', () => {
     cy.visit('/dummy');
-    cy.getByTestid('grw-personal-dropdown').click();
-    cy.getByTestid('grw-personal-dropdown').find('.dropdown-menu .btn-group > .btn-outline-secondary:eq(0)').click();
+    cy.waitUntilSkeletonDisappear();
+    cy.get('.grw-personal-dropdown').as('dropdown').should('be.visible').click()
+    cy.get('@dropdown').within(()=>{
+      cy.getByTestid('personal-dropdown-menu').should('have.css', 'display', 'block');
+    });
+    cy.getByTestid('grw-personal-dropdown-menu-user-home').should('be.visible').click();
+    cy.waitUntilSkeletonDisappear();
 
     // eslint-disable-next-line cypress/no-unnecessary-waiting
     cy.wait(2000); // wait for calcViewHeight and rendering
 
-    cy.waitUntilSkeletonDisappear();
     // for check download toc data
-    cy.get('.toc-link').should('be.visible');
+    cy.get('.toc-link', { timeout: 60000 }).should('be.visible');
 
     // same screenshot is taken in access-to-page.spec
     cy.screenshot(`${ssPrefix}-visit-home`);