Browse Source

Merge branch 'master' into support/move-theme-into-external-package

Yuki Takei 3 years ago
parent
commit
6d764029a0
28 changed files with 105 additions and 105 deletions
  1. 1 1
      package.json
  2. 29 0
      packages/app/cypress.config.ts
  3. 0 18
      packages/app/cypress.json
  4. 1 1
      packages/app/src/components/Admin/Common/AdminNavigation.jsx
  5. 1 1
      packages/app/src/components/Admin/PluginsExtension/PluginInstallerForm.tsx
  6. 1 1
      packages/app/src/components/Common/Dropdown/PageItemControl.tsx
  7. 7 4
      packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx
  8. 1 1
      packages/app/src/components/Page/CopyDropdown.jsx
  9. 9 11
      packages/app/src/components/Page/RenderTagLabels.tsx
  10. 1 1
      packages/app/src/components/Page/TagLabels.tsx
  11. 13 11
      packages/app/src/components/PageAlert/TrashPageAlert.tsx
  12. 4 0
      packages/app/src/components/PageCreateModal.jsx
  13. 1 1
      packages/app/src/pages/admin/plugins.page.tsx
  14. 1 1
      packages/app/src/server/routes/apiv3/index.js
  15. 0 0
      packages/app/src/server/routes/apiv3/plugins-extension.ts
  16. 0 0
      packages/app/test/cypress/integration/10-install/10-install--install.spec.ts
  17. 0 0
      packages/app/test/cypress/integration/20-basic-features/20-basic-features--access-to-page.spec.ts
  18. 0 0
      packages/app/test/cypress/integration/20-basic-features/20-basic-features--access-to-pagelist.spec.ts
  19. 0 0
      packages/app/test/cypress/integration/20-basic-features/20-basic-features--click-page-icons.spec.ts
  20. 0 0
      packages/app/test/cypress/integration/20-basic-features/20-basic-features--use-tools.spec.ts
  21. 0 0
      packages/app/test/cypress/integration/21-basic-features-for-guest/21-basic-features-for-guest--access-to-page.spec.ts
  22. 0 0
      packages/app/test/cypress/integration/30-search/30-search--search.spec.ts
  23. 0 0
      packages/app/test/cypress/integration/40-admin/40-admin--access-to-admin-page.spec.ts
  24. 0 0
      packages/app/test/cypress/integration/50-sidebar/50-sidebar--access-to-side-bar.spec.ts
  25. 0 0
      packages/app/test/cypress/integration/50-sidebar/50-sidebar--switching-sidebar-mode.spec.ts
  26. 0 0
      packages/app/test/cypress/integration/60-home/60-home--home.spec.ts
  27. 0 33
      packages/app/test/cypress/plugins/index.ts
  28. 35 20
      yarn.lock

+ 1 - 1
package.json

@@ -61,7 +61,7 @@
     "@types/rewire": "^2.5.28",
     "@types/rewire": "^2.5.28",
     "@typescript-eslint/eslint-plugin": "^5.0.0",
     "@typescript-eslint/eslint-plugin": "^5.0.0",
     "@typescript-eslint/parser": "^5.0.0",
     "@typescript-eslint/parser": "^5.0.0",
-    "cypress": "^9.2.0",
+    "cypress": "^12.0.1",
     "eslint": "^8.18.0",
     "eslint": "^8.18.0",
     "eslint-config-next": "^12.1.6",
     "eslint-config-next": "^12.1.6",
     "eslint-config-weseek": "^2.1.0",
     "eslint-config-weseek": "^2.1.0",

+ 29 - 0
packages/app/cypress.config.ts

@@ -0,0 +1,29 @@
+import { defineConfig } from 'cypress';
+
+export default defineConfig({
+  e2e: {
+    baseUrl: 'http://localhost:3000',
+    specPattern: 'test/cypress/integration/',
+    supportFile: 'test/cypress/support/index.ts',
+    setupNodeEvents: (on) => {
+      // change screen size
+      // see: https://docs.cypress.io/api/plugins/browser-launch-api#Set-screen-size-when-running-headless
+      on('before:browser:launch', (browser, launchOptions) => {
+        if (browser.name === 'chrome' && browser.isHeadless) {
+          launchOptions.args.push('--window-size=1400,1024');
+          launchOptions.args.push('--force-device-scale-factor=1');
+        }
+        return launchOptions;
+      });
+    },
+  },
+  fileServerFolder: 'test/cypress',
+  fixturesFolder: 'test/cypress/fixtures',
+  screenshotsFolder: 'test/cypress/screenshots',
+  videosFolder: 'test/cypress/videos',
+
+  viewportWidth: 1400,
+  viewportHeight: 1024,
+
+  defaultCommandTimeout: 30000,
+});

+ 0 - 18
packages/app/cypress.json

@@ -1,18 +0,0 @@
-{
-  "baseUrl": "http://localhost:3000",
-
-  "fileServerFolder": "test/cypress",
-  "fixturesFolder": "test/cypress/fixtures",
-  "integrationFolder": "test/cypress/integration",
-  "screenshotsFolder": "test/cypress/screenshots",
-  "videosFolder": "test/cypress/videos",
-  "supportFile": "test/cypress/support/index.ts",
-  "pluginsFile": "test/cypress/plugins/index.ts",
-  "testFiles": "**/*.spec.ts",
-
-  "viewportWidth": 1400,
-  "viewportHeight": 1024,
-
-  "experimentalSessionSupport": true,
-  "defaultCommandTimeout": 30000
-}

+ 1 - 1
packages/app/src/components/Admin/Common/AdminNavigation.jsx

@@ -36,7 +36,7 @@ const AdminNavigation = (props) => {
       case 'user-groups':              return <><i className="mr-1 icon-fw icon-people"></i>{          t('user_group_management.user_group_management') }</>;
       case 'user-groups':              return <><i className="mr-1 icon-fw icon-people"></i>{          t('user_group_management.user_group_management') }</>;
       case 'search':                   return <><i className="mr-1 icon-fw icon-magnifier"></i>{       t('full_text_search_management.full_text_search_management') }</>;
       case 'search':                   return <><i className="mr-1 icon-fw icon-magnifier"></i>{       t('full_text_search_management.full_text_search_management') }</>;
       case 'audit-log':                return <><i className="mr-1 icon-fw icon-feed"></i>{            t('audit_log_management.audit_log')}</>;
       case 'audit-log':                return <><i className="mr-1 icon-fw icon-feed"></i>{            t('audit_log_management.audit_log')}</>;
-      case 'plugins':                  return <><i className="mr-1 icon-fw icon-puzzle"></i>{           'Plugins Extention'}</>;
+      case 'plugins':                  return <><i className="mr-1 icon-fw icon-puzzle"></i>{           'Plugins Extension'}</>;
       case 'cloud':                    return <><i className="mr-1 icon-fw icon-share-alt"></i>{       t('cloud_setting_management.to_cloud_settings')} </>;
       case 'cloud':                    return <><i className="mr-1 icon-fw icon-share-alt"></i>{       t('cloud_setting_management.to_cloud_settings')} </>;
       default:                         return <><i className="mr-1 icon-fw icon-home"></i>{            t('wiki_management_home_page') }</>;
       default:                         return <><i className="mr-1 icon-fw icon-home"></i>{            t('wiki_management_home_page') }</>;
       /* eslint-enable no-multi-spaces, max-len */
       /* eslint-enable no-multi-spaces, max-len */

+ 1 - 1
packages/app/src/components/Admin/PluginsExtension/PluginInstallerForm.tsx

@@ -30,7 +30,7 @@ export const PluginInstallerForm = (): JSX.Element => {
     };
     };
 
 
     try {
     try {
-      await apiv3Post('/plugins-extention', { pluginInstallerForm });
+      await apiv3Post('/plugins-extension', { pluginInstallerForm });
       toastSuccess('Plugin Install Successed!');
       toastSuccess('Plugin Install Successed!');
     }
     }
     catch (err) {
     catch (err) {

+ 1 - 1
packages/app/src/components/Common/Dropdown/PageItemControl.tsx

@@ -248,7 +248,7 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
     <DropdownMenu
     <DropdownMenu
       data-testid="page-item-control-menu"
       data-testid="page-item-control-menu"
       positionFixed
       positionFixed
-      modifiers={{ preventOverflow: { boundariesElement: undefined } }}
+      modifiers={{ preventOverflow: { boundariesElement: 'viewport' } }}
       right={alignRight}
       right={alignRight}
     >
     >
       {contents}
       {contents}

+ 7 - 4
packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -1,6 +1,6 @@
 import React, { useState, useEffect, useCallback } from 'react';
 import React, { useState, useEffect, useCallback } from 'react';
 
 
-import { isPopulated, IUser } from '@growi/core';
+import { isPopulated, IUser, pagePathUtils } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
@@ -43,6 +43,7 @@ import type { SubNavButtonsProps } from './SubNavButtons';
 import AuthorInfoStyles from './AuthorInfo.module.scss';
 import AuthorInfoStyles from './AuthorInfo.module.scss';
 import PageEditorModeManagerStyles from './PageEditorModeManager.module.scss';
 import PageEditorModeManagerStyles from './PageEditorModeManager.module.scss';
 
 
+const { isUsersHomePage } = pagePathUtils;
 
 
 const AuthorInfoSkeleton = () => <Skeleton additionalClass={`${AuthorInfoStyles['grw-author-info-skeleton']} py-1`} />;
 const AuthorInfoSkeleton = () => <Skeleton additionalClass={`${AuthorInfoStyles['grw-author-info-skeleton']} py-1`} />;
 
 
@@ -304,11 +305,13 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
         router.push(path);
         router.push(path);
       }
       }
       else {
       else {
-        reload();
+        // Do not use "router.push(currentPathname)" to avoid `Error: Invariant: attempted to hard navigate to the same URL`
+        // See: https://github.com/weseek/growi/pull/7061
+        router.reload();
       }
       }
     };
     };
     openDeleteModal([pageWithMeta], { onDeleted: deletedHandler });
     openDeleteModal([pageWithMeta], { onDeleted: deletedHandler });
-  }, [openDeleteModal, reload, router]);
+  }, [openDeleteModal, router]);
 
 
   const switchContentWidthHandler = useCallback(async(pageId: string, value: boolean) => {
   const switchContentWidthHandler = useCallback(async(pageId: string, value: boolean) => {
     await updateContentWidth(pageId, value);
     await updateContentWidth(pageId, value);
@@ -378,7 +381,7 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
               />
               />
             )}
             )}
           </div>
           </div>
-          { (isAbleToShowPageAuthors && !isCompactMode) && (
+          { (isAbleToShowPageAuthors && !isCompactMode && !isUsersHomePage(path ?? '')) && (
             <ul className={`${AuthorInfoStyles['grw-author-info']} text-nowrap border-left d-none d-lg-block d-edit-none py-2 pl-4 mb-0 ml-3`}>
             <ul className={`${AuthorInfoStyles['grw-author-info']} text-nowrap border-left d-none d-lg-block d-edit-none py-2 pl-4 mb-0 ml-3`}>
               <li className="pb-1">
               <li className="pb-1">
                 { currentPage != null
                 { currentPage != null

+ 1 - 1
packages/app/src/components/Page/CopyDropdown.jsx

@@ -118,7 +118,7 @@ const CopyDropdown = (props) => {
           <span id={dropdownToggleId}>{children}</span>
           <span id={dropdownToggleId}>{children}</span>
         </DropdownToggle>
         </DropdownToggle>
 
 
-        <DropdownMenu positionFixed modifiers={{ preventOverflow: { boundariesElement: undefined } }}>
+        <DropdownMenu positionFixed modifiers={{ preventOverflow: { boundariesElement: 'viewport' } }}>
 
 
           <div className="d-flex align-items-center justify-content-between">
           <div className="d-flex align-items-center justify-content-between">
             <DropdownItem header className="px-3">
             <DropdownItem header className="px-3">

+ 9 - 11
packages/app/src/components/Page/RenderTagLabels.tsx

@@ -21,25 +21,23 @@ const RenderTagLabels = React.memo((props: RenderTagLabelsProps) => {
   }
   }
 
 
   const isTagsEmpty = tags.length === 0;
   const isTagsEmpty = tags.length === 0;
-  const tagElements = tags.map((tag) => {
-    return (
-      <a key={tag} href={`/_search?q=tag:${tag}`} className="grw-tag-label badge badge-secondary mr-2">
-        {tag}
-      </a>
-    );
-  });
 
 
   return (
   return (
     <>
     <>
-      {tagElements}
-
+      {tags.map((tag) => {
+        return (
+          <a key={tag} href={`/_search?q=tag:${tag}`} className="grw-tag-label badge badge-secondary mr-2">
+            {tag}
+          </a>
+        );
+      })}
       <div id="edit-tags-btn-wrapper-for-tooltip">
       <div id="edit-tags-btn-wrapper-for-tooltip">
         <a
         <a
-          className={`btn btn-link btn-edit-tags p-0 text-muted d-flex ${isTagsEmpty ? 'no-tags' : ''} ${isGuestUser ? 'disabled' : ''}`}
+          className={`btn btn-link btn-edit-tags text-muted p-0 d-flex align-items-center ${isTagsEmpty && 'no-tags'} ${isGuestUser && 'disabled'}`}
           onClick={openEditorHandler}
           onClick={openEditorHandler}
         >
         >
           { isTagsEmpty && <>{ t('Add tags for this page') }</>}
           { isTagsEmpty && <>{ t('Add tags for this page') }</>}
-          <i className="ml-1 icon-plus"></i>
+          <i className={`icon-plus ${isTagsEmpty && 'ml-1'}`}/>
         </a>
         </a>
       </div>
       </div>
       {isGuestUser && (
       {isGuestUser && (

+ 1 - 1
packages/app/src/components/Page/TagLabels.tsx

@@ -37,7 +37,7 @@ export const TagLabels:FC<Props> = (props: Props) => {
   return (
   return (
     <>
     <>
       <div className={`${styles['grw-tag-labels']} grw-tag-labels d-flex align-items-center`} data-testid="grw-tag-labels">
       <div className={`${styles['grw-tag-labels']} grw-tag-labels d-flex align-items-center`} data-testid="grw-tag-labels">
-        <i className="tag-icon icon-tag mr-2"></i>
+        <i className="tag-icon icon-tag mr-2"/>
         <RenderTagLabels
         <RenderTagLabels
           tags={tags}
           tags={tags}
           openEditorModal={openEditorModal}
           openEditorModal={openEditorModal}

+ 13 - 11
packages/app/src/components/PageAlert/TrashPageAlert.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useCallback } from 'react';
 
 
 import { UserPicture } from '@growi/ui';
 import { UserPicture } from '@growi/ui';
 import { format } from 'date-fns';
 import { format } from 'date-fns';
@@ -32,27 +32,25 @@ export const TrashPageAlert = (): JSX.Element => {
   const { open: openDeleteModal } = usePageDeleteModal();
   const { open: openDeleteModal } = usePageDeleteModal();
   const { open: openPutBackPageModal } = usePutBackPageModal();
   const { open: openPutBackPageModal } = usePutBackPageModal();
 
 
-  if (!isTrashPage) {
-    return <></>;
-  }
-
 
 
   const deleteUser = pageData?.deleteUser;
   const deleteUser = pageData?.deleteUser;
   const deletedAt = pageData?.deletedAt ? format(new Date(pageData?.deletedAt), 'yyyy/MM/dd HH:mm') : '';
   const deletedAt = pageData?.deletedAt ? format(new Date(pageData?.deletedAt), 'yyyy/MM/dd HH:mm') : '';
   const revisionId = pageData?.revision?._id;
   const revisionId = pageData?.revision?._id;
 
 
 
 
-  function openPutbackPageModalHandler() {
+  const openPutbackPageModalHandler = useCallback(() => {
     if (pageId === undefined || pagePath === undefined) {
     if (pageId === undefined || pagePath === undefined) {
       return;
       return;
     }
     }
     const putBackedHandler = () => {
     const putBackedHandler = () => {
-      router.push(`/${pageId}`);
+      // Do not use "router.push(`/${pageId}`)" to avoid `Error: Invariant: attempted to hard navigate to the same URL`
+      // See: https://github.com/weseek/growi/pull/7054
+      router.reload();
     };
     };
     openPutBackPageModal({ pageId, path: pagePath }, { onPutBacked: putBackedHandler });
     openPutBackPageModal({ pageId, path: pagePath }, { onPutBacked: putBackedHandler });
-  }
+  }, [openPutBackPageModal, pageId, pagePath, router]);
 
 
-  function openPageDeleteModalHandler() {
+  const openPageDeleteModalHandler = useCallback(() => {
     if (pageId === undefined || revisionId === undefined || pagePath === undefined) {
     if (pageId === undefined || revisionId === undefined || pagePath === undefined) {
       return;
       return;
     }
     }
@@ -65,9 +63,9 @@ export const TrashPageAlert = (): JSX.Element => {
       meta: pageInfo,
       meta: pageInfo,
     };
     };
     openDeleteModal([pageToDelete], { onDeleted: onDeletedHandler });
     openDeleteModal([pageToDelete], { onDeleted: onDeletedHandler });
-  }
+  }, [openDeleteModal, pageId, pageInfo, pagePath, revisionId]);
 
 
-  function renderTrashPageManagementButtons() {
+  const renderTrashPageManagementButtons = useCallback(() => {
     return (
     return (
       <>
       <>
         <button
         <button
@@ -88,6 +86,10 @@ export const TrashPageAlert = (): JSX.Element => {
         </button>
         </button>
       </>
       </>
     );
     );
+  }, [openPageDeleteModalHandler, openPutbackPageModalHandler, pageInfo?.isAbleToDeleteCompletely, t]);
+
+  if (!isTrashPage) {
+    return <></>;
   }
   }
 
 
   return (
   return (

+ 4 - 0
packages/app/src/components/PageCreateModal.jsx

@@ -55,6 +55,10 @@ const PageCreateModal = () => {
     }
     }
   }, [isOpened, pathname, isCreatable]);
   }, [isOpened, pathname, isCreatable]);
 
 
+  useEffect(() => {
+    setTodayInput1(t('Memo'));
+  }, [t]);
+
   const checkIsUsersHomePageDebounce = useMemo(() => {
   const checkIsUsersHomePageDebounce = useMemo(() => {
     const checkIsUsersHomePage = () => {
     const checkIsUsersHomePage = () => {
       setIsMatchedWithUserHomePagePath(isUsersHomePage(pageNameInput));
       setIsMatchedWithUserHomePagePath(isUsersHomePage(pageNameInput));

+ 1 - 1
packages/app/src/pages/admin/plugins.page.tsx

@@ -27,7 +27,7 @@ const AdminAppPage: NextPage<CommonProps> = (props) => {
   useIsMaintenanceMode(props.isMaintenanceMode);
   useIsMaintenanceMode(props.isMaintenanceMode);
   useCurrentUser(props.currentUser ?? null);
   useCurrentUser(props.currentUser ?? null);
 
 
-  const title = 'Plugins Extention';
+  const title = 'Plugins Extension';
   const injectableContainers: Container<any>[] = [];
   const injectableContainers: Container<any>[] = [];
 
 
   if (isClient()) {
   if (isClient()) {

+ 1 - 1
packages/app/src/server/routes/apiv3/index.js

@@ -104,7 +104,7 @@ module.exports = (crowi, app) => {
     userActivation.validateCompleteRegistration,
     userActivation.validateCompleteRegistration,
     userActivation.completeRegistrationAction(crowi));
     userActivation.completeRegistrationAction(crowi));
 
 
-  router.use('/plugins-extention', require('./plugins-extention')(crowi));
+  router.use('/plugins-extension', require('./plugins-extension')(crowi));
 
 
   router.use('/user-ui-settings', require('./user-ui-settings')(crowi));
   router.use('/user-ui-settings', require('./user-ui-settings')(crowi));
 
 

+ 0 - 0
packages/app/src/server/routes/apiv3/plugins-extention.ts → packages/app/src/server/routes/apiv3/plugins-extension.ts


+ 0 - 0
packages/app/test/cypress/integration/10-install/install.spec.ts → packages/app/test/cypress/integration/10-install/10-install--install.spec.ts


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


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


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


+ 0 - 0
packages/app/test/cypress/integration/20-basic-features/use-tools.spec.ts → packages/app/test/cypress/integration/20-basic-features/20-basic-features--use-tools.spec.ts


+ 0 - 0
packages/app/test/cypress/integration/21-basic-features-for-guest/access-to-page.spec.ts → packages/app/test/cypress/integration/21-basic-features-for-guest/21-basic-features-for-guest--access-to-page.spec.ts


+ 0 - 0
packages/app/test/cypress/integration/30-search/search.spec.ts → packages/app/test/cypress/integration/30-search/30-search--search.spec.ts


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


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


+ 0 - 0
packages/app/test/cypress/integration/50-sidebar/switching-sidebar-mode.spec.ts → packages/app/test/cypress/integration/50-sidebar/50-sidebar--switching-sidebar-mode.spec.ts


+ 0 - 0
packages/app/test/cypress/integration/60-home/home.spec.ts → packages/app/test/cypress/integration/60-home/60-home--home.spec.ts


+ 0 - 33
packages/app/test/cypress/plugins/index.ts

@@ -1,33 +0,0 @@
-/// <reference types="cypress" />
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-/**
- * @type {Cypress.PluginConfig}
- */
-// eslint-disable-next-line no-unused-vars
-module.exports = (on, config) => {
-  // `on` is used to hook into various events Cypress emits
-  // `config` is the resolved Cypress config
-
-  // change screen size
-  // see: https://docs.cypress.io/api/plugins/browser-launch-api#Set-screen-size-when-running-headless
-  on('before:browser:launch', (browser, launchOptions) => {
-    if (browser.name === 'chrome' && browser.isHeadless) {
-      launchOptions.args.push('--window-size=1400,1024')
-      launchOptions.args.push('--force-device-scale-factor=1')
-    }
-
-    return launchOptions
-  })
-}

+ 35 - 20
yarn.lock

@@ -1647,6 +1647,11 @@
   resolved "https://registry.yarnpkg.com/@browser-bunyan/levels/-/levels-1.8.0.tgz#1c0a98d04284e0620e8ee414d7ce43385080a5cf"
   resolved "https://registry.yarnpkg.com/@browser-bunyan/levels/-/levels-1.8.0.tgz#1c0a98d04284e0620e8ee414d7ce43385080a5cf"
   integrity sha512-f9oSDik8kAl+4rhVyHqIr012P1boHFUKc7D9nzA5+lDsFoP90UQnDwpseqBdF2mTaWYju10E7h+GdH8u+7MHOQ==
   integrity sha512-f9oSDik8kAl+4rhVyHqIr012P1boHFUKc7D9nzA5+lDsFoP90UQnDwpseqBdF2mTaWYju10E7h+GdH8u+7MHOQ==
 
 
+"@colors/colors@1.5.0":
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
+  integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
+
 "@cspotcode/source-map-support@^0.8.0":
 "@cspotcode/source-map-support@^0.8.0":
   version "0.8.1"
   version "0.8.1"
   resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
   resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
@@ -4532,10 +4537,10 @@
     "@types/mime" "^1"
     "@types/mime" "^1"
     "@types/node" "*"
     "@types/node" "*"
 
 
-"@types/sinonjs__fake-timers@^6.0.2":
-  version "6.0.4"
-  resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz#0ecc1b9259b76598ef01942f547904ce61a6a77d"
-  integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==
+"@types/sinonjs__fake-timers@8.1.1":
+  version "8.1.1"
+  resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3"
+  integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==
 
 
 "@types/sizzle@*", "@types/sizzle@^2.3.2":
 "@types/sizzle@*", "@types/sizzle@^2.3.2":
   version "2.3.3"
   version "2.3.3"
@@ -6884,7 +6889,7 @@ cli-spinners@^2.5.0:
   resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d"
   resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d"
   integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==
   integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==
 
 
-cli-table3@^0.6.0, cli-table3@~0.6.0:
+cli-table3@^0.6.0:
   version "0.6.0"
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee"
   resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee"
   integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==
   integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==
@@ -6894,6 +6899,15 @@ cli-table3@^0.6.0, cli-table3@~0.6.0:
   optionalDependencies:
   optionalDependencies:
     colors "^1.1.2"
     colors "^1.1.2"
 
 
+cli-table3@~0.6.1:
+  version "0.6.3"
+  resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2"
+  integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==
+  dependencies:
+    string-width "^4.2.0"
+  optionalDependencies:
+    "@colors/colors" "1.5.0"
+
 cli-truncate@^2.1.0:
 cli-truncate@^2.1.0:
   version "2.1.0"
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
   resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
@@ -7786,30 +7800,31 @@ currently-unhandled@^0.4.1:
   dependencies:
   dependencies:
     array-find-index "^1.0.1"
     array-find-index "^1.0.1"
 
 
-cypress@^9.2.0:
-  version "9.2.0"
-  resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.2.0.tgz#727c20b4662167890db81d5f6ba615231835b17d"
-  integrity sha512-Jn26Tprhfzh/a66Sdj9SoaYlnNX6Mjfmj5PHu2a7l3YHXhrgmavM368wjCmgrxC6KHTOv9SpMQGhAJn+upDViA==
+cypress@^12.0.1:
+  version "12.0.1"
+  resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.0.1.tgz#3a51a38b2f162256c7226e68e902cfe1750e3d92"
+  integrity sha512-I1Ag5RsPEINfUlQtV6xwkd6ktJuu5QGiKZ3pFa/IXjcyCY6I7CH3gOz0juLOhg/LXOPrQtZH35ulcWDQohyyEA==
   dependencies:
   dependencies:
     "@cypress/request" "^2.88.10"
     "@cypress/request" "^2.88.10"
     "@cypress/xvfb" "^1.2.4"
     "@cypress/xvfb" "^1.2.4"
     "@types/node" "^14.14.31"
     "@types/node" "^14.14.31"
-    "@types/sinonjs__fake-timers" "^6.0.2"
+    "@types/sinonjs__fake-timers" "8.1.1"
     "@types/sizzle" "^2.3.2"
     "@types/sizzle" "^2.3.2"
     arch "^2.2.0"
     arch "^2.2.0"
     blob-util "^2.0.2"
     blob-util "^2.0.2"
-    bluebird "3.7.2"
+    bluebird "^3.7.2"
+    buffer "^5.6.0"
     cachedir "^2.3.0"
     cachedir "^2.3.0"
     chalk "^4.1.0"
     chalk "^4.1.0"
     check-more-types "^2.24.0"
     check-more-types "^2.24.0"
     cli-cursor "^3.1.0"
     cli-cursor "^3.1.0"
-    cli-table3 "~0.6.0"
+    cli-table3 "~0.6.1"
     commander "^5.1.0"
     commander "^5.1.0"
     common-tags "^1.8.0"
     common-tags "^1.8.0"
     dayjs "^1.10.4"
     dayjs "^1.10.4"
     debug "^4.3.2"
     debug "^4.3.2"
     enquirer "^2.3.6"
     enquirer "^2.3.6"
-    eventemitter2 "^6.4.3"
+    eventemitter2 "6.4.7"
     execa "4.1.0"
     execa "4.1.0"
     executable "^4.1.1"
     executable "^4.1.1"
     extract-zip "2.0.1"
     extract-zip "2.0.1"
@@ -7822,15 +7837,15 @@ cypress@^9.2.0:
     listr2 "^3.8.3"
     listr2 "^3.8.3"
     lodash "^4.17.21"
     lodash "^4.17.21"
     log-symbols "^4.0.0"
     log-symbols "^4.0.0"
-    minimist "^1.2.5"
+    minimist "^1.2.6"
     ospath "^1.2.2"
     ospath "^1.2.2"
     pretty-bytes "^5.6.0"
     pretty-bytes "^5.6.0"
     proxy-from-env "1.0.0"
     proxy-from-env "1.0.0"
     request-progress "^3.0.0"
     request-progress "^3.0.0"
+    semver "^7.3.2"
     supports-color "^8.1.1"
     supports-color "^8.1.1"
     tmp "~0.2.1"
     tmp "~0.2.1"
     untildify "^4.0.0"
     untildify "^4.0.0"
-    url "^0.11.0"
     yauzl "^2.10.0"
     yauzl "^2.10.0"
 
 
 damerau-levenshtein@^1.0.7:
 damerau-levenshtein@^1.0.7:
@@ -9787,10 +9802,10 @@ event-target-shim@^5.0.0:
   resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
   resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
   integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
   integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
 
 
-eventemitter2@^6.4.3:
-  version "6.4.5"
-  resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.5.tgz#97380f758ae24ac15df8353e0cc27f8b95644655"
-  integrity sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==
+eventemitter2@6.4.7:
+  version "6.4.7"
+  resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d"
+  integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==
 
 
 eventemitter3@^3.1.0:
 eventemitter3@^3.1.0:
   version "3.1.2"
   version "3.1.2"
@@ -24065,7 +24080,7 @@ url@0.10.3:
     punycode "1.3.2"
     punycode "1.3.2"
     querystring "0.2.0"
     querystring "0.2.0"
 
 
-url@0.11.0, url@^0.11.0:
+url@0.11.0:
   version "0.11.0"
   version "0.11.0"
   resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
   resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
   dependencies:
   dependencies: