فهرست منبع

refactor and add an unit test

Yuki Takei 2 سال پیش
والد
کامیت
56c858fb06

+ 11 - 39
apps/app/src/components/TemplateModal.tsx → apps/app/src/components/TemplateModal/index.tsx

@@ -1,12 +1,8 @@
 import React, {
-  useCallback, useEffect, useMemo, useState,
+  useCallback, useEffect, useState,
 } from 'react';
 
-import path from 'path';
-
-import type { ITemplate } from '@growi/core';
-import dateFnsFormat from 'date-fns/format';
-import mustache from 'mustache';
+import type { ITemplate } from '@growi/core/dist/interfaces/template';
 import { useTranslation } from 'next-i18next';
 import {
   Modal,
@@ -16,12 +12,13 @@ import {
 } from 'reactstrap';
 
 import { useTemplateModal } from '~/stores/modal';
-import { useCurrentPagePath } from '~/stores/page';
 import { usePreviewOptions } from '~/stores/renderer';
 import { useTemplates } from '~/stores/template';
 import loggerFactory from '~/utils/logger';
 
-import Preview from './PageEditor/Preview';
+import Preview from '../PageEditor/Preview';
+
+import { useFormatter } from './use-formatter';
 
 const logger = loggerFactory('growi:components:TemplateModal');
 
@@ -54,7 +51,6 @@ const TemplateRadioButton = ({ template, onChange, isSelected }: TemplateRadioBu
 export const TemplateModal = (): JSX.Element => {
   const { t } = useTranslation();
 
-  const { data: currentPagePath } = useCurrentPagePath();
 
   const { data: templateModalStatus, close } = useTemplateModal();
 
@@ -63,34 +59,10 @@ export const TemplateModal = (): JSX.Element => {
 
   const [selectedTemplate, setSelectedTemplate] = useState<ITemplate>();
 
-  const formattedMarkdown = useMemo(() => {
-    if (selectedTemplate == null) {
-      return null;
-    }
-
-    // replace placeholder
-    let markdown = selectedTemplate.markdown;
-    const now = new Date();
-    try {
-      markdown = mustache.render(selectedTemplate.markdown, {
-        title: path.basename(currentPagePath ?? '/'),
-        path: currentPagePath ?? '/',
-        yyyy: dateFnsFormat(now, 'yyyy'),
-        MM: dateFnsFormat(now, 'MM'),
-        dd: dateFnsFormat(now, 'dd'),
-        HH: dateFnsFormat(now, 'HH'),
-        mm: dateFnsFormat(now, 'mm'),
-      });
-    }
-    catch (err) {
-      logger.warn('An error occured while ejs processing.', err);
-    }
-
-    return markdown;
-  }, [currentPagePath, selectedTemplate]);
+  const { format } = useFormatter();
 
   const submitHandler = useCallback((template?: ITemplate) => {
-    if (templateModalStatus == null || formattedMarkdown == null) {
+    if (templateModalStatus == null || selectedTemplate == null) {
       return;
     }
 
@@ -99,9 +71,9 @@ export const TemplateModal = (): JSX.Element => {
       return;
     }
 
-    templateModalStatus.onSubmit(formattedMarkdown);
+    templateModalStatus.onSubmit(format(selectedTemplate));
     close();
-  }, [close, formattedMarkdown, templateModalStatus]);
+  }, [close, format, selectedTemplate, templateModalStatus]);
 
   useEffect(() => {
     if (!templateModalStatus?.isOpened) {
@@ -133,13 +105,13 @@ export const TemplateModal = (): JSX.Element => {
           </div>
         </div>
 
-        { rendererOptions != null && formattedMarkdown != null && (
+        { rendererOptions != null && selectedTemplate != null && (
           <>
             <hr />
             <h3>Preview</h3>
             <div className='card'>
               <div className="card-body" style={{ maxHeight: '60vh', overflowY: 'auto' }}>
-                <Preview rendererOptions={rendererOptions} markdown={formattedMarkdown}/>
+                <Preview rendererOptions={rendererOptions} markdown={format(selectedTemplate)}/>
               </div>
             </div>
           </>

+ 36 - 0
apps/app/src/components/TemplateModal/use-formatter.spec.tsx

@@ -0,0 +1,36 @@
+import { useFormatter } from './use-formatter';
+
+
+const mocks = vi.hoisted(() => {
+  return {
+    useCurrentPagePathMock: vi.fn(() => { return {} }),
+  };
+});
+
+vi.mock('~/stores/page', () => {
+  return { useCurrentPagePath: mocks.useCurrentPagePathMock };
+});
+
+
+describe('useFormatter', () => {
+
+  describe('format()', () => {
+
+    it('returns an empty string when the argument is undefined', () => {
+      // setup
+      const mastacheMock = {
+        render: vi.fn(),
+      };
+      vi.doMock('mustache', () => mastacheMock);
+
+      // when
+      const { format } = useFormatter();
+      const markdown = format(undefined);
+
+      // then
+      expect(markdown).toBe('');
+      expect(mastacheMock.render).not.toHaveBeenCalled();
+    });
+
+  });
+});

+ 48 - 0
apps/app/src/components/TemplateModal/use-formatter.tsx

@@ -0,0 +1,48 @@
+import path from 'path';
+
+import type { ITemplate } from '@growi/core/dist/interfaces/template';
+import dateFnsFormat from 'date-fns/format';
+import mustache from 'mustache';
+
+import { useCurrentPagePath } from '~/stores/page';
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('growi:components:TemplateModal:use-formatter');
+
+
+type FormatMethod = (selectedTemplate?: ITemplate) => string;
+type FormatterData = {
+  format: FormatMethod,
+}
+
+export const useFormatter = (): FormatterData => {
+  const { data: currentPagePath } = useCurrentPagePath();
+
+  const format: FormatMethod = (selectedTemplate) => {
+    if (selectedTemplate == null) {
+      return '';
+    }
+
+    // replace placeholder
+    let markdown = selectedTemplate.markdown;
+    const now = new Date();
+    try {
+      markdown = mustache.render(selectedTemplate.markdown, {
+        title: path.basename(currentPagePath ?? '/'),
+        path: currentPagePath ?? '/',
+        yyyy: dateFnsFormat(now, 'yyyy'),
+        MM: dateFnsFormat(now, 'MM'),
+        dd: dateFnsFormat(now, 'dd'),
+        HH: dateFnsFormat(now, 'HH'),
+        mm: dateFnsFormat(now, 'mm'),
+      });
+    }
+    catch (err) {
+      logger.warn('An error occured while ejs processing.', err);
+    }
+
+    return markdown;
+  };
+
+  return { format };
+};