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

Merge branch 'master' into feat/10814-remark-drawio-plugin

Yuki Takei 3 лет назад
Родитель
Сommit
2ac87362a7

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

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

@@ -116,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": "例",

+ 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": "例子",

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

@@ -123,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">

+ 18 - 29
packages/app/src/components/PageEditor.tsx

@@ -5,7 +5,7 @@ import React, {
 import EventEmitter from 'events';
 
 import {
-  envUtils, IPageHasId, PageGrant, pathUtils,
+  IPageHasId, PageGrant, pathUtils,
 } from '@growi/core';
 import detectIndent from 'detect-indent';
 import { useTranslation } from 'next-i18next';
@@ -19,7 +19,7 @@ import { IEditorMethods } from '~/interfaces/editor-methods';
 import { OptionsToSave } from '~/interfaces/page-operation';
 import {
   useCurrentPathname, useCurrentPageId, useIsEnabledAttachTitleHeader, useTemplateBodyData,
-  useIsEditable, useIsUploadableFile, useIsUploadableImage, useIsNotFound,
+  useIsEditable, useIsUploadableFile, useIsUploadableImage, useIsNotFound, useIsIndentSizeForced,
 } from '~/stores/context';
 import {
   useCurrentIndentSize, useSWRxSlackChannels, useIsSlackEnabled, useIsTextlintEnabled, usePageTagsForEditors,
@@ -75,7 +75,8 @@ const PageEditor = React.memo((): JSX.Element => {
   const { data: isSlackEnabled } = useIsSlackEnabled();
   const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
   const { data: isTextlintEnabled } = useIsTextlintEnabled();
-  const { data: indentSize } = useCurrentIndentSize();
+  const { data: isIndentSizeForced } = useIsIndentSizeForced();
+  const { data: currentIndentSize, mutate: mutateCurrentIndentSize } = useCurrentIndentSize();
   const { data: isUploadableFile } = useIsUploadableFile();
   const { data: isUploadableImage } = useIsUploadableImage();
 
@@ -402,33 +403,21 @@ const PageEditor = React.memo((): JSX.Element => {
     }
   }, [editorMode]);
 
-  // Unnecessary code. Delete after PageEditor and PageEditorByHackmd implementation has completed. -- 2022.09.06 Yuki Takei
-  //
-  // set handler to update editor value
-  // useEffect(() => {
-  //   const handler = (markdown) => {
-  //     if (editorRef.current != null) {
-  //       editorRef.current.setValue(markdown);
-  //     }
-  //   };
-  //   globalEmitter.on('updateEditorValue', handler);
-
-  //   return function cleanup() {
-  //     globalEmitter.removeListener('updateEditorValue', handler);
-  //   };
-  // }, []);
-
   // Detect indent size from contents (only when users are allowed to change it)
-  // useEffect(() => {
-  //   const currentPageMarkdown = pageContainer.state.markdown;
-  //   if (!isIndentSizeForced && currentPageMarkdown != null) {
-  //     const detectedIndent = detectIndent(currentPageMarkdown);
-  //     if (detectedIndent.type === 'space' && new Set([2, 4]).has(detectedIndent.amount)) {
-  //       mutateCurrentIndentSize(detectedIndent.amount);
-  //     }
-  //   }
-  // }, [isIndentSizeForced, mutateCurrentIndentSize, pageContainer.state.markdown]);
+  useEffect(() => {
+    // do nothing if the indent size fixed
+    if (isIndentSizeForced == null || isIndentSizeForced) {
+      return;
+    }
 
+    // detect from markdown
+    if (initialValue != null) {
+      const detectedIndent = detectIndent(initialValue);
+      if (detectedIndent.type === 'space' && new Set([2, 4]).has(detectedIndent.amount)) {
+        mutateCurrentIndentSize(detectedIndent.amount);
+      }
+    }
+  }, [initialValue, isIndentSizeForced, mutateCurrentIndentSize]);
 
   if (!isEditable) {
     return <></>;
@@ -449,7 +438,7 @@ const PageEditor = React.memo((): JSX.Element => {
           isUploadable={isUploadable}
           isUploadableFile={isUploadableFile}
           isTextlintEnabled={isTextlintEnabled}
-          indentSize={indentSize}
+          indentSize={currentIndentSize}
           onScroll={editorScrolledHandler}
           onScrollCursorIntoView={editorScrollCursorIntoViewHandler}
           onChange={markdownChangedHandler}

+ 11 - 24
packages/app/src/components/PageRenameModal.tsx

@@ -50,7 +50,6 @@ const PageRenameModal = (): JSX.Element => {
 
   const [subordinatedPages, setSubordinatedPages] = useState([]);
   const [existingPaths, setExistingPaths] = useState<string[]>([]);
-  const [canRename, setCanRename] = useState(false);
   const [isRenameRecursively, setIsRenameRecursively] = useState(true);
   const [isRenameRedirect, setIsRenameRedirect] = useState(false);
   const [isRemainMetadata, setIsRemainMetadata] = useState(false);
@@ -81,6 +80,16 @@ const PageRenameModal = (): JSX.Element => {
     }
   }, [isOpened, page, updateSubordinatedList]);
 
+  const canRename = useMemo(() => {
+    if (page == null || isMatchedWithUserHomePagePath || page.data.path === pageNameInput) {
+      return false;
+    }
+    if (isV5Compatible(page.meta)) {
+      return existingPaths.length === 0; // v5 data
+    }
+    return isRenameRecursively; // v4 data
+  }, [existingPaths.length, isMatchedWithUserHomePagePath, isRenameRecursively, page, pageNameInput]);
+
   const rename = useCallback(async() => {
     if (page == null || !canRename) {
       return;
@@ -127,9 +136,6 @@ const PageRenameModal = (): JSX.Element => {
     try {
       const res = await apiv3Get<{ existPaths: string[]}>('/page/exist-paths', { fromPath, toPath });
       const { existPaths } = res.data;
-      if (existPaths.length === 0) {
-        setCanRename(true);
-      }
       setExistingPaths(existPaths);
     }
     catch (err) {
@@ -157,13 +163,6 @@ const PageRenameModal = (): JSX.Element => {
     }
   }, [isOpened, pageNameInput, subordinatedPages, checkExistPathsDebounce, page, checkIsUsersHomePageDebounce]);
 
-  useEffect(() => {
-    if (isOpened && page != null) {
-      setCanRename(false);
-    }
-  }, [isOpened, page, pageNameInput]);
-
-
   function ppacInputChangeHandler(value) {
     setErrs(null);
     setPageNameInput(value);
@@ -330,20 +329,8 @@ const PageRenameModal = (): JSX.Element => {
       return <></>;
     }
 
-    let submitButtonDisabled = false;
+    const submitButtonDisabled = !canRename;
 
-    if (isMatchedWithUserHomePagePath) {
-      submitButtonDisabled = true;
-    }
-    else if (!canRename) {
-      submitButtonDisabled = true;
-    }
-    else if (isV5Compatible(page.meta)) {
-      submitButtonDisabled = existingPaths.length !== 0; // v5 data
-    }
-    else {
-      submitButtonDisabled = !isRenameRecursively; // v4 data
-    }
     return (
       <>
         <ApiErrorMessageList errs={errs} targetPath={pageNameInput} />

+ 6 - 0
packages/app/src/interfaces/rehype.ts

@@ -0,0 +1,6 @@
+export const RehypeSanitizeOption = {
+  RECOMMENDED: 'Recommended',
+  CUSTOM: 'Custom',
+} as const;
+
+export type RehypeSanitizeOption = typeof RehypeSanitizeOption[keyof typeof RehypeSanitizeOption];

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

+ 5 - 0
packages/app/src/server/models/config.ts

@@ -3,6 +3,7 @@ import uniqueValidator from 'mongoose-unique-validator';
 
 import { GrowiThemes } from '~/interfaces/theme';
 
+import { RehypeSanitizeOption } from '../../interfaces/rehype';
 import { getOrCreateModel } from '../util/mongoose-utils';
 
 
@@ -154,6 +155,10 @@ export const defaultMarkdownConfigs: { [key: string]: any } = {
   'markdown:xss:option': 2,
   'markdown:xss:tagWhiteList': [],
   'markdown:xss:attrWhiteList': [],
+  'markdown:rehypeSanitize:isEnabledPrevention': true,
+  'markdown:rehypeSanitize:option': RehypeSanitizeOption.RECOMMENDED,
+  'markdown:rehypeSanitize:tagNames': [],
+  'markdown:rehypeSanitize:attributes': {},
   'markdown:isEnabledLinebreaks': false,
   'markdown:isEnabledLinebreaksInComments': true,
   'markdown:adminPreferredIndentSize': 4,

+ 33 - 25
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`);
+  });
+
+  // TODO: https://redmine.weseek.co.jp/issues/109939
+  // it('/Sandbox with anchor hash is successfully loaded', () => {
+  //   cy.visit('/Sandbox#Headers');
+  //   cy.waitUntilSkeletonDisappear();
 
-    // 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');
+  //   // for check download toc data
+  //   cy.get('.toc-link').should('be.visible');
 
-    cy.screenshot(`${ssPrefix}-sandbox-headers`);
-  });
+  //   // 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,8 +120,10 @@ 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`);
   });
 
@@ -226,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`);
    });
 
 })

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