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

Merge branch 'fix/enable-link-to-home-at-editor' of https://github.com/weseek/growi into fix/enable-link-to-home-at-editor

Taichi Masuyama 3 лет назад
Родитель
Сommit
e76ad89143

+ 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 - 1
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",

+ 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": "カスタムサイドバー",

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

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

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

@@ -28,7 +28,6 @@
 	"Admin": "管理",
 	"administrator": "管理员",
 	"Tags": "Tags",
-  "New": "新建",
   "Close": "Close",
 	"Shortcuts": "快捷方式",
   "CustomSidebar": "Custom Sidebar",

+ 2 - 20
packages/app/src/components/Admin/MarkdownSetting/XssForm.jsx

@@ -43,26 +43,8 @@ class XssForm extends React.Component {
     return (
       <div className="form-group col-12 my-3">
         <div className="row">
-          <div className="col-md-4 col-sm-12 align-self-start mb-4">
-            <div className="custom-control custom-radio ">
-              <input
-                type="radio"
-                className="custom-control-input"
-                id="xssOption1"
-                name="XssOption"
-                checked={xssOption === 1}
-                onChange={() => { adminMarkDownContainer.setState({ xssOption: 1 }) }}
-              />
-              <label className="custom-control-label w-100" htmlFor="xssOption1">
-                <p className="font-weight-bold">{t('markdown_settings.xss_options.remove_all_tags')}</p>
-                <div className="mt-4">
-                  {t('markdown_settings.xss_options.remove_all_tags_desc')}
-                </div>
-              </label>
-            </div>
-          </div>
 
-          <div className="col-md-4 col-sm-12 align-self-start mb-4">
+          <div className="col-md-6 col-sm-12 align-self-start mb-4">
             <div className="custom-control custom-radio">
               <input
                 type="radio"
@@ -104,7 +86,7 @@ class XssForm extends React.Component {
             </div>
           </div>
 
-          <div className="col-md-4 col-sm-12 align-self-start mb-4">
+          <div className="col-md-6 col-sm-12 align-self-start mb-4">
             <div className="custom-control custom-radio">
               <input
                 type="radio"

+ 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>

+ 1 - 1
packages/app/src/components/PageComment/Comment.tsx

@@ -124,7 +124,7 @@ export const Comment = (props: CommentProps): JSX.Element => {
   }, [comment, isMarkdown, markdown, rendererOptions]);
 
   const rootClassName = getRootClassName(comment);
-  const revHref = `?revision=${comment.revision}`;
+  const revHref = `?revisionId=${comment.revision}`;
   const editedDateId = `editedDate-${comment._id}`;
   const editedDateFormatted = isEdited ? format(updatedAt, 'yyyy/MM/dd HH:mm') : null;
 

+ 11 - 4
packages/app/src/components/PageEditorByHackmd.tsx

@@ -4,8 +4,11 @@ import React, {
 
 import EventEmitter from 'events';
 
+import { pathUtils } from '@growi/core';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
 import { useTranslation } from 'react-i18next';
-
+import urljoin from 'url-join';
 
 import { saveOrUpdate } from '~/client/services/page-operation';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
@@ -29,7 +32,6 @@ import {
 import loggerFactory from '~/utils/logger';
 
 import HackmdEditor from './PageEditorByHackmd/HackmdEditor';
-import { useRouter } from 'next/router';
 
 const logger = loggerFactory('growi:PageEditorByHackmd');
 
@@ -56,6 +58,8 @@ export const PageEditorByHackmd = (): JSX.Element => {
   const { data: grant } = useSelectedGrant();
   const { data: hackmdUri } = useHackmdUri();
 
+  const { returnPathForURL } = pathUtils;
+
   // pageData
   const { data: pageData, mutate: mutatePageData } = useSWRxCurrentPage();
   const revision = pageData?.revision;
@@ -352,8 +356,11 @@ export const PageEditorByHackmd = (): JSX.Element => {
               <div className="card-header bg-warning"><i className="icon-fw icon-info"></i> {t('hackmd.draft_outdated')}</div>
               <div className="card-body text-center">
                 {t('hackmd.based_on_revision')}&nbsp;
-                <a href={`?revision=${revisionIdHackmdSynced}`}><span className="badge badge-secondary">{revisionIdHackmdSynced?.substr(-8)}</span></a>
-
+                { pageData != null && (
+                  <Link href={urljoin(returnPathForURL(pageData.path, pageData._id), `?revisionId=${revisionIdHackmdSynced}`)} prefetch={false}>
+                    <a><span className="badge badge-secondary">{revisionIdHackmdSynced?.substr(-8)}</span></a>
+                  </Link>
+                )}
                 <div className="text-center mt-3">
                   <button
                     className="btn btn-link btn-view-outdated-draft p-0"

+ 1 - 1
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -195,7 +195,7 @@ const ItemsTree = (props: ItemsTreeProps): JSX.Element => {
     };
 
     openDeleteModal([pageToDelete], { onDeleted: onDeletedHandler });
-  }, [advanceDpl, advanceFts, advancePt, currentPagePath, mutateCurrentPage, openDeleteModal, t]);
+  }, [advanceDpl, advanceFts, advancePi, advancePt, currentPagePath, mutateCurrentPage, openDeleteModal, t]);
 
   // ***************************  Scroll on init ***************************
   const scrollOnInit = useCallback(() => {

+ 1 - 7
packages/app/src/pages/[[...path]].page.tsx

@@ -57,7 +57,7 @@ import DisplaySwitcher from '../components/Page/DisplaySwitcher';
 // import PageStatusAlert from '../client/js/components/PageStatusAlert';
 import {
   useCurrentUser,
-  useIsLatestRevision, useCurrentRevisionId,
+  useIsLatestRevision,
   useIsForbidden, useIsNotFound, useIsSharedUser,
   useIsEnabledStaleNotification, useIsIdenticalPath,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing,
@@ -136,7 +136,6 @@ type Props = CommonProps & {
 
   // shareLinkId?: string;
   isLatestRevision?: boolean,
-  currentRevisionId?: string,
 
   isIdenticalPathPage?: boolean,
   isForbidden: boolean,
@@ -248,7 +247,6 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   useCurrentPageId(pageId ?? null);
   // useIsNotCreatable(props.isForbidden || !isCreatablePage(pagePath)); // TODO: need to include props.isIdentical
   useCurrentPathname(props.currentPathname);
-  useCurrentRevisionId(props.currentRevisionId);
 
   const { data: currentPage } = useSWRxCurrentPage(undefined, pageWithMeta?.data ?? null); // store initial data
   useEditingMarkdown(pageWithMeta?.data.revision?.body ?? props.templateBodyData ?? '');
@@ -424,10 +422,6 @@ async function injectPageData(context: GetServerSidePropsContext, props: Props):
     props.isLatestRevision = page.isLatestRevision();
   }
 
-  if (typeof revisionId === 'string' || typeof revisionId === 'undefined') {
-    props.currentRevisionId = props.isLatestRevision && page.latestRevision != null ? page.latestRevision.toString() : revisionId;
-  }
-
   if (page == null && user != null) {
     const templateData = await Page.findTemplate(props.currentPathname);
     if (templateData != null) {

+ 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) => {

+ 0 - 4
packages/app/src/stores/context.tsx

@@ -48,10 +48,6 @@ export const useCurrentUser = (initialData?: Nullable<IUser>): SWRResponse<Nulla
   return useContextSWR<Nullable<IUser>, Error>('currentUser', initialData);
 };
 
-export const useCurrentRevisionId = (initialData?: string): SWRResponse<string, Error> => {
-  return useContextSWR('currentRevisionId', initialData);
-};
-
 export const useCurrentPathname = (initialData?: string): SWRResponse<string, Error> => {
   return useContextSWR('currentPathname', initialData);
 };

+ 13 - 5
packages/app/src/stores/page.tsx

@@ -1,8 +1,9 @@
+import { useEffect } from 'react';
+
 import type {
   IPageInfoForEntity, IPagePopulatedToShowRevision, Nullable,
 } from '@growi/core';
-import { pagePathUtils } from '@growi/core';
-import { useEffect } from 'react';
+import { isClient, pagePathUtils } from '@growi/core';
 import useSWR, { Key, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
@@ -16,7 +17,7 @@ import { IRevisionsForPagination } from '~/interfaces/revision';
 
 import { IPageTagsInfo } from '../interfaces/tag';
 
-import { useCurrentPageId, useCurrentPathname, useCurrentRevisionId } from './context';
+import { useCurrentPageId, useCurrentPathname } from './context';
 import { ITermNumberManagerUtil, useTermNumberManager } from './use-static-swr';
 
 const { isPermalink: _isPermalink } = pagePathUtils;
@@ -64,9 +65,16 @@ export const useSWRxCurrentPage = (
     shareLinkId?: string, initialData?: IPagePopulatedToShowRevision|null,
 ): SWRResponse<IPagePopulatedToShowRevision|null, Error> => {
   const { data: currentPageId } = useCurrentPageId();
-  const { data: currentRevisionId } = useCurrentRevisionId();
 
-  const swrResult = useSWRxPage(currentPageId, shareLinkId, currentRevisionId, initialData);
+  // Get URL parameter for specific revisionId
+  let revisionId: string|undefined;
+  if (isClient()) {
+    const urlParams = new URLSearchParams(window.location.search);
+    const requestRevisionId = urlParams.get('revisionId');
+    revisionId = requestRevisionId != null ? requestRevisionId : undefined;
+  }
+
+  const swrResult = useSWRxPage(currentPageId, shareLinkId, revisionId, initialData);
 
   return swrResult;
 };

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

@@ -119,19 +119,22 @@ context('Access to special pages', () => {
     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`);
   });
 

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

@@ -45,20 +45,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`);
   });
 });
 

+ 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'});
   });
 

+ 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`);