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

Merge branch 'master' into feat/sync-latest-revision-body-to-yjs-draft

Shun Miyazawa 1 год назад
Родитель
Сommit
ac5ac91c2d

+ 27 - 0
apps/app/playwright/23-editor/template-modal.spec.ts

@@ -0,0 +1,27 @@
+import { test, expect } from '@playwright/test';
+
+test('Successfully select template and template locale', async({ page }) => {
+  const jaText = '今日の目標';
+  const enText = "TODAY'S GOALS";
+  await page.goto('/Sandbox/TemplateModal');
+
+  // move to edit mode
+  await page.getByTestId('editor-button').click();
+  await expect(page.getByTestId('grw-editor-navbar-bottom')).toBeVisible();
+
+  // open TemplateModal
+  const templateModal = page.getByTestId('template-modal');
+  await page.getByTestId('open-template-button').click();
+  await expect(templateModal).toBeVisible();
+
+  // select template and template locale
+  await templateModal.locator('.list-group-item').nth(0).click();
+  await expect(templateModal.locator('.card-body').locator('.has-data-line').nth(1)).toHaveText(enText);
+  await templateModal.getByTestId('select-locale-dropdown-toggle').click();
+  await templateModal.getByTestId('select-locale-dropdown-item').nth(1).click();
+  await expect(templateModal.locator('.card-body').locator('.has-data-line').nth(1)).toHaveText(jaText);
+
+  // insert
+  await templateModal.locator('.btn-primary').click();
+  await expect(page.locator('.has-data-line').nth(1)).toHaveText(jaText);
+});

+ 9 - 1
apps/app/src/client/components/TemplateModal/TemplateModal.tsx

@@ -247,13 +247,21 @@ const TemplateModalSubstance = (props: TemplateModalSubstanceProps): JSX.Element
               </div>
               <div className="col-6 d-flex justify-content-end">
                 <UncontrolledDropdown>
-                  <DropdownToggle caret type="button" outline className="float-end" disabled={selectedTemplateSummary == null}>
+                  <DropdownToggle
+                    caret
+                    type="button"
+                    outline
+                    className="float-end"
+                    disabled={selectedTemplateSummary == null}
+                    data-testid="select-locale-dropdown-toggle"
+                  >
                     <span className="float-start">{selectedTemplateLocale != null ? selectedTemplateLocale : t('Language')}</span>
                   </DropdownToggle>
                   <DropdownMenu className="dropdown-menu" role="menu">
                     { selectedTemplateLocales != null && Array.from(selectedTemplateLocales).map((locale) => {
                       return (
                         <DropdownItem
+                          data-testid="select-locale-dropdown-item"
                           key={locale}
                           onClick={() => setSelectedTemplateLocale(locale)}
                         >

+ 1 - 0
apps/app/src/server/service/page/page-service.ts

@@ -33,4 +33,5 @@ export interface IPageService {
   ): boolean,
   getYjsData(pageId: string, revisionBody?: string): Promise<CurrentPageYjsData>,
   syncLatestRevisionBodyToYjsDraft(pageId: string): Promise<void>,
+  hasRevisionBodyDiff(pageId: string, comparisonTarget?: string): Promise<boolean>,
 }

+ 34 - 18
apps/app/src/server/service/socket-io.js → apps/app/src/server/service/socket-io.ts

@@ -1,29 +1,44 @@
+import type { IncomingMessage } from 'http';
+
+import type { IUserHasId } from '@growi/core/dist/interfaces';
 import { GlobalSocketEventName } from '@growi/core/dist/interfaces';
+import expressSession from 'express-session';
+import passport from 'passport';
+import type { Namespace } from 'socket.io';
 import { Server } from 'socket.io';
+import type { Document } from 'y-socket.io/dist/server';
 
 import { SocketEventName } from '~/interfaces/websocket';
 import loggerFactory from '~/utils/logger';
 
+import type Crowi from '../crowi';
 import { RoomPrefix, getRoomNameWithId } from '../util/socket-io-helpers';
 
+import { configManager } from './config-manager';
 import { getYjsConnectionManager, extractPageIdFromYdocId } from './yjs-connection-manager';
 
 
-const expressSession = require('express-session');
-const passport = require('passport');
-
 const logger = loggerFactory('growi:service:socket-io');
 
 
+type RequestWithUser = IncomingMessage & { user: IUserHasId };
+
 /**
  * Serve socket.io for server-to-client messaging
  */
 class SocketIoService {
 
+  crowi: Crowi;
+
+  guestClients: Set<string>;
+
+  io: Server;
+
+  adminNamespace: Namespace;
+
+
   constructor(crowi) {
     this.crowi = crowi;
-    this.configManager = crowi.configManager;
-
     this.guestClients = new Set();
   }
 
@@ -33,11 +48,9 @@ class SocketIoService {
 
   // Since the Order is important, attachServer() should be async
   async attachServer(server) {
-    this.io = new Server({
-      transports: ['websocket'],
+    this.io = new Server(server, {
       serveClient: false,
     });
-    this.io.attach(server);
 
     // create namespace for admin
     this.adminNamespace = this.io.of('/admin');
@@ -128,7 +141,7 @@ class SocketIoService {
 
   setupStoreGuestIdEventHandler() {
     this.io.on('connection', (socket) => {
-      if (socket.request.user == null) {
+      if ((socket.request as RequestWithUser).user == null) {
         this.guestClients.add(socket.id);
 
         socket.on('disconnect', () => {
@@ -140,7 +153,7 @@ class SocketIoService {
 
   setupLoginedUserRoomsJoinOnConnection() {
     this.io.on('connection', (socket) => {
-      const user = socket.request.user;
+      const user = (socket.request as RequestWithUser).user;
       if (user == null) {
         logger.debug('Socket io: An anonymous user has connected');
         return;
@@ -171,9 +184,12 @@ class SocketIoService {
 
     this.io.on('connection', (socket) => {
 
-      yjsConnectionManager.ysocketioInstance.on('awareness-update', async(update) => {
-        const pageId = extractPageIdFromYdocId(update.name);
-        const awarenessStateSize = update.awareness.states.size;
+      yjsConnectionManager.ysocketioInstance.on('awareness-update', async(doc: Document) => {
+        const pageId = extractPageIdFromYdocId(doc.name);
+
+        if (pageId == null) return;
+
+        const awarenessStateSize = doc.awareness.states.size;
 
         // Triggered when awareness changes
         this.io
@@ -207,12 +223,12 @@ class SocketIoService {
     const namespaceName = socket.nsp.name;
 
     if (namespaceName === '/admin') {
-      const clients = await this.getAdminSocket().allSockets();
+      const clients = await this.getAdminSocket().fetchSockets();
       const clientsCount = clients.length;
 
       logger.debug('Current count of clients for \'/admin\':', clientsCount);
 
-      const limit = this.configManager.getConfig('crowi', 's2cMessagingPubsub:connectionsLimitForAdmin');
+      const limit = configManager.getConfig('crowi', 's2cMessagingPubsub:connectionsLimitForAdmin');
       if (limit <= clientsCount) {
         const msg = `The connection was refused because the current count of clients for '/admin' is ${clientsCount} and exceeds the limit`;
         logger.warn(msg);
@@ -231,7 +247,7 @@ class SocketIoService {
 
       logger.debug('Current count of clients for guests:', clientsCount);
 
-      const limit = this.configManager.getConfig('crowi', 's2cMessagingPubsub:connectionsLimitForGuest');
+      const limit = configManager.getConfig('crowi', 's2cMessagingPubsub:connectionsLimitForGuest');
       if (limit <= clientsCount) {
         const msg = `The connection was refused because the current count of clients for guests is ${clientsCount} and exceeds the limit`;
         logger.warn(msg);
@@ -253,12 +269,12 @@ class SocketIoService {
       next();
     }
 
-    const clients = await this.getDefaultSocket().allSockets();
+    const clients = await this.getDefaultSocket().fetchSockets();
     const clientsCount = clients.length;
 
     logger.debug('Current count of clients for \'/\':', clientsCount);
 
-    const limit = this.configManager.getConfig('crowi', 's2cMessagingPubsub:connectionsLimit');
+    const limit = configManager.getConfig('crowi', 's2cMessagingPubsub:connectionsLimit');
     if (limit <= clientsCount) {
       const msg = `The connection was refused because the current count of clients for '/' is ${clientsCount} and exceeds the limit`;
       logger.warn(msg);

+ 0 - 67
apps/app/test/cypress/e2e/23-editor/23-editor--template-modal.cy.ts

@@ -1,67 +0,0 @@
-context('TemplateModal', () => {
-
-  const ssPrefix = 'template-modal-';
-
-  beforeEach(() => {
-    // login
-    cy.fixture("user-admin.json").then(user => {
-      cy.login(user.username, user.password);
-    });
-  });
-
-  it("TemplateModal is shown and closed successfully", () => {
-    cy.visit('/Sandbox/TemplateModal');
-    cy.collapseSidebar(true, true);
-
-    // move to edit mode
-    cy.get('#grw-page-editor-mode-manager').as('pageEditorModeManager').should('be.visible');
-    cy.getByTestid('editor-button').click();
-    cy.getByTestid('grw-editor-navbar-bottom').should('be.visible');
-
-    // open TemplateModal
-    cy.getByTestid('open-template-button').click();
-    cy.getByTestid('template-modal').should('be.visible');
-    cy.screenshot(`${ssPrefix}opened`);
-
-    // close TemplateModal
-    cy.getByTestid('template-modal').should('be.visible').within(() => {
-      cy.get('.btn-close').click();
-    });
-    cy.screenshot(`${ssPrefix}close`);
-  });
-
-  it("Successfully select template and template locale", () => {
-    cy.visit('/Sandbox/TemplateModal');
-    cy.collapseSidebar(true, true);
-
-    // move to edit mode
-    cy.get('#grw-page-editor-mode-manager').as('pageEditorModeManager').should('be.visible');
-    cy.getByTestid('editor-button').click();
-    cy.getByTestid('grw-editor-navbar-bottom').should('be.visible');
-
-    // open TemplateModal
-    cy.getByTestid('open-template-button').click();
-    cy.getByTestid('template-modal').should('be.visible');
-
-    // select template and template locale
-    cy.getByTestid('template-modal').should('be.visible').within(() => {
-      // select first template
-      cy.get('.list-group > .list-group-item:nth-child(1)').click();
-      // check preview exist
-      cy.get('.card-body').should('be.visible');
-      cy.screenshot(`${ssPrefix}select-template`);
-
-      // change template locale
-      cy.get('.modal-body > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) > div:nth-child(2) > .dropdown > button').click();
-      cy.get('.modal-body > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) > div:nth-child(2) > .dropdown > div > button:nth-child(2)').click();
-      cy.screenshot(`${ssPrefix}select-template-locale`);
-
-      // click insert button
-      cy.get('.modal-footer > button:nth-child(2)').click();
-    });
-
-    // check show template on markdown
-    cy.screenshot(`${ssPrefix}insert-template`);
-  });
-
-});