From 3ef3ba100317baa71944e1c66089e883a3ec67da Mon Sep 17 00:00:00 2001 From: LZS911 <932177767@qq.com> Date: Thu, 28 Nov 2024 17:58:06 +0800 Subject: [PATCH] [test]:(DataSourceComparison) Add unit tests --- .../__snapshots__/index.test.tsx.snap | 98 + .../components/BasicTreeSelect/index.test.tsx | 95 + .../lib/components/BasicTreeSelect/index.tsx | 2 +- .../__tests__/ComparisonTreeNode.test.tsx | 222 +++ .../__tests__/ModifedSqlAuditResult.test.tsx | 249 +++ .../__tests__/ModifiedSqlDrawer.test.tsx | 122 ++ .../__tests__/SqlAuditResult.test.tsx | 98 + .../__tests__/TreeNode.test.tsx | 366 ++++ .../ComparisonTreeNode.test.tsx.snap | 1545 +++++++++++++++ .../ModifedSqlAuditResult.test.tsx.snap | 1659 +++++++++++++++++ .../ModifiedSqlDrawer.test.tsx.snap | 3 + .../SqlAuditResult.test.tsx.snap | 221 +++ .../__snapshots__/index.test.tsx.snap | 226 +++ .../useComparisonResultTree.test.tsx.snap | 427 +++++ .../useDataSourceSelectorTree.test.tsx.snap | 123 ++ .../ComparisonEntry/__tests__/index.test.tsx | 226 +++ .../useComparisonResultTree.test.tsx | 377 ++++ .../useDataSourceSelectorTree.test.tsx | 144 ++ .../ComparisonDetailDrawer/index.tsx | 18 +- .../component/ComparisonTreeNode/index.tsx | 2 +- .../component/EnvironmentSelector/index.tsx | 5 +- .../ModifiedSqlAuditResult/index.tsx | 2 +- .../component/SqlAuditResult/index.tsx | 2 +- .../hooks/useDataSourceSelectorTree.tsx | 11 +- .../ComparisonEntry/index.tsx | 1 - .../ComparisonEntry/utils/TreeNode.tsx | 16 +- .../__snapshots__/index.ce.test.tsx.snap | 1202 ++++++++++++ .../__snapshots__/index.test.tsx.snap | 21 + .../__tests__/index.ce.test.tsx | 14 + .../__tests__/index.test.tsx | 9 + ...FormValuesWithGenModifiedSqlParams.test.ts | 219 +++ .../mockApi/database_comparison/data.ts | 284 +++ .../mockApi/database_comparison/index.ts | 43 + 33 files changed, 8020 insertions(+), 32 deletions(-) create mode 100644 packages/shared/lib/components/BasicTreeSelect/__snapshots__/index.test.tsx.snap create mode 100644 packages/shared/lib/components/BasicTreeSelect/index.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ComparisonTreeNode.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ModifedSqlAuditResult.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ModifiedSqlDrawer.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/SqlAuditResult.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/TreeNode.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/ComparisonTreeNode.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/ModifedSqlAuditResult.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/ModifiedSqlDrawer.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/SqlAuditResult.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/index.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/useComparisonResultTree.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/useDataSourceSelectorTree.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/index.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/useComparisonResultTree.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/useDataSourceSelectorTree.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/__tests__/__snapshots__/index.ce.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/__tests__/__snapshots__/index.test.tsx.snap create mode 100644 packages/sqle/src/page/DataSourceComparison/__tests__/index.ce.test.tsx create mode 100644 packages/sqle/src/page/DataSourceComparison/__tests__/index.test.tsx create mode 100644 packages/sqle/src/page/SqlExecWorkflow/Create/components/FormStep/SqlAuditInfoForm/SqlAuditInfoFormItem/DatabaseSelectionItems/__tests__/useSetFormValuesWithGenModifiedSqlParams.test.ts create mode 100644 packages/sqle/src/testUtils/mockApi/database_comparison/data.ts create mode 100644 packages/sqle/src/testUtils/mockApi/database_comparison/index.ts diff --git a/packages/shared/lib/components/BasicTreeSelect/__snapshots__/index.test.tsx.snap b/packages/shared/lib/components/BasicTreeSelect/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..938c5c74e --- /dev/null +++ b/packages/shared/lib/components/BasicTreeSelect/__snapshots__/index.test.tsx.snap @@ -0,0 +1,98 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BasicTreeSelect should render the not found content when loading is true 1`] = ` +
+
+
+
+ +
+ +
+`; diff --git a/packages/shared/lib/components/BasicTreeSelect/index.test.tsx b/packages/shared/lib/components/BasicTreeSelect/index.test.tsx new file mode 100644 index 000000000..baed19894 --- /dev/null +++ b/packages/shared/lib/components/BasicTreeSelect/index.test.tsx @@ -0,0 +1,95 @@ +import { fireEvent, screen } from '@testing-library/dom'; +import BasicTreeSelect from '.'; +import { superRender } from '../../testUtil/customRender'; +import { getBySelector } from '../../testUtil/customQuery'; + +describe('BasicTreeSelect', () => { + const mockProps = { + className: 'custom-class', + allowClear: true, + loading: false, + popupClassName: 'custom-popup-class', + treeData: [ + { + value: 'parent 1', + title: 'parent 1', + children: [ + { + value: 'parent 1-0', + title: 'parent 1-0', + children: [ + { + value: 'leaf1', + title: 'leaf1' + }, + { + value: 'leaf2', + title: 'leaf2' + }, + { + value: 'leaf3', + title: 'leaf3' + }, + { + value: 'leaf4', + title: 'leaf4' + }, + { + value: 'leaf5', + title: 'leaf5' + }, + { + value: 'leaf6', + title: 'leaf6' + } + ] + }, + { + value: 'parent 1-1', + title: 'parent 1-1', + children: [ + { + value: 'leaf11', + title: leaf11 + } + ] + } + ] + } + ] + }; + + it('should render the component with all elements', () => { + superRender(); + + expect(screen.getByText('请选择{{name}}')).toBeInTheDocument(); + expect(screen.getByRole('combobox')).toBeInTheDocument(); + expect(getBySelector('.basic-tree-select-wrapper')).toHaveClass( + 'custom-class' + ); + }); + + it('should render the dropdown menu correctly', () => { + superRender(); + + const treeSelect = screen.getByRole('combobox'); + fireEvent.mouseDown(treeSelect); + + const customMenu = screen.getByTestId( + 'basic-tree-select-popup-menu-style-wrapper' + ); + expect(customMenu).toBeInTheDocument(); + }); + + it('should render the not found content when loading is true', () => { + superRender(); + + const treeSelect = screen.getByRole('combobox'); + fireEvent.mouseDown(treeSelect); + + const customMenu = screen.getByTestId( + 'basic-tree-select-popup-menu-style-wrapper' + ); + expect(customMenu).toMatchSnapshot(); + }); +}); diff --git a/packages/shared/lib/components/BasicTreeSelect/index.tsx b/packages/shared/lib/components/BasicTreeSelect/index.tsx index e447c1baa..804bb540d 100644 --- a/packages/shared/lib/components/BasicTreeSelect/index.tsx +++ b/packages/shared/lib/components/BasicTreeSelect/index.tsx @@ -20,7 +20,7 @@ const BasicTreeSelect = ( const renderDropdown: TreeSelectProps['dropdownRender'] = (menu) => { const customMenu = ( <> - + {menu} diff --git a/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ComparisonTreeNode.test.tsx b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ComparisonTreeNode.test.tsx new file mode 100644 index 000000000..2cba06c3b --- /dev/null +++ b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ComparisonTreeNode.test.tsx @@ -0,0 +1,222 @@ +import { act, fireEvent, renderHook, screen } from '@testing-library/react'; +import { superRender } from '../../../../testUtils/customRender'; +import ComparisonDetailDrawer from '../component/ComparisonTreeNode/ComparisonDetailDrawer'; +import useComparisonResultTree from '../hooks/useComparisonResultTree'; +import { + executeDatabaseComparisonMockData, + genDatabaseDiffModifySQLsMockData +} from '../../../../testUtils/mockApi/database_comparison/data'; +import { generateTreeNodeKey } from '../utils/TreeNode'; +import { mockUseCurrentProject } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentProject'; +import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser'; +import DatabaseComparisonMockService from '../../../../testUtils/mockApi/database_comparison'; +import { getBySelector } from '@actiontech/shared/lib/testUtil/customQuery'; +import { mockProjectInfo } from '@actiontech/shared/lib/testUtil/mockHook/data'; +import { Copy, Download } from '@actiontech/shared'; +import { mockUsePermission } from '@actiontech/shared/lib/testUtil/mockHook/mockUsePermission'; +import dayjs from 'dayjs'; +import MockDate from 'mockdate'; +import ComparisonTreeNode from '../component/ComparisonTreeNode'; + +describe('ComparisonTreeNode', () => { + describe('ComparisonDetailDrawer', () => { + const onCloseSpy = jest.fn(); + let genDatabaseDiffModifySQLSpy: jest.SpyInstance; + let getComparisonStatementSpy: jest.SpyInstance; + const customRender = (open = true) => { + const { result } = renderHook(() => + useComparisonResultTree(executeDatabaseComparisonMockData) + ); + const getDetailParams = result.current.generateGetComparisonDetailParams( + generateTreeNodeKey('2', 'TABLE', 'task'), + 'baseline-id', + 'comparison-id', + 'baseline-name', + 'comparison-name' + ); + const genModifiedSqlParams = result.current.generateModifiedSqlParams( + generateTreeNodeKey('2', 'TABLE', 'task'), + 'baseline-id', + 'comparison-id', + 'baseline-name', + 'comparison-name' + ); + return superRender( + + ); + }; + + beforeEach(() => { + mockUseCurrentProject(); + mockUseCurrentUser(); + mockUsePermission( + { + checkActionPermission: jest.fn().mockReturnValue(true) + }, + { useSpyOnMockHooks: true } + ); + jest.useFakeTimers(); + MockDate.set(dayjs('2023-12-18 12:00:00').valueOf()); + + genDatabaseDiffModifySQLSpy = + DatabaseComparisonMockService.mockGenDatabaseDiffModifySQLsV1(); + getComparisonStatementSpy = + DatabaseComparisonMockService.mockGetComparisonStatementV1(); + }); + afterEach(() => { + jest.clearAllMocks(); + jest.clearAllTimers(); + jest.useRealTimers(); + MockDate.reset(); + }); + + it('should render the drawer when open is true', async () => { + const { baseElement } = customRender(); + expect(getComparisonStatementSpy).toHaveBeenCalledTimes(1); + expect(getComparisonStatementSpy).toHaveBeenCalledWith({ + database_comparison_object: { + base_db_object: { + instance_id: 'baseline-id', + schema_name: 'baseline-name' + } + }, + database_object: { + object_name: 'task', + object_type: 'TABLE' + }, + project_name: mockProjectInfo.projectName + }); + expect(screen.getByText('查看对比详情')).toBeInTheDocument(); + expect(baseElement).toMatchSnapshot(); + + await act(async () => jest.advanceTimersByTime(3000)); + expect(baseElement).toMatchSnapshot(); + }); + + it('should close the drawer and reset state when onClose is called', () => { + customRender(); + + fireEvent.click(getBySelector('.closed-icon-custom')); + expect(onCloseSpy).toHaveBeenCalled(); + }); + + it('should call genModifiedSqlAsyncApi when generate SQL button is clicked', async () => { + customRender(); + + await act(async () => jest.advanceTimersByTime(3000)); + fireEvent.click(screen.getByText('生成变更SQL')); + expect(genDatabaseDiffModifySQLSpy).toHaveBeenCalledTimes(1); + expect(genDatabaseDiffModifySQLSpy).toHaveBeenCalledWith({ + base_instance_id: 'baseline-id', + comparison_instance_id: 'comparison-id', + database_schema_objects: [ + { + base_schema_name: 'baseline-name', + comparison_schema_name: 'comparison-name', + database_objects: [ + { + object_name: 'task', + object_type: 'TABLE' + } + ] + } + ], + project_name: mockProjectInfo.projectName + }); + }); + + it('should copy modified SQL to clipboard when copy button is clicked', async () => { + const copyTextByTextarea = jest.fn(); + jest + .spyOn(Copy, 'copyTextByTextarea') + .mockImplementation(copyTextByTextarea); + + customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + fireEvent.click(screen.getByText('生成变更SQL')); + await act(async () => jest.advanceTimersByTime(3000)); + + fireEvent.click(screen.getByText('复制变更SQL语句')); + expect(copyTextByTextarea).toHaveBeenCalledWith( + genDatabaseDiffModifySQLsMockData[0].modify_sqls + ?.map((v) => v.sql_statement) + ?.join('\n') + ); + }); + + it('should download modified SQL when download button is clicked', async () => { + const downloadByCreateElementA = jest.fn(); + jest + .spyOn(Download, 'downloadByCreateElementA') + .mockImplementation(downloadByCreateElementA); + + customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + fireEvent.click(screen.getByText('生成变更SQL')); + await act(async () => jest.advanceTimersByTime(3000)); + + fireEvent.click(screen.getByText('下载变更SQL语句')); + expect(downloadByCreateElementA).toHaveBeenCalledWith( + genDatabaseDiffModifySQLsMockData[0].modify_sqls + ?.map((v) => v.sql_statement) + ?.join('\n'), + 'comparison-name-modified-sql-20231218120000.sql' + ); + }); + }); + + describe('ComparisonTreeNode', () => { + const comparisonObjectTreeOnCheckSpy = jest.fn(); + const customRender = () => { + return superRender( + + ); + }; + + beforeEach(() => { + mockUseCurrentProject(); + mockUseCurrentUser(); + }); + afterEach(() => { + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + + it('should render the baseline and comparison trees', () => { + const { container } = customRender(); + + expect(container).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ModifedSqlAuditResult.test.tsx b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ModifedSqlAuditResult.test.tsx new file mode 100644 index 000000000..ada7b2a6a --- /dev/null +++ b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ModifedSqlAuditResult.test.tsx @@ -0,0 +1,249 @@ +import { + IDatabaseDiffModifySQL, + ISQLStatementWithAuditResult +} from '@actiontech/shared/lib/api/sqle/service/common'; +import { superRender } from '../../../../testUtils/customRender'; +import ModifiedSqlAuditResult from '../component/ModifiedSqlAuditResult'; +import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser'; +import { fireEvent, screen } from '@testing-library/dom'; +import ModifiedSqlAuditResultList from '../component/ModifiedSqlAuditResult/List'; + +describe('ModifiedSqlAuditResult', () => { + const auditResultCollapseActiveKeysOnChangeSpy = jest.fn(); + const instanceName = 'MySQL-Test-01'; + const instanceType = 'MySQL'; + const auditResultCollapseActiveKeys = ['0', '1']; + const dataSource: IDatabaseDiffModifySQL[] = [ + { + schema_name: 'schema_name1', + modify_sqls: [ + { + sql_statement: 'USE sqle;\n' + }, + { + sql_statement: 'DROP TABLE `audit_files`;\n', + audit_results: [ + { + level: 'error', + message: '禁止除索引外的DROP操作', + rule_name: 'ddl_disable_drop_statement', + db_type: 'MySQL', + i18n_audit_result_info: { + en: { + Message: 'Do not use DROP operations except for indexes' + }, + zh: { + Message: '禁止除索引外的DROP操作' + } + } + } + ] + }, + { + sql_statement: 'DROP TABLE `audit_plan_report_sqls_v2`;\n', + audit_results: [ + { + level: 'error', + message: '禁止除索引外的DROP操作', + rule_name: 'ddl_disable_drop_statement', + db_type: 'MySQL', + i18n_audit_result_info: { + en: { + Message: 'Do not use DROP operations except for indexes' + }, + zh: { + Message: '禁止除索引外的DROP操作' + } + } + } + ] + } + ] + }, + { + schema_name: 'schema_name2', + modify_sqls: [ + { + sql_statement: 'USE sqle;\n' + }, + { + sql_statement: 'DROP TABLE `audit_files`;\n', + audit_results: [ + { + level: 'error', + message: '禁止除索引外的DROP操作', + rule_name: 'ddl_disable_drop_statement', + db_type: 'MySQL', + i18n_audit_result_info: { + en: { + Message: 'Do not use DROP operations except for indexes' + }, + zh: { + Message: '禁止除索引外的DROP操作' + } + } + } + ] + }, + { + sql_statement: 'DROP TABLE `audit_plan_report_sqls_v2`;\n', + audit_results: [ + { + level: 'error', + message: '禁止除索引外的DROP操作', + rule_name: 'ddl_disable_drop_statement', + db_type: 'MySQL', + i18n_audit_result_info: { + en: { + Message: 'Do not use DROP operations except for indexes' + }, + zh: { + Message: '禁止除索引外的DROP操作' + } + } + } + ] + } + ] + }, + { + schema_name: 'schema_name3', + modify_sqls: [ + { + sql_statement: 'USE sqle;\n' + }, + { + sql_statement: 'DROP TABLE `audit_files`;\n', + audit_results: [ + { + level: 'error', + message: '禁止除索引外的DROP操作', + rule_name: 'ddl_disable_drop_statement', + db_type: 'MySQL', + i18n_audit_result_info: { + en: { + Message: 'Do not use DROP operations except for indexes' + }, + zh: { + Message: '禁止除索引外的DROP操作' + } + } + } + ] + }, + { + sql_statement: 'DROP TABLE `audit_plan_report_sqls_v2`;\n', + audit_results: [ + { + level: 'error', + message: '禁止除索引外的DROP操作', + rule_name: 'ddl_disable_drop_statement', + db_type: 'MySQL', + i18n_audit_result_info: { + en: { + Message: 'Do not use DROP operations except for indexes' + }, + zh: { + Message: '禁止除索引外的DROP操作' + } + } + } + ] + } + ] + } + ]; + + describe('index', () => { + const customRender = (dataSource?: IDatabaseDiffModifySQL[]) => { + return superRender( + + ); + }; + + beforeEach(() => { + mockUseCurrentUser(); + }); + + afterEach(() => { + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + + it('should render without crashing when no dataSource is provided', () => { + const { container } = customRender(); + + expect(container).toMatchSnapshot(); + }); + + it('should set the activeTabKey to the first schema_name in dataSource on initial render if dataSource is provided', () => { + const { container } = customRender(dataSource); + + expect(container).toMatchSnapshot(); + + expect( + screen.getByTestId(dataSource[0].schema_name!).parentNode?.parentNode + ).toHaveClass('ant-segmented-item-selected'); + }); + + it('should update the activeTabKey and re-render the component when a new tab is selected', () => { + const { container } = customRender(dataSource); + + fireEvent.click(screen.getByTestId(dataSource[1].schema_name!)); + expect(container).toMatchSnapshot(); + expect( + screen.getByTestId(dataSource[1].schema_name!).parentNode?.parentNode + ).toHaveClass('ant-segmented-item-selected'); + }); + }); + + describe('list', () => { + const customRender = (dataSource?: ISQLStatementWithAuditResult[]) => { + return superRender( + + ); + }; + it('should render without crashing when no dataSource is provided', () => { + const { container } = customRender(); + expect(container).toMatchSnapshot(); + }); + + it('should render the correct number of list items based on the dataSource length', () => { + const { container } = customRender(dataSource[0].modify_sqls); + expect(container).toMatchSnapshot(); + + expect(screen.getAllByRole('listitem')).toHaveLength( + dataSource[0].modify_sqls!.length + ); + }); + + it('should render the SQLRenderer component with the correct SQL statement', () => { + customRender(dataSource[0].modify_sqls); + expect(screen.getByText(/USE sqle;/)).toBeInTheDocument(); + }); + + it('should call auditResultCollapseActiveKeysOnChange with updated keys when a collapse item is toggled', () => { + customRender(dataSource[1].modify_sqls); + fireEvent.click(screen.getAllByText(/变更SQL语句审核结果/)[0]); + expect(auditResultCollapseActiveKeysOnChangeSpy).toHaveBeenCalledTimes(1); + expect(auditResultCollapseActiveKeysOnChangeSpy).toHaveBeenCalledWith([ + '1' + ]); + }); + }); +}); diff --git a/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ModifiedSqlDrawer.test.tsx b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ModifiedSqlDrawer.test.tsx new file mode 100644 index 000000000..f93899680 --- /dev/null +++ b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/ModifiedSqlDrawer.test.tsx @@ -0,0 +1,122 @@ +import { DatabaseObjectObjectTypeEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; +import { superRender } from '../../../../testUtils/customRender'; +import ModifiedSqlDrawer from '../component/ModifiedSqlDrawer'; +import { fireEvent, screen } from '@testing-library/dom'; +import { mockUseCurrentProject } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentProject'; +import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser'; +import { mockUsePermission } from '@actiontech/shared/lib/testUtil/mockHook/mockUsePermission'; +import { getBySelector } from '@actiontech/shared/lib/testUtil/customQuery'; +import { mockProjectInfo } from '@actiontech/shared/lib/testUtil/mockHook/data'; +import { compressToEncodedURIComponent } from 'lz-string'; + +describe('ModifiedSqlDrawer', () => { + beforeEach(() => { + mockUseCurrentProject(); + mockUseCurrentUser(); + mockUsePermission( + { + checkActionPermission: jest.fn().mockReturnValue(true) + }, + { useSpyOnMockHooks: true } + ); + }); + afterEach(() => { + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + + const onCloseSpy = jest.fn(); + const apiParams = { + project_name: 'default', + base_instance_id: '1861344168081625088', + comparison_instance_id: '1861344168081625088', + database_schema_objects: [ + { + base_schema_name: 'dms', + comparison_schema_name: 'sqle', + database_objects: [ + { + object_name: 'audit_files', + object_type: DatabaseObjectObjectTypeEnum.TABLE + }, + { + object_name: 'audit_plan_report_sqls_v2', + object_type: DatabaseObjectObjectTypeEnum.TABLE + } + ] + } + ] + }; + const dbExistingSchemas = ['schema']; + const comparisonInstanceName = 'test-instance-name'; + const customRender = (open = true, generateModifySqlPending = false) => { + return superRender( + + ); + }; + + it('should not render the drawer when open is false', () => { + customRender(false); + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + }); + + it('should render the drawer with the correct title when open is true', () => { + customRender(true); + + expect(screen.getByText('变更SQL语句信息')).toBeInTheDocument(); + }); + + it('should call onClose and reset auditResultCollapseActiveKeys when the close button is clicked', () => { + customRender(true); + + fireEvent.click(getBySelector('.closed-icon-custom')); + expect(onCloseSpy).toHaveBeenCalled(); + }); + + it('should display a loading spinner when generateModifySqlPending is true', () => { + const { container } = customRender(true, true); + + expect(container).toMatchSnapshot(); + }); + + it('should render the extra content provided by CreateWorkflowForModifiedSqlAction', () => { + customRender(true); + expect(screen.getByText('生成变更工单')).toBeInTheDocument(); + expect(screen.getByText('生成变更工单').closest('a')).toHaveAttribute( + 'href', + `/sqle/project/${ + mockProjectInfo.projectID + }/exec-workflow/create?gen_modified_sql_params=${compressToEncodedURIComponent( + JSON.stringify({ + ...apiParams, + comparisonInstanceName, + dbExistingSchemas + }) + )}` + ); + }); + + it('should not render the extra content when checkActionPermission is return false', () => { + mockUsePermission( + { + checkActionPermission: jest.fn().mockReturnValue(false) + }, + { useSpyOnMockHooks: true } + ); + + customRender(true); + expect(screen.queryByText('生成变更工单')).not.toBeInTheDocument(); + }); +}); diff --git a/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/SqlAuditResult.test.tsx b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/SqlAuditResult.test.tsx new file mode 100644 index 000000000..7267c70d7 --- /dev/null +++ b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/SqlAuditResult.test.tsx @@ -0,0 +1,98 @@ +import { superRender } from '../../../../testUtils/customRender'; +import rule_template from '../../../../testUtils/mockApi/rule_template'; +import SqlAuditResult from '../component/SqlAuditResult'; + +describe('SqlAuditResult', () => { + let getRulesSpy: jest.SpyInstance; + + beforeEach(() => { + getRulesSpy = rule_template.getRuleList(); + jest.useFakeTimers(); + }); + afterEach(() => { + jest.useRealTimers(); + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + + it('should match snapshot when audit result is empty', () => { + const { container } = superRender( + + ); + + expect(getRulesSpy).toHaveBeenCalledTimes(0); + expect(container).toMatchSnapshot(); + }); + + it('should match snapshot when audit error is not empty', () => { + const { container } = superRender( + + ); + + expect(getRulesSpy).toHaveBeenCalledTimes(0); + expect(container).toMatchSnapshot(); + }); + + it('should match snapshot when shouldFetchRules is equal false', () => { + const { container } = superRender( + + ); + + expect(getRulesSpy).toHaveBeenCalledTimes(0); + expect(container).toMatchSnapshot(); + }); + + it('should match snapshot when shouldFetchRules is equal true', () => { + const { container } = superRender( + + ); + + expect(getRulesSpy).toHaveBeenCalledTimes(1); + expect(getRulesSpy).toHaveBeenCalledWith({ + filter_db_type: 'MySQL', + filter_rule_names: 'rule1,rule2' + }); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/TreeNode.test.tsx b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/TreeNode.test.tsx new file mode 100644 index 000000000..5a0d5c692 --- /dev/null +++ b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/TreeNode.test.tsx @@ -0,0 +1,366 @@ +import { + DatabaseTableFilled, + DatabaseViewFilled, + MenuSquareFilled +} from '@actiontech/icons'; +import { + DatabaseDiffObjectObjectTypeEnum, + ObjectDiffResultComparisonResultEnum, + SchemaObjectComparisonResultEnum +} from '@actiontech/shared/lib/api/sqle/service/common.enum'; +import { ISchemaObject } from '@actiontech/shared/lib/api/sqle/service/common'; +import { + filterSchemasInDatabase, + generateClassNamesByComparisonResult, + generateTreeDefaultExpandedKeys, + generateTreeNodeKey, + getComparisonResultByNodeKey, + getComparisonResultSchemaName, + getObjectTypeIcon, + parseTreeNodeKey, + renderComparisonResultObjectName +} from '../utils/TreeNode'; +import { TreeProps } from 'antd'; + +describe('TreeNode Utils', () => { + describe('getObjectTypeIcon', () => { + it('should return DatabaseTableFilled icon when objectType is TABLE', () => { + expect(getObjectTypeIcon(DatabaseDiffObjectObjectTypeEnum.TABLE)).toEqual( + + ); + }); + + it('should return DatabaseViewFilled icon when objectType is VIEW', () => { + expect(getObjectTypeIcon(DatabaseDiffObjectObjectTypeEnum.VIEW)).toEqual( + + ); + }); + + it('should return MenuSquareFilled icon when objectType is not TABLE or VIEW', () => { + expect( + getObjectTypeIcon('OTHER' as DatabaseDiffObjectObjectTypeEnum) + ).toEqual(); + }); + }); + + describe('renderComparisonResultObjectName', () => { + it('should render empty content for baseline when result is base not exist', () => { + const result = renderComparisonResultObjectName( + ObjectDiffResultComparisonResultEnum.base_not_exist, + 'test', + 'baseline' + ); + expect(result).toEqual(); + }); + + it('should render object name for baseline when result is comparison not exist', () => { + const result = renderComparisonResultObjectName( + ObjectDiffResultComparisonResultEnum.comparison_not_exist, + 'test', + 'baseline' + ); + expect(result).toEqual(test); + }); + + it('should render object name for baseline when result is same or inconsistent', () => { + const result = renderComparisonResultObjectName( + ObjectDiffResultComparisonResultEnum.same, + 'test', + 'baseline' + ); + expect(result).toEqual(test); + }); + + it('should render object name for comparison when result is same', () => { + const result = renderComparisonResultObjectName( + ObjectDiffResultComparisonResultEnum.same, + 'test', + 'comparison' + ); + expect(result).toEqual( + <> + + test + + + ); + }); + + it('should render object name for comparison when result is inconsistent', () => { + const result = renderComparisonResultObjectName( + ObjectDiffResultComparisonResultEnum.inconsistent, + 'test', + 'comparison' + ); + expect(result).toEqual( + <> + + test + + + ); + }); + + it('should render object name for comparison when result is base not exist', () => { + const result = renderComparisonResultObjectName( + ObjectDiffResultComparisonResultEnum.base_not_exist, + 'test', + 'comparison' + ); + expect(result).toEqual( + <> + + test + + + ); + }); + + it('should render object name for comparison when result is comparison not exist', () => { + const result = renderComparisonResultObjectName( + ObjectDiffResultComparisonResultEnum.comparison_not_exist, + 'test', + 'comparison' + ); + expect(result).toEqual( + <> + + + ); + }); + }); + + describe('generateTreeNodeKey', () => { + it('should join arguments with separator', () => { + expect(generateTreeNodeKey('1', '2', '3')).toBe( + '1_TREE_NODE_KEY_SEPARATOR_2_TREE_NODE_KEY_SEPARATOR_3' + ); + }); + }); + + describe('parseTreeNodeKey', () => { + const mockComparisonResults: ISchemaObject[] = [ + { + base_schema_name: 'schema1', + comparison_schema_name: 'schema2', + comparison_result: SchemaObjectComparisonResultEnum.inconsistent + } + ]; + + it('should parse schema level key', () => { + const result = parseTreeNodeKey('0', mockComparisonResults); + expect(result).toEqual({ + baselineSchemaName: 'schema1', + comparisonSchemaName: 'schema2' + }); + }); + + it('should parse object level key', () => { + const result = parseTreeNodeKey( + '0_TREE_NODE_KEY_SEPARATOR_table_TREE_NODE_KEY_SEPARATOR_test', + mockComparisonResults + ); + expect(result).toEqual({ + baselineSchemaName: 'schema1', + comparisonSchemaName: 'schema2', + objectType: 'table', + objectName: 'test' + }); + }); + }); + + describe('getComparisonResultByNodeKey', () => { + it('should return null when targetKey is invalid key', () => { + expect(getComparisonResultByNodeKey([], '')).toBeNull(); + expect(getComparisonResultByNodeKey([], 'z')).toBeNull(); + }); + + it('should return null when schema is not found', () => { + expect( + getComparisonResultByNodeKey([], '0_TREE_NODE_KEY_SEPARATOR_table_tb') + ).toBeNull(); + }); + + it('should return null when object type is not found', () => { + expect( + getComparisonResultByNodeKey( + [ + { + base_schema_name: 'schema', + database_diff_objects: [ + { object_type: DatabaseDiffObjectObjectTypeEnum.VIEW } + ] + } + ], + '0_TREE_NODE_KEY_SEPARATOR_table_tb' + ) + ).toBeNull(); + }); + + it('should return null when object name is not found', () => { + expect( + getComparisonResultByNodeKey( + [ + { + base_schema_name: 'schema', + database_diff_objects: [ + { object_type: DatabaseDiffObjectObjectTypeEnum.VIEW } + ] + }, + { + base_schema_name: 'schema', + database_diff_objects: [ + { + object_type: DatabaseDiffObjectObjectTypeEnum.TABLE, + objects_diff_result: [ + { + object_name: 'tb1', + comparison_result: + ObjectDiffResultComparisonResultEnum.inconsistent + } + ] + } + ] + } + ], + '1_TREE_NODE_KEY_SEPARATOR_TABLE_TREE_NODE_KEY_SEPARATOR_tb' + ) + ).toBeNull(); + }); + + it('should return comparison result', () => { + expect( + getComparisonResultByNodeKey( + [ + { + base_schema_name: 'schema', + database_diff_objects: [ + { object_type: DatabaseDiffObjectObjectTypeEnum.VIEW } + ] + }, + { + base_schema_name: 'schema', + database_diff_objects: [ + { + object_type: DatabaseDiffObjectObjectTypeEnum.TABLE, + objects_diff_result: [ + { + object_name: 'tb', + comparison_result: + ObjectDiffResultComparisonResultEnum.inconsistent + } + ] + } + ] + } + ], + '1_TREE_NODE_KEY_SEPARATOR_TABLE_TREE_NODE_KEY_SEPARATOR_tb' + ) + ).toBe(ObjectDiffResultComparisonResultEnum.inconsistent); + }); + }); + + describe('getComparisonResultSchemaName', () => { + it('should get correct schema name', () => { + expect( + getComparisonResultSchemaName( + { + comparison_result: SchemaObjectComparisonResultEnum.base_not_exist, + comparison_schema_name: 'schema' + }, + 'baseline' + ) + ).toBe('schema'); + + expect( + getComparisonResultSchemaName( + { + comparison_result: + SchemaObjectComparisonResultEnum.comparison_not_exist, + base_schema_name: 'schema' + }, + 'baseline' + ) + ).toBe('schema'); + + expect( + getComparisonResultSchemaName( + { + comparison_result: SchemaObjectComparisonResultEnum.same, + base_schema_name: 'schema' + }, + 'baseline' + ) + ).toBe('schema'); + + expect( + getComparisonResultSchemaName( + { + comparison_result: SchemaObjectComparisonResultEnum.same, + comparison_schema_name: 'schema' + }, + 'comparison' + ) + ).toBe('schema'); + }); + }); + + describe('generateClassNamesByComparisonResult', () => { + it('should return diff class when result is not same', () => { + expect( + generateClassNamesByComparisonResult( + ObjectDiffResultComparisonResultEnum.inconsistent + ) + ).toBe('object-comparison-result-diff'); + }); + + it('should return pass class when result is same', () => { + expect( + generateClassNamesByComparisonResult( + ObjectDiffResultComparisonResultEnum.same + ) + ).toBe('object-comparison-result-pass'); + }); + }); + + describe('filterSchemasInDatabase', () => { + const mockComparisonResults: ISchemaObject[] = [ + { + base_schema_name: 'schema1', + comparison_schema_name: 'schema1', + comparison_result: SchemaObjectComparisonResultEnum.same + } + ]; + + it('should filter and return unique schema names', () => { + const result = filterSchemasInDatabase( + ['0_TREE_NODE_KEY_SEPARATOR_table_TREE_NODE_KEY_SEPARATOR_test'], + mockComparisonResults + ); + expect(result).toEqual(['schema1']); + }); + }); + + describe('generateTreeDefaultExpandedKeys', () => { + it('should generate expanded keys for tree', () => { + const mockTreeData: TreeProps['treeData'] = [ + { + key: 'parent', + children: [ + { + key: 'node1', + children: [{ key: 'child1' }] + } + ] + } + ]; + + const result = generateTreeDefaultExpandedKeys(mockTreeData); + expect(result).toEqual(['TREE_PARENT_NODE_KEY', 'node1', 'child1']); + }); + + it('should return empty array when tree data is empty', () => { + expect(generateTreeDefaultExpandedKeys([])).toEqual([]); + }); + }); +}); diff --git a/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/ComparisonTreeNode.test.tsx.snap b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/ComparisonTreeNode.test.tsx.snap new file mode 100644 index 000000000..67455ff52 --- /dev/null +++ b/packages/sqle/src/page/DataSourceComparison/ComparisonEntry/__tests__/__snapshots__/ComparisonTreeNode.test.tsx.snap @@ -0,0 +1,1545 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ComparisonTreeNode ComparisonDetailDrawer should render the drawer when open is true 1`] = ` + +
+
+
+
+
+