Parcourir la source

Merge branch 'master' into fix/110195-2-invalid-path-errs-are-shown-on-page-duplicate-modal

Kaori Tokashiki il y a 3 ans
Parent
commit
fc39884f8b

+ 4 - 0
.github/workflows/reusable-app-prod.yml

@@ -237,6 +237,10 @@ jobs:
       run: |
         npx lerna bootstrap -- --production
 
+    - name: lerna add packages needed for CI
+      run: |
+        npx lerna add yargs
+
     - name: Download production files artifact
       uses: actions/download-artifact@v3
       with:

+ 18 - 0
packages/app/src/components/ReactMarkdownComponents/Table.tsx

@@ -0,0 +1,18 @@
+import React from 'react';
+
+type TableProps = {
+  children: React.ReactNode,
+  className?: string
+}
+
+export const Table = React.memo((props: TableProps): JSX.Element => {
+
+  const { children, className } = props;
+
+  return (
+    <table className={`${className}`}>
+      {children}
+    </table>
+  );
+});
+Table.displayName = 'Table';

+ 1 - 0
packages/app/src/components/TableOfContents.tsx

@@ -60,6 +60,7 @@ const TableOfContents = (): JSX.Element => {
       >
         <div
           id="revision-toc-content"
+          data-testid="revision-toc-content"
           className="revision-toc-content mb-3"
         >
           {/* parse blank to show toc (https://github.com/weseek/growi/pull/6277) */}

+ 15 - 1
packages/app/src/pages/tags.page.tsx

@@ -3,6 +3,7 @@ import React, { useState, useCallback } from 'react';
 import type { IUser, IUserHasId } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 import Head from 'next/head';
 
@@ -24,7 +25,7 @@ import {
 } from '../stores/context';
 
 import {
-  CommonProps, getServerSideCommonProps, useCustomTitle,
+  CommonProps, getServerSideCommonProps, getNextI18NextConfig, useCustomTitle,
 } from './utils/commons';
 
 const PAGING_LIMIT = 10;
@@ -139,6 +140,17 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
   };
 }
 
+/**
+ * for Server Side Translations
+ * @param context
+ * @param props
+ * @param namespacesRequired
+ */
+async function injectNextI18NextConfigurations(context: GetServerSidePropsContext, props: Props, namespacesRequired?: string[] | undefined): Promise<void> {
+  const nextI18NextConfig = await getNextI18NextConfig(serverSideTranslations, context, namespacesRequired);
+  props._nextI18Next = nextI18NextConfig._nextI18Next;
+}
+
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
   const req = context.req as CrowiRequest<IUserHasId & any>;
   const { user } = req;
@@ -152,8 +164,10 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   if (user != null) {
     props.currentUser = user.toObject();
   }
+
   await injectUserUISettings(context, props);
   injectServerConfigurations(context, props);
+  await injectNextI18NextConfigurations(context, props, ['translation']);
 
   return {
     props,

+ 20 - 0
packages/app/src/services/renderer/remark-plugins/table.ts

@@ -0,0 +1,20 @@
+import { Plugin } from 'unified';
+import { visit } from 'unist-util-visit';
+
+export const remarkPlugin: Plugin = function() {
+  return (tree) => {
+    visit(tree, (node) => {
+      if (node.type === 'table' || node.type === 'tableCell' || node.type === 'tableRow') {
+
+        // omit position to fix the key regardless of its position
+        // see:
+        //   https://github.com/remarkjs/react-markdown/issues/703
+        //   https://github.com/remarkjs/react-markdown/issues/466
+        //
+        //   https://github.com/remarkjs/react-markdown/blob/a80dfdee2703d84ac2120d28b0e4998a5b417c85/lib/ast-to-react.js#L201-L204
+        //   https://github.com/remarkjs/react-markdown/blob/a80dfdee2703d84ac2120d28b0e4998a5b417c85/lib/ast-to-react.js#L217-L222
+        delete node.position;
+      }
+    });
+  };
+};

+ 8 - 2
packages/app/src/services/renderer/renderer.tsx

@@ -1,10 +1,10 @@
 // allow only types to import from react
 import { ComponentType } from 'react';
 
-import { Lsx, LsxImmutable } from '@growi/remark-lsx/components';
-import * as lsxGrowiPlugin from '@growi/remark-lsx/services/renderer';
 import * as drawioPlugin from '@growi/remark-drawio-plugin';
 import growiPlugin from '@growi/remark-growi-plugin';
+import { Lsx, LsxImmutable } from '@growi/remark-lsx/components';
+import * as lsxGrowiPlugin from '@growi/remark-lsx/services/renderer';
 import { Schema as SanitizeOption } from 'hast-util-sanitize';
 import { SpecialComponents } from 'react-markdown/lib/ast-to-react';
 import { NormalComponents } from 'react-markdown/lib/complex-types';
@@ -26,6 +26,7 @@ import { CodeBlock } from '~/components/ReactMarkdownComponents/CodeBlock';
 import { DrawioViewerWithEditButton } from '~/components/ReactMarkdownComponents/DrawioViewerWithEditButton';
 import { Header } from '~/components/ReactMarkdownComponents/Header';
 import { NextLink } from '~/components/ReactMarkdownComponents/NextLink';
+import { Table } from '~/components/ReactMarkdownComponents/Table';
 import { TableWithEditButton } from '~/components/ReactMarkdownComponents/TableWithEditButton';
 import { RendererConfig } from '~/interfaces/services/renderer';
 import loggerFactory from '~/utils/logger';
@@ -38,6 +39,7 @@ import { relativeLinksByPukiwikiLikeLinker } from './rehype-plugins/relative-lin
 import * as toc from './rehype-plugins/relocate-toc';
 import * as plantuml from './remark-plugins/plantuml';
 import { pukiwikiLikeLinker } from './remark-plugins/pukiwiki-like-linker';
+import * as table from './remark-plugins/table';
 import * as xsvToTable from './remark-plugins/xsv-to-table';
 
 // import CsvToTable from './PreProcessor/CsvToTable';
@@ -407,6 +409,7 @@ export const generateSimpleViewOptions = (config: RendererConfig, pagePath: stri
     drawioPlugin.remarkPlugin,
     xsvToTable.remarkPlugin,
     lsxGrowiPlugin.remarkPlugin,
+    table.remarkPlugin,
   );
   if (config.isEnabledLinebreaks) {
     remarkPlugins.push(breaks);
@@ -428,6 +431,7 @@ export const generateSimpleViewOptions = (config: RendererConfig, pagePath: stri
   if (components != null) {
     components.lsx = LsxImmutable;
     components.drawio = drawioPlugin.DrawioViewer;
+    components.table = Table;
   }
 
   verifySanitizePlugin(options, false);
@@ -446,6 +450,7 @@ export const generatePreviewOptions = (config: RendererConfig, pagePath: string)
     drawioPlugin.remarkPlugin,
     xsvToTable.remarkPlugin,
     lsxGrowiPlugin.remarkPlugin,
+    table.remarkPlugin,
   );
   if (config.isEnabledLinebreaks) {
     remarkPlugins.push(breaks);
@@ -468,6 +473,7 @@ export const generatePreviewOptions = (config: RendererConfig, pagePath: string)
   if (components != null) {
     components.lsx = LsxImmutable;
     components.drawio = drawioPlugin.DrawioViewer;
+    components.table = Table;
   }
 
   // verifySanitizePlugin(options, false);

+ 21 - 24
packages/app/test/cypress/integration/10-install/install.spec.ts

@@ -1,47 +1,42 @@
-context('Installer', () => {
-
+describe('Install', () => {
   const ssPrefix = 'installer-';
 
   beforeEach(() => {
     cy.visit('/');
-  })
+  });
 
-  it('successfully loads', () => {
-    cy.screenshot(`${ssPrefix}-on-load`);
+  it('Successfully show installer', () => {
     cy.getByTestid('installerForm').should('be.visible');
+    cy.screenshot(`${ssPrefix}-redirect-to-installer-page`);
   });
 
-  it('the dropdown for language works', () => {
+  it('Sccessfully choose languages', () => {
+    cy.getByTestid('installerForm').should('be.visible');
     cy.getByTestid('dropdownLanguage').should('be.visible');
+    // TODO: should not use wait.
+    // eslint-disable-next-line cypress/no-unnecessary-waiting
+    cy.wait(1000); // waiting for load
 
     cy.getByTestid('dropdownLanguage').click();
-    cy.screenshot(`${ssPrefix}-open-dropdownLanguage`);
-    cy.getByTestid('dropdownLanguage').click(); // close
-
-    cy.getByTestid('dropdownLanguage').click();
+    cy.get('.dropdown-menu').should('be.visible');
     cy.getByTestid('dropdownLanguageMenu-en_US').click();
+    cy.get('.alert-success').should('be.visible');
     cy.screenshot(`${ssPrefix}-select-en_US`);
 
     cy.getByTestid('dropdownLanguage').click();
+    cy.get('.dropdown-menu').should('be.visible');
     cy.getByTestid('dropdownLanguageMenu-ja_JP').click();
+    cy.get('.alert-success').should('be.visible');
     cy.screenshot(`${ssPrefix}-select-ja_JP`);
 
     cy.getByTestid('dropdownLanguage').click();
+    cy.get('.dropdown-menu').should('be.visible');
     cy.getByTestid('dropdownLanguageMenu-zh_CN').click();
+    cy.get('.alert-success').should('be.visible');
     cy.screenshot(`${ssPrefix}-select-zh_CN`);
   });
 
-});
-
-context('Installing', () => {
-
-  const ssPrefix = 'installing-';
-
-  beforeEach(() => {
-    cy.visit('/');
-  })
-
-  it('has succeeded', () => {
+  it('Successfully installing and redirect to root page', () => {
     cy.fixture("user-admin.json").then(user => {
       cy.getByTestid('tiUsername').type(user.username);
       cy.getByTestid('tiName').type(user.name);
@@ -52,9 +47,11 @@ context('Installing', () => {
 
     cy.getByTestid('btnSubmit').click();
 
-    cy.screenshot(`${ssPrefix}-installed`, {
-      blackout: ['#grw-sidebar-contents-wrapper','[data-line="2"]:eq(0) > a > img', '[data-hide-in-vrt=true]'],
+    // Redirects to the root page take a long time (more than 10000ms)
+    cy.waitUntilSkeletonDisappear();
+    cy.getByTestid('grw-pagetree-item-container').should('be.visible');
+    cy.screenshot(`${ssPrefix}-installed-redirect-to-root-page`, {
+      blackout: ['[data-hide-in-vrt=true]']
     });
   });
-
 });

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

@@ -15,7 +15,7 @@ context('Access to page', () => {
     cy.waitUntilSkeletonDisappear();
 
     // for check download toc data
-    cy.get('.toc-link').should('be.visible');
+    cy.get('.toc-link').eq(0).contains('Table of Contents');
 
     cy.screenshot(`${ssPrefix}-sandbox`);
   });
@@ -59,8 +59,11 @@ context('Access to page', () => {
       cy.wait(2000);
       cy.getByTestid('editor-button').should('be.visible').click();
     })
+
     cy.getByTestid('navbar-editor').should('be.visible');
     cy.get('.grw-editor-navbar-bottom').should('be.visible');
+    cy.getByTestid('save-page-btn').should('be.visible');
+    cy.get('.grw-grant-selector').should('be.visible')
 
     cy.screenshot(`${ssPrefix}-Sandbox-edit-page`);
   })
@@ -95,8 +98,9 @@ context('Access to /me page', () => {
 
   it('/me is successfully loaded', () => {
     cy.visit('/me');
-    // eslint-disable-next-line cypress/no-unnecessary-waiting
-    cy.wait(500); // wait loading image
+
+    cy.getByTestid('grw-user-settings').should('be.visible');
+
     cy.screenshot(`${ssPrefix}-me`);
   });
 

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

@@ -38,10 +38,12 @@ context('Modal for page operation', () => {
 
     cy.getByTestid('newPageBtn').click();
 
+    // eslint-disable-next-line cypress/no-unnecessary-waiting
+    cy.wait(500) // Wait for animation to finish when the Create Page button is pressed
+
     cy.getByTestid('page-create-modal').should('be.visible').within(() => {
       cy.screenshot(`${ssPrefix}new-page-modal-opened`);
       cy.get('button.close').click();
-
     });
     cy.screenshot(`${ssPrefix}page-create-modal-closed`);
   });
@@ -82,6 +84,7 @@ context('Modal for page operation', () => {
     cy.getByTestid('newPageBtn').click();
 
     cy.getByTestid('page-create-modal').should('be.visible').within(() => {
+      cy.get('.rbt-input-main').should('have.value', '/Sandbox/');
       cy.get('.rbt-input-main').type(pageName);
       cy.screenshot(`${ssPrefix}under-path-add-page-name`);
       cy.getByTestid('btn-create-page-under-below').click();
@@ -230,16 +233,13 @@ context('Page Accessories Modal', () => {
      cy.waitUntilSkeletonDisappear();
 
      cy.get('#grw-subnav-container').within(() => {
-      cy.getByTestid('open-page-item-control-btn').should('be.visible');
       cy.getByTestid('open-page-item-control-btn').within(() => {
         cy.get('button.btn-page-item-control').click({force: true});
-        cy.getByTestid('page-item-control-menu').should('be.visible');
-        cy.getByTestid('open-page-accessories-modal-btn-with-attachment-data-tab').click();
       });
+      cy.getByTestid('open-page-accessories-modal-btn-with-attachment-data-tab').click({force: true});
     });
 
-     cy.getByTestid('page-accessories-modal').should('be.visible')
-     cy.getByTestid('page-attachment').should('be.visible')
+     cy.getByTestid('page-attachment').should('be.visible').contains('No attachments yet.');
      cy.screenshot(`${ssPrefix}-open-page-attachment-data-bootstrap4`);
   });
 
@@ -317,19 +317,13 @@ context('Tag Oprations', { scrollBehavior: false }, () =>{
     cy.visit('/Sandbox');
     cy.waitUntilSkeletonDisappear();
 
-    cy.get('.grw-taglabels-container').within(()=>{
-      cy.get('.grw-tag-labels').within(()=>{
-        cy.get('a').then(($el)=>{
-          cy.wrap($el).contains(tag).click();
-        });
-      });
-    });
+    cy.get('.grw-tag-label').should('be.visible').contains(tag).click();
 
     // Search result page
     cy.getByTestid('search-result-base').should('be.visible');
     cy.getByTestid('search-result-list').should('be.visible');
-    cy.getByTestid('search-result-content').should('be.visible');
-    cy.get('#revision-loader').should('be.visible');
+    cy.getByTestid('search-result-content', { timeout: 60000 }).should('be.visible');
+    cy.get('#revision-loader', { timeout: 60000 }).contains('Table of Contents', { timeout: 60000 });
 
     // force to add 'active' to pass VRT: https://github.com/weseek/growi/pull/6603
     cy.getByTestid('page-list-item-L').first().invoke('addClass', 'active');
@@ -355,7 +349,10 @@ context('Tag Oprations', { scrollBehavior: false }, () =>{
     }).screenshot(`${ssPrefix}3-duplicate-page`);
 
     cy.getByTestid('page-duplicate-modal').within(() => {
+      cy.intercept('POST', '/_api/v3/pages/duplicate').as('duplicate');
       cy.get('.modal-footer > button.btn').click();
+      // Wait for completion of request to '/_api/v3/pages/duplicate'
+      cy.wait('@duplicate')
     });
 
     cy.visit(`Sandbox-${newPageName}`);
@@ -369,15 +366,16 @@ context('Tag Oprations', { scrollBehavior: false }, () =>{
     const oldPageName = '/Sandbox-our';
     const newPageName = '/Sandbox-us';
 
-    cy.visit('/Sandbox-our');
+    cy.visit(oldPageName);
     cy.waitUntilSkeletonDisappear();
 
-    // Search result page
     cy.get('.grw-tag-label').should('be.visible').contains(tag).click();
+
+    // Search result page
     cy.getByTestid('search-result-base').should('be.visible');
     cy.getByTestid('search-result-list').should('be.visible');
-    cy.getByTestid('search-result-content').should('be.visible');
-    cy.get('#revision-loader').should('be.visible');
+    cy.getByTestid('search-result-content', { timeout: 60000 }).should('be.visible');
+    cy.get('#revision-loader', { timeout: 60000 }).contains('Table of Contents', { timeout: 60000 });
 
     // eslint-disable-next-line cypress/no-unnecessary-waiting
     cy.wait(300);
@@ -417,7 +415,10 @@ context('Tag Oprations', { scrollBehavior: false }, () =>{
     }).screenshot(`${ssPrefix}3-insert-new-page-name`);
 
     cy.getByTestid('page-rename-modal').should('be.visible').within(() => {
-      cy.get('.modal-footer > button').click();
+      cy.intercept('PUT', '/_api/v3/pages/rename').as('rename');
+      cy.getByTestid('grw-page-rename-button').should('not.be.disabled').click();
+      // Wait for completion of request to '/_api/v3/pages/rename'
+      cy.wait('@rename')
     });
 
     cy.visit(newPageName);

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

@@ -25,6 +25,7 @@ context('Access to page by guest', () => {
 
   it('/Sandbox/Math is successfully loaded', () => {
     cy.visit('/Sandbox/Math');
+    cy.getByTestid('revision-toc-content').should('be.visible');
     cy.collapseSidebar(true, true);
 
     cy.get('.math').should('be.visible');

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

@@ -18,7 +18,7 @@ describe('Access to sidebar', () => {
 
       describe('Test show/collapse button', () => {
         it('Successfully show sidebar', () => {
-          cy.get('.grw-pagetree').should('be.visible');
+          cy.getByTestid('grw-pagetree-item-container').should('be.visible');
           cy.screenshot(`${ssPrefix}1-sidebar-shown`, {
             capture: 'viewport',
             // Blackout for recalculation of toc content hight
@@ -39,7 +39,7 @@ describe('Access to sidebar', () => {
       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.getByTestid('grw-pagetree-item-container').should('be.visible');
             cy.screenshot(`${ssPrefix}page-tree-1-access-to-page-tree`);
           });
         });

+ 3 - 3
yarn.lock

@@ -13460,9 +13460,9 @@ jose@^4.1.4:
   integrity sha512-f8E/z+T3Q0kA9txzH2DKvH/ds2uggcw0m3vVPSB9HrSkrQ7mojjifvS7aR8cw+lQl2Fcmx9npwaHpM/M3GD8UQ==
 
 jpeg-js@^0.4.0, jpeg-js@^0.4.2:
-  version "0.4.3"
-  resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b"
-  integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==
+  version "0.4.4"
+  resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
+  integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
 
 jquery-slimscroll@^1.3.8:
   version "1.3.8"