Browse Source

Merge pull request #109 from Tumao727/test/client-component

update client tests
nameczz 4 years ago
parent
commit
f9e82ad8ae
26 changed files with 331 additions and 71 deletions
  1. 9 0
      client/package.json
  2. 20 0
      client/src/components/__test__/cards/EmptyCard.spec.tsx
  3. 8 1
      client/src/components/__test__/copy/Copy.spec.tsx
  4. 0 0
      client/src/components/__test__/customButton/CustomButton.spec.tsx
  5. 28 0
      client/src/components/__test__/customButton/CustomIconButton.spec.tsx
  6. 6 0
      client/src/components/__test__/customDialog/CustomDialog.spec.tsx
  7. 20 0
      client/src/components/__test__/customDialog/CustomDialogTitle.spec.tsx
  8. 50 0
      client/src/components/__test__/customDialog/DeleteDialogTemplate.spec.tsx
  9. 53 0
      client/src/components/__test__/customDialog/DialogTemplate.spec.tsx
  10. 4 0
      client/src/components/__test__/customInput/CustomInput.spec.tsx
  11. 1 1
      client/src/components/__test__/grid/Grid.spec.tsx
  12. 18 0
      client/src/components/__test__/layout/Layout.spec.tsx
  13. 7 2
      client/src/components/__test__/status/Status.spec.tsx
  14. 6 0
      client/src/components/__test__/utils/provideTheme.tsx
  15. 5 1
      client/src/components/customDialog/CustomDialogTitle.tsx
  16. 8 3
      client/src/components/customDialog/DeleteDialogTemplate.tsx
  17. 2 1
      client/src/components/customDialog/DialogTemplate.tsx
  18. 0 0
      client/src/components/grid/Grid.tsx
  19. 0 10
      client/src/components/layout/Layout.tsx
  20. 18 16
      client/src/components/menu/SimpleMenu.tsx
  21. 22 31
      client/src/hooks/__test__/Form.spec.tsx
  22. 2 2
      client/src/http/Axios.ts
  23. 1 1
      client/src/pages/collections/Collections.tsx
  24. 1 1
      client/src/pages/partitions/Partitions.tsx
  25. 1 1
      client/src/pages/schema/Schema.tsx
  26. 41 0
      client/yarn.lock

+ 9 - 0
client/package.json

@@ -31,10 +31,16 @@
     "typescript": "^4.1.2",
     "typescript": "^4.1.2",
     "web-vitals": "^1.0.1"
     "web-vitals": "^1.0.1"
   },
   },
+  "jest": {
+    "coverageDirectory": "<rootDir>/coverage/"
+  },
   "scripts": {
   "scripts": {
     "start": "react-app-rewired start -FAST_REFRESH=true",
     "start": "react-app-rewired start -FAST_REFRESH=true",
     "build": "react-app-rewired build",
     "build": "react-app-rewired build",
     "test": "react-app-rewired test",
     "test": "react-app-rewired test",
+    "test:watch": "react-app-rewired test --watch",
+    "test:cov": "react-app-rewired test --watchAll=false --coverage",
+    "test:report": "react-app-rewired test --watchAll=false --coverage --coverageReporters='text-summary'",
     "eject": "react-app-rewired eject"
     "eject": "react-app-rewired eject"
   },
   },
   "eslintConfig": {
   "eslintConfig": {
@@ -54,5 +60,8 @@
       "last 1 firefox version",
       "last 1 firefox version",
       "last 1 safari version"
       "last 1 safari version"
     ]
     ]
+  },
+  "devDependencies": {
+    "@testing-library/react-hooks": "^7.0.1"
   }
   }
 }
 }

+ 20 - 0
client/src/components/__test__/cards/EmptyCard.spec.tsx

@@ -0,0 +1,20 @@
+import { render, screen, RenderResult } from '@testing-library/react';
+import EmptyCard from '../../cards/EmptyCard';
+import provideTheme from '../utils/provideTheme';
+
+let body: RenderResult;
+
+describe('test empty card component', () => {
+  beforeEach(() => {
+    body = render(
+      provideTheme(
+        <EmptyCard icon={<span className="icon">icon</span>} text="empty" />
+      )
+    );
+  });
+
+  it('renders default state', () => {
+    expect(screen.getByText('icon')).toHaveClass('icon');
+    expect(screen.getByText('empty')).toBeInTheDocument();
+  });
+});

+ 8 - 1
client/src/components/__test__/copy/Copy.spec.tsx

@@ -1,10 +1,17 @@
+import { ReactNode } from 'react';
 import { render, unmountComponentAtNode } from 'react-dom';
 import { render, unmountComponentAtNode } from 'react-dom';
 import { act } from 'react-dom/test-utils';
 import { act } from 'react-dom/test-utils';
 import Copy from '../../copy/Copy';
 import Copy from '../../copy/Copy';
 let container: any = null;
 let container: any = null;
 
 
+jest.mock('react-i18next', () => ({
+  useTranslation: () => ({
+    t: (key: any) => key,
+  }),
+}));
+
 jest.mock('@material-ui/core/Tooltip', () => {
 jest.mock('@material-ui/core/Tooltip', () => {
-  return props => {
+  return (props: { children: ReactNode }) => {
     return <div id="tooltip">{props.children}</div>;
     return <div id="tooltip">{props.children}</div>;
   };
   };
 });
 });

+ 0 - 0
client/src/components/__test__/customButton/customButton.spec.tsx → client/src/components/__test__/customButton/CustomButton.spec.tsx


+ 28 - 0
client/src/components/__test__/customButton/CustomIconButton.spec.tsx

@@ -0,0 +1,28 @@
+import { fireEvent, render, screen } from '@testing-library/react';
+import CustomIconButton from '../../customButton/CustomIconButton';
+
+describe('test custom icon button component', () => {
+  it('renders default state', () => {
+    render(
+      <CustomIconButton>
+        <div className="icon">icon</div>
+      </CustomIconButton>
+    );
+
+    expect(screen.getByText('icon')).toHaveClass('icon');
+
+    const tooltip = screen.queryByText('tooltip');
+    expect(tooltip).toBeNull();
+  });
+
+  it('checks tooltip', async () => {
+    render(
+      <CustomIconButton tooltip="tooltip">
+        <div className="icon">icon</div>
+      </CustomIconButton>
+    );
+    // mock hover event
+    fireEvent.mouseOver(screen.getByText('icon'));
+    expect(await screen.findByText('tooltip')).toBeInTheDocument();
+  });
+});

+ 6 - 0
client/src/components/__test__/customDialog/CustomDialog.spec.tsx

@@ -6,6 +6,12 @@ import CustomDialog from '../../customDialog/CustomDialog';
 
 
 let container: any = null;
 let container: any = null;
 
 
+jest.mock('react-i18next', () => ({
+  useTranslation: () => ({
+    t: (key: any) => key,
+  }),
+}));
+
 jest.mock('@material-ui/core/Dialog', () => {
 jest.mock('@material-ui/core/Dialog', () => {
   return props => {
   return props => {
     return <div id="dialog-wrapper">{props.children}</div>;
     return <div id="dialog-wrapper">{props.children}</div>;

+ 20 - 0
client/src/components/__test__/customDialog/CustomDialogTitle.spec.tsx

@@ -0,0 +1,20 @@
+import { fireEvent, render } from '@testing-library/react';
+import CustomDialogTitle from '../../customDialog/CustomDialogTitle';
+
+describe('test custom dialog title component', () => {
+  it('renders default state', () => {
+    const container = render(<CustomDialogTitle>title</CustomDialogTitle>);
+
+    expect(container.getByText('title')).toBeInTheDocument();
+    expect(container.queryByTestId('clear-icon')).toBeNull();
+  });
+
+  it('checks clear event', () => {
+    const mockClearFn = jest.fn();
+    const container = render(
+      <CustomDialogTitle onClose={mockClearFn}>title</CustomDialogTitle>
+    );
+    fireEvent.click(container.getByTestId('clear-icon'));
+    expect(mockClearFn).toBeCalledTimes(1);
+  });
+});

+ 50 - 0
client/src/components/__test__/customDialog/DeleteDialogTemplate.spec.tsx

@@ -0,0 +1,50 @@
+import { screen, render, fireEvent } from '@testing-library/react';
+import DeleteTemplate from '../../customDialog/DeleteDialogTemplate';
+import provideTheme from '../utils/provideTheme';
+import { I18nextProvider } from 'react-i18next';
+import i18n from '../../../i18n';
+
+describe('test delete dialog template component', () => {
+  const mockDeleteFn = jest.fn();
+  const mockCancelFn = jest.fn();
+
+  beforeEach(() => {
+    render(
+      provideTheme(
+        <I18nextProvider i18n={i18n}>
+          <DeleteTemplate
+            title="delete title"
+            text="delete text"
+            label="delete"
+            handleDelete={mockDeleteFn}
+            handleCancel={mockCancelFn}
+          />
+        </I18nextProvider>
+      )
+    );
+  });
+
+  it('renders default state', () => {
+    expect(screen.getByText('delete title')).toBeInTheDocument();
+    expect(screen.getByText('delete text')).toBeInTheDocument();
+  });
+
+  it('checks button disabled status and callback when input value change', () => {
+    // Material Textfield role should be textbox
+    const input = screen.getByRole('textbox');
+    const deleteBtn = screen.getByRole('button', { name: /delete/i });
+    fireEvent.change(input, { target: { value: 'test' } });
+    expect(deleteBtn).toBeDisabled();
+    fireEvent.change(input, { target: { value: 'delete' } });
+    expect(deleteBtn).not.toBeDisabled();
+    fireEvent.click(deleteBtn);
+    expect(mockDeleteFn).toBeCalledTimes(1);
+  });
+
+  it('checks cancel callback', () => {
+    const cancelBtn = screen.getByRole('button', { name: /cancel/i });
+
+    fireEvent.click(cancelBtn);
+    expect(mockCancelFn).toBeCalledTimes(1);
+  });
+});

+ 53 - 0
client/src/components/__test__/customDialog/DialogTemplate.spec.tsx

@@ -0,0 +1,53 @@
+import { screen, render, fireEvent } from '@testing-library/react';
+import DialogTemplate from '../../customDialog/DialogTemplate';
+import provideTheme from '../utils/provideTheme';
+import { I18nextProvider } from 'react-i18next';
+import i18n from '../../../i18n';
+
+describe('test dialog template component', () => {
+  const mockCancelFn = jest.fn();
+  const mockConfirmFn = jest.fn();
+
+  it('renders default state and callbacks', () => {
+    render(
+      provideTheme(
+        <I18nextProvider i18n={i18n}>
+          <DialogTemplate
+            title="dialog template"
+            handleCancel={mockCancelFn}
+            handleConfirm={mockConfirmFn}
+          >
+            dialog content
+          </DialogTemplate>
+        </I18nextProvider>
+      )
+    );
+
+    expect(screen.getByText('dialog template')).toBeInTheDocument();
+    expect(screen.getByText('dialog content')).toBeInTheDocument();
+
+    fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
+    expect(mockCancelFn).toBeCalledTimes(1);
+    fireEvent.click(screen.getByRole('button', { name: /confirm/i }));
+    expect(mockConfirmFn).toBeCalledTimes(1);
+  });
+
+  it('checks confirm button disable', () => {
+    render(
+      provideTheme(
+        <I18nextProvider i18n={i18n}>
+          <DialogTemplate
+            title="dialog template"
+            handleCancel={mockCancelFn}
+            handleConfirm={mockConfirmFn}
+            confirmDisabled={true}
+          >
+            dialog content
+          </DialogTemplate>
+        </I18nextProvider>
+      )
+    );
+
+    expect(screen.getByRole('button', { name: /confirm/i })).toBeDisabled();
+  });
+});

+ 4 - 0
client/src/components/__test__/textField/customInput.spec.tsx → client/src/components/__test__/customInput/CustomInput.spec.tsx

@@ -10,6 +10,10 @@ import {
 
 
 let container: any = null;
 let container: any = null;
 
 
+jest.mock('@material-ui/core/styles/makeStyles', () => {
+  return () => () => ({});
+});
+
 jest.mock('@material-ui/core/FormControl', () => {
 jest.mock('@material-ui/core/FormControl', () => {
   return props => {
   return props => {
     const { children } = props;
     const { children } = props;

+ 1 - 1
client/src/components/__test__/grid/index.spec.tsx → client/src/components/__test__/grid/Grid.spec.tsx

@@ -1,6 +1,6 @@
 import { render, unmountComponentAtNode } from 'react-dom';
 import { render, unmountComponentAtNode } from 'react-dom';
 import { act } from 'react-dom/test-utils';
 import { act } from 'react-dom/test-utils';
-import MilvusGrid from '../../grid/index';
+import MilvusGrid from '../../grid/Grid';
 import { ToolBarConfig } from '../../grid/Types';
 import { ToolBarConfig } from '../../grid/Types';
 
 
 let container: any = null;
 let container: any = null;

+ 18 - 0
client/src/components/__test__/layout/Layout.spec.tsx

@@ -4,6 +4,24 @@ import Layout from '../../layout/Layout';
 
 
 let container: any = null;
 let container: any = null;
 
 
+jest.mock('react-i18next', () => ({
+  useTranslation: () => ({
+    t: (key: any) => key,
+  }),
+}));
+
+jest.mock('react-router-dom', () => ({
+  useHistory: () => ({
+    push: jest.fn(),
+  }),
+  useLocation: () => ({
+    hash: '',
+    pathname: '/use-location-mock',
+    search: '',
+    state: undefined,
+  }),
+}));
+
 jest.mock('../../layout/GlobalEffect', () => {
 jest.mock('../../layout/GlobalEffect', () => {
   return () => {
   return () => {
     return <div id="global">{}</div>;
     return <div id="global">{}</div>;

+ 7 - 2
client/src/components/__test__/status/Status.spec.tsx

@@ -1,3 +1,4 @@
+import { ReactNode } from 'react';
 import { render, unmountComponentAtNode } from 'react-dom';
 import { render, unmountComponentAtNode } from 'react-dom';
 import { act } from 'react-dom/test-utils';
 import { act } from 'react-dom/test-utils';
 import Status from '../../status/Status';
 import Status from '../../status/Status';
@@ -5,11 +6,15 @@ import { StatusEnum } from '../../status/Types';
 
 
 let container: any = null;
 let container: any = null;
 
 
+jest.mock('@material-ui/core/styles/makeStyles', () => {
+  return () => () => ({});
+});
+
 jest.mock('react-i18next', () => {
 jest.mock('react-i18next', () => {
   return {
   return {
     useTranslation: () => {
     useTranslation: () => {
       return {
       return {
-        t: name => {
+        t: () => {
           return {
           return {
             loaded: 'loaded',
             loaded: 'loaded',
             unloaded: 'unloaded',
             unloaded: 'unloaded',
@@ -22,7 +27,7 @@ jest.mock('react-i18next', () => {
 });
 });
 
 
 jest.mock('@material-ui/core/Typography', () => {
 jest.mock('@material-ui/core/Typography', () => {
-  return props => {
+  return (props: { children: ReactNode }) => {
     return <div className="label">{props.children}</div>;
     return <div className="label">{props.children}</div>;
   };
   };
 });
 });

+ 6 - 0
client/src/components/__test__/utils/provideTheme.tsx

@@ -0,0 +1,6 @@
+import { ReactElement } from 'react';
+import { MuiThemeProvider } from '@material-ui/core/styles';
+import { theme } from '../../../styles/theme';
+export default (ui: ReactElement): ReactElement => {
+  return <MuiThemeProvider theme={theme}>{ui}</MuiThemeProvider>;
+};

+ 5 - 1
client/src/components/customDialog/CustomDialogTitle.tsx

@@ -43,7 +43,11 @@ const CustomDialogTitle = (props: IProps) => {
     >
     >
       <Typography variant="h5">{children}</Typography>
       <Typography variant="h5">{children}</Typography>
       {onClose ? (
       {onClose ? (
-        <ClearIcon classes={{ root: innerClass.icon }} onClick={onClose} />
+        <ClearIcon
+          data-testid="clear-icon"
+          classes={{ root: innerClass.icon }}
+          onClick={onClose}
+        />
       ) : null}
       ) : null}
     </MuiDialogTitle>
     </MuiDialogTitle>
   );
   );

+ 8 - 3
client/src/components/customDialog/DeleteDialogTemplate.tsx

@@ -38,7 +38,7 @@ const useStyles = makeStyles((theme: Theme) => ({
 }));
 }));
 
 
 const DeleteTemplate: FC<DeleteDialogContentType> = props => {
 const DeleteTemplate: FC<DeleteDialogContentType> = props => {
-  const { title, text, label, handleDelete, handleCancel = () => {} } = props;
+  const { title, text, label, handleDelete, handleCancel } = props;
   const { handleCloseDialog } = useContext(rootContext);
   const { handleCloseDialog } = useContext(rootContext);
   const classes = useStyles();
   const classes = useStyles();
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: dialogTrans } = useTranslation('dialog');
@@ -49,7 +49,7 @@ const DeleteTemplate: FC<DeleteDialogContentType> = props => {
 
 
   const onCancelClick = () => {
   const onCancelClick = () => {
     handleCloseDialog();
     handleCloseDialog();
-    handleCancel();
+    handleCancel && handleCancel();
   };
   };
 
 
   const onDeleteClick = () => {
   const onDeleteClick = () => {
@@ -97,7 +97,11 @@ const DeleteTemplate: FC<DeleteDialogContentType> = props => {
       </DialogContent>
       </DialogContent>
 
 
       <DialogActions className={classes.btnWrapper}>
       <DialogActions className={classes.btnWrapper}>
-        <CustomButton onClick={onCancelClick} className={classes.cancelBtn}>
+        <CustomButton
+          name="cancel"
+          onClick={onCancelClick}
+          className={classes.cancelBtn}
+        >
           {btnTrans('cancel')}
           {btnTrans('cancel')}
         </CustomButton>
         </CustomButton>
         <CustomButton
         <CustomButton
@@ -105,6 +109,7 @@ const DeleteTemplate: FC<DeleteDialogContentType> = props => {
           onClick={onDeleteClick}
           onClick={onDeleteClick}
           color="secondary"
           color="secondary"
           disabled={!deleteReady}
           disabled={!deleteReady}
+          name="delete"
         >
         >
           {label}
           {label}
         </CustomButton>
         </CustomButton>

+ 2 - 1
client/src/components/customDialog/DialogTemplate.tsx

@@ -35,7 +35,7 @@ const DialogTemplate: FC<DialogContainerProps> = ({
       <CustomDialogTitle onClose={handleCancel}>{title}</CustomDialogTitle>
       <CustomDialogTitle onClose={handleCancel}>{title}</CustomDialogTitle>
       <DialogContent>{children}</DialogContent>
       <DialogContent>{children}</DialogContent>
       <DialogActions className={classes.actions}>
       <DialogActions className={classes.actions}>
-        <CustomButton onClick={handleCancel} color="default">
+        <CustomButton onClick={handleCancel} color="default" name="cancel">
           {cancel}
           {cancel}
         </CustomButton>
         </CustomButton>
         <CustomButton
         <CustomButton
@@ -43,6 +43,7 @@ const DialogTemplate: FC<DialogContainerProps> = ({
           onClick={handleConfirm}
           onClick={handleConfirm}
           color="primary"
           color="primary"
           disabled={confirmDisabled}
           disabled={confirmDisabled}
+          name="confirm"
         >
         >
           {confirm}
           {confirm}
         </CustomButton>
         </CustomButton>

+ 0 - 0
client/src/components/grid/index.tsx → client/src/components/grid/Grid.tsx


+ 0 - 10
client/src/components/layout/Layout.tsx

@@ -25,16 +25,6 @@ const useStyles = makeStyles((theme: Theme) =>
       height: '100vh',
       height: '100vh',
       overflowY: 'scroll',
       overflowY: 'scroll',
     },
     },
-    activeConsole: {
-      '& path': {
-        fill: theme.palette.primary.main,
-      },
-    },
-    normalConsole: {
-      '& path': {
-        fill: theme.palette.milvusGrey.dark,
-      },
-    },
   })
   })
 );
 );
 
 

+ 18 - 16
client/src/components/menu/SimpleMenu.tsx

@@ -48,22 +48,24 @@ const SimpleMenu: FC<SimpleMenuType> = props => {
         anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
         anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
         transformOrigin={{ vertical: 'top', horizontal: 'center' }}
         transformOrigin={{ vertical: 'top', horizontal: 'center' }}
       >
       >
-        {menuItems.map((v, i) =>
-          typeof v.label === 'string' ? (
-            <MenuItem
-              classes={{ root: classes.menuItem }}
-              onClick={() => {
-                v.callback && v.callback();
-                handleClose();
-              }}
-              key={v.label + i}
-            >
-              {v.label}
-            </MenuItem>
-          ) : (
-            <span key={i}>{v.label}</span>
-          )
-        )}
+        <div>
+          {menuItems.map((v, i) =>
+            typeof v.label === 'string' ? (
+              <MenuItem
+                classes={{ root: classes.menuItem }}
+                onClick={() => {
+                  v.callback && v.callback();
+                  handleClose();
+                }}
+                key={v.label + i}
+              >
+                {v.label}
+              </MenuItem>
+            ) : (
+              <span key={i}>{v.label}</span>
+            )
+          )}
+        </div>
       </Menu>
       </Menu>
     </div>
     </div>
   );
   );

+ 22 - 31
client/src/hooks/__test__/Form.spec.tsx

@@ -1,11 +1,6 @@
-import { render } from '@testing-library/react';
+import { renderHook, act } from '@testing-library/react-hooks';
 import { IForm, useFormValidation } from '../Form';
 import { IForm, useFormValidation } from '../Form';
 
 
-// jest.mock('react', () => ({
-//   ...jest.requireActual('react'),
-//   useState: jest.fn().mockReturnValue([[], jest.fn]),
-// }));
-
 const mockForm: IForm[] = [
 const mockForm: IForm[] = [
   {
   {
     key: 'username',
     key: 'username',
@@ -19,42 +14,38 @@ const mockForm: IForm[] = [
   },
   },
 ];
 ];
 
 
-const setupUseFormValidation = () => {
-  const returnVal: any = {};
-
-  const TestComponent = () => {
-    Object.assign(returnVal, useFormValidation(mockForm));
-    return null;
-  };
-
-  render(<TestComponent />);
-  return returnVal;
-};
-
 test('test useFormValidation hook', () => {
 test('test useFormValidation hook', () => {
-  const { checkFormValid, checkIsValid, validation } = setupUseFormValidation();
+  const { result } = renderHook(() => useFormValidation(mockForm));
+  const { checkFormValid, checkIsValid, validation } = result.current;
 
 
   expect(checkFormValid(mockForm)).toBeFalsy();
   expect(checkFormValid(mockForm)).toBeFalsy();
   expect(Object.keys(validation)).toEqual(['username']);
   expect(Object.keys(validation)).toEqual(['username']);
-  expect(
-    checkIsValid({
+
+  act(() => {
+    const { result } = checkIsValid({
       value: '',
       value: '',
       key: 'username',
       key: 'username',
       rules: [{ rule: 'require', errorText: 'name is required' }],
       rules: [{ rule: 'require', errorText: 'name is required' }],
-    }).result
-  ).toBeTruthy();
-  expect(
-    checkIsValid({
+    });
+
+    expect(result).toBeTruthy();
+  });
+
+  act(() => {
+    const { result } = checkIsValid({
       value: '11111',
       value: '11111',
       key: 'email',
       key: 'email',
       rules: [{ rule: 'email', errorText: 'email is invalid' }],
       rules: [{ rule: 'email', errorText: 'email is invalid' }],
-    }).result
-  ).toBeTruthy();
-  expect(
-    checkIsValid({
+    });
+    expect(result).toBeTruthy();
+  });
+
+  act(() => {
+    const { result } = checkIsValid({
       value: '12345678aQ',
       value: '12345678aQ',
       key: 'password',
       key: 'password',
       rules: [{ rule: 'password', errorText: 'password is invalid' }],
       rules: [{ rule: 'password', errorText: 'password is invalid' }],
-    }).result
-  ).toBeTruthy();
+    });
+    expect(result).toBeTruthy();
+  });
 });
 });

+ 2 - 2
client/src/http/Axios.ts

@@ -1,8 +1,8 @@
 import axios from 'axios';
 import axios from 'axios';
 // import { SESSION } from '../consts/Localstorage';
 // import { SESSION } from '../consts/Localstorage';
 
 
-console.log(process.env.NODE_ENV, 'api:', process.env.REACT_APP_BASE_URL);
-console.log('docker env', (window as any)._env_);
+// console.log(process.env.NODE_ENV, 'api:', process.env.REACT_APP_BASE_URL);
+// console.log('docker env', (window as any)._env_);
 
 
 export const url =
 export const url =
   ((window as any)._env_ && (window as any)._env_.HOST_URL) ||
   ((window as any)._env_ && (window as any)._env_.HOST_URL) ||

+ 1 - 1
client/src/pages/collections/Collections.tsx

@@ -2,7 +2,7 @@ import { useCallback, useContext, useEffect, useState } from 'react';
 import { Link } from 'react-router-dom';
 import { Link } from 'react-router-dom';
 import { useNavigationHook } from '../../hooks/Navigation';
 import { useNavigationHook } from '../../hooks/Navigation';
 import { ALL_ROUTER_TYPES } from '../../router/Types';
 import { ALL_ROUTER_TYPES } from '../../router/Types';
-import MilvusGrid from '../../components/grid';
+import MilvusGrid from '../../components/grid/Grid';
 import CustomToolBar from '../../components/grid/ToolBar';
 import CustomToolBar from '../../components/grid/ToolBar';
 import { CollectionCreateParam, CollectionView, DataTypeEnum } from './Types';
 import { CollectionCreateParam, CollectionView, DataTypeEnum } from './Types';
 import { ColDefinitionsType, ToolBarConfig } from '../../components/grid/Types';
 import { ColDefinitionsType, ToolBarConfig } from '../../components/grid/Types';

+ 1 - 1
client/src/pages/partitions/Partitions.tsx

@@ -5,7 +5,7 @@ import {
   // PartitionParam,
   // PartitionParam,
   PartitionView,
   PartitionView,
 } from './Types';
 } from './Types';
-import MilvusGrid from '../../components/grid';
+import MilvusGrid from '../../components/grid/Grid';
 import { ColDefinitionsType, ToolBarConfig } from '../../components/grid/Types';
 import { ColDefinitionsType, ToolBarConfig } from '../../components/grid/Types';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { usePaginationHook } from '../../hooks/Pagination';
 import { usePaginationHook } from '../../hooks/Pagination';

+ 1 - 1
client/src/pages/schema/Schema.tsx

@@ -1,6 +1,6 @@
 import { makeStyles, Theme, Typography } from '@material-ui/core';
 import { makeStyles, Theme, Typography } from '@material-ui/core';
 import { FC, useCallback, useEffect, useState } from 'react';
 import { FC, useCallback, useEffect, useState } from 'react';
-import MilvusGrid from '../../components/grid';
+import MilvusGrid from '../../components/grid/Grid';
 import { ColDefinitionsType } from '../../components/grid/Types';
 import { ColDefinitionsType } from '../../components/grid/Types';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { usePaginationHook } from '../../hooks/Pagination';
 import { usePaginationHook } from '../../hooks/Pagination';

+ 41 - 0
client/yarn.lock

@@ -1762,6 +1762,17 @@
     lodash "^4.17.15"
     lodash "^4.17.15"
     redent "^3.0.0"
     redent "^3.0.0"
 
 
+"@testing-library/react-hooks@^7.0.1":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-7.0.1.tgz#8429d8bf55bfe82e486bd582dd06457c2464900a"
+  integrity sha512-bpEQ2SHSBSzBmfJ437NmnP+oArQ7aVmmULiAp6Ag2rtyLBLPNFSMmgltUbFGmQOJdPWo4Ub31kpUC5T46zXNwQ==
+  dependencies:
+    "@babel/runtime" "^7.12.5"
+    "@types/react" ">=16.9.0"
+    "@types/react-dom" ">=16.9.0"
+    "@types/react-test-renderer" ">=16.9.0"
+    react-error-boundary "^3.1.0"
+
 "@testing-library/react@^11.1.0":
 "@testing-library/react@^11.1.0":
   version "11.2.7"
   version "11.2.7"
   resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818"
   resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818"
@@ -1940,6 +1951,13 @@
   resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
   resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
   integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
   integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
 
 
+"@types/react-dom@>=16.9.0":
+  version "17.0.8"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.8.tgz#3180de6d79bf53762001ad854e3ce49f36dd71fc"
+  integrity sha512-0ohAiJAx1DAUEcY9UopnfwCE9sSMDGnY/oXjWMax6g3RpzmTt2GMyMVAXcbn0mo8XAff0SbQJl2/SBU+hjSZ1A==
+  dependencies:
+    "@types/react" "*"
+
 "@types/react-dom@^17.0.0":
 "@types/react-dom@^17.0.0":
   version "17.0.6"
   version "17.0.6"
   resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.6.tgz#c158325cf91b196270bc0f4af73463f149e7eafe"
   resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.6.tgz#c158325cf91b196270bc0f4af73463f149e7eafe"
@@ -1971,6 +1989,13 @@
     "@types/history" "*"
     "@types/history" "*"
     "@types/react" "*"
     "@types/react" "*"
 
 
+"@types/react-test-renderer@>=16.9.0":
+  version "17.0.1"
+  resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz#3120f7d1c157fba9df0118dae20cb0297ee0e06b"
+  integrity sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw==
+  dependencies:
+    "@types/react" "*"
+
 "@types/react-transition-group@^4.2.0":
 "@types/react-transition-group@^4.2.0":
   version "4.4.1"
   version "4.4.1"
   resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.1.tgz#e1a3cb278df7f47f17b5082b1b3da17170bd44b1"
   resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.1.tgz#e1a3cb278df7f47f17b5082b1b3da17170bd44b1"
@@ -1987,6 +2012,15 @@
     "@types/scheduler" "*"
     "@types/scheduler" "*"
     csstype "^3.0.2"
     csstype "^3.0.2"
 
 
+"@types/react@>=16.9.0":
+  version "17.0.13"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.13.tgz#6b7c9a8f2868586ad87d941c02337c6888fb874f"
+  integrity sha512-D/G3PiuqTfE3IMNjLn/DCp6umjVCSvtZTPdtAFy5+Ved6CsdRvivfKeCzw79W4AatShtU4nGqgvOv5Gro534vQ==
+  dependencies:
+    "@types/prop-types" "*"
+    "@types/scheduler" "*"
+    csstype "^3.0.2"
+
 "@types/resolve@0.0.8":
 "@types/resolve@0.0.8":
   version "0.0.8"
   version "0.0.8"
   resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
   resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
@@ -9299,6 +9333,13 @@ react-dom@^17.0.2:
     object-assign "^4.1.1"
     object-assign "^4.1.1"
     scheduler "^0.20.2"
     scheduler "^0.20.2"
 
 
+react-error-boundary@^3.1.0:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.3.tgz#276bfa05de8ac17b863587c9e0647522c25e2a0b"
+  integrity sha512-A+F9HHy9fvt9t8SNDlonq01prnU8AmkjvGKV4kk8seB9kU3xMEO8J/PQlLVmoOIDODl5U2kufSBs4vrWIqhsAA==
+  dependencies:
+    "@babel/runtime" "^7.12.5"
+
 react-error-overlay@^6.0.9:
 react-error-overlay@^6.0.9:
   version "6.0.9"
   version "6.0.9"
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"