From 01b5692c233563f122681509df170ee09aae5efa Mon Sep 17 00:00:00 2001 From: CynthiaKamau Date: Tue, 22 Oct 2024 15:28:20 +0300 Subject: [PATCH] (tests) O3-4097 Add tests for rendering a dummy schema --- jest.config.js | 40 +++++--------- package.json | 3 +- .../add-columns-modal.component.test.tsx | 2 +- .../add-package-modal.component.test.tsx | 2 +- .../add-submenu-modal.component.test.tsx | 2 +- ...nfigure-dashboard-modal.component.test.tsx | 2 +- ...ete-config-detail-modal.component.test.tsx | 2 +- .../edit-tab-definition-modal.test.tsx | 14 ++--- .../interactive-builder.component.test.tsx | 2 +- .../view-editor.component.test.tsx | 54 +++++++++++++++++++ .../view-editor/view-editor.component.tsx | 8 +-- src/setup-tests.ts | 8 +++ src/test-helpers.tsx | 30 +++++++++++ webpack.config.js | 11 +++- yarn.lock | 28 ++++++++-- 15 files changed, 155 insertions(+), 53 deletions(-) create mode 100644 src/components/view-editor/view-editor.component.test.tsx create mode 100644 src/test-helpers.tsx diff --git a/jest.config.js b/jest.config.js index 6b7f3be..ebfd53d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,34 +1,22 @@ -/** - * @returns {Promise} - */ -const path = require('path'); - +/** @type {import('jest').Config} */ module.exports = { - collectCoverageFrom: [ - '**/src/**/*.component.tsx', - '!**/node_modules/**', - '!**/vendor/**', - '!**/src/**/*.test.*', - '!**/src/declarations.d.ts', - '!**/e2e/**', - ], + clearMocks: true, transform: { '^.+\\.tsx?$': ['@swc/jest'], }, transformIgnorePatterns: ['/node_modules/(?!@openmrs)'], -moduleNameMapper: { - '@openmrs/esm-framework': '@openmrs/esm-framework/mock', - '@openmrs/esm-utils': '@openmrs/esm-framework/mock', - '\\.(s?css)$': 'identity-obj-proxy', - '^lodash-es/(.*)$': 'lodash/$1', - 'lodash-es': 'lodash', - '^dexie$': require.resolve('dexie'), - '^@testing-library/jest-dom/extend-expect$': '@testing-library/jest-dom', -}, + moduleNameMapper: { + '^@carbon/icons-react/es/(.*)$': '@carbon/icons-react/lib/$1', + '^carbon-components-react/es/(.*)$': 'carbon-components-react/lib/$1', + '@openmrs/esm-framework': '@openmrs/esm-framework/mock', + '\\.(s?css)$': 'identity-obj-proxy', + '^lodash-es/(.*)$': 'lodash/$1', + 'lodash-es': 'lodash', + '^dexie$': '/node_modules/dexie', + '^react-i18next$': '/__mocks__/react-i18next.js', + 'ace-builds': '/node_modules/ace-builds', + }, setupFilesAfterEnv: ['/src/setup-tests.ts'], - testPathIgnorePatterns: [path.resolve(__dirname, 'e2e')], testEnvironment: 'jsdom', - testEnvironmentOptions: { - url: 'http://localhost/', - }, + testPathIgnorePatterns: ['/e2e'], }; diff --git a/package.json b/package.json index 9489c2c..9f3052f 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "dotenv": "^16.4.5", "file-loader": "^6.2.0", "fuzzy": "^0.1.3", + "globy": "^0.1.8", "lodash-es": "^4.17.21", "react-ace": "^11.0.1", "sass": "^1.67.0", @@ -75,7 +76,7 @@ "@swc/core": "^1.5.7", "@swc/jest": "^0.2.36", "@testing-library/dom": "^9.3.4", - "@testing-library/jest-dom": "^6.6.1", + "@testing-library/jest-dom": "^6.6.2", "@testing-library/react": "^14.3.1", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", diff --git a/src/components/interactive-builder/add-columns-modal.component.test.tsx b/src/components/interactive-builder/add-columns-modal.component.test.tsx index fef61c3..70de4ea 100644 --- a/src/components/interactive-builder/add-columns-modal.component.test.tsx +++ b/src/components/interactive-builder/add-columns-modal.component.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom'; import userEvent from '@testing-library/user-event'; import { useTranslation } from 'react-i18next'; import ConfigureDashboardModal from './add-columns-modal.component'; diff --git a/src/components/interactive-builder/add-package-modal.component.test.tsx b/src/components/interactive-builder/add-package-modal.component.test.tsx index d013aed..d66efc8 100644 --- a/src/components/interactive-builder/add-package-modal.component.test.tsx +++ b/src/components/interactive-builder/add-package-modal.component.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom'; import { useTranslation } from 'react-i18next'; import { showSnackbar } from '@openmrs/esm-framework'; import PackageModal from './add-package-modal.component'; diff --git a/src/components/interactive-builder/add-submenu-modal.component.test.tsx b/src/components/interactive-builder/add-submenu-modal.component.test.tsx index 173ed4d..fb7681f 100644 --- a/src/components/interactive-builder/add-submenu-modal.component.test.tsx +++ b/src/components/interactive-builder/add-submenu-modal.component.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom'; import { useTranslation } from 'react-i18next'; import { showSnackbar } from '@openmrs/esm-framework'; import NewSubMenuModal from './add-submenu-modal.component'; diff --git a/src/components/interactive-builder/configure-dashboard-modal.component.test.tsx b/src/components/interactive-builder/configure-dashboard-modal.component.test.tsx index 1d074d6..7a94f92 100644 --- a/src/components/interactive-builder/configure-dashboard-modal.component.test.tsx +++ b/src/components/interactive-builder/configure-dashboard-modal.component.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom'; import userEvent from '@testing-library/user-event'; import ConfigureDashboardModal from './configure-dashboard-modal.component'; import { showSnackbar } from '@openmrs/esm-framework'; diff --git a/src/components/interactive-builder/delete-config-detail-modal.component.test.tsx b/src/components/interactive-builder/delete-config-detail-modal.component.test.tsx index 8d05af4..54335d9 100644 --- a/src/components/interactive-builder/delete-config-detail-modal.component.test.tsx +++ b/src/components/interactive-builder/delete-config-detail-modal.component.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom'; import DeleteConfigDetailModal from './delete-config-detail-modal.component'; import { WidgetTypes } from '../../types'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/interactive-builder/edit-tab-definition-modal.test.tsx b/src/components/interactive-builder/edit-tab-definition-modal.test.tsx index 2ca112b..202bf31 100644 --- a/src/components/interactive-builder/edit-tab-definition-modal.test.tsx +++ b/src/components/interactive-builder/edit-tab-definition-modal.test.tsx @@ -1,15 +1,9 @@ import React from 'react'; import { render } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; -import { I18nextProvider } from 'react-i18next'; -import i18n from 'i18next'; +import '@testing-library/jest-dom'; import EditTabDefinitionModal from './edit-tab-definition-modal'; import userEvent from '@testing-library/user-event'; -const renderWithI18n = (ui: React.ReactElement) => { - return render({ui}); -}; - describe('EditTabDefinitionModal', () => { const mockOnSave = jest.fn(); const mockOnCancel = jest.fn(); @@ -20,7 +14,7 @@ describe('EditTabDefinitionModal', () => { }); it('renders with initial values', () => { - const { getByLabelText } = renderWithI18n( + const { getByLabelText } = render( , ); @@ -30,7 +24,7 @@ describe('EditTabDefinitionModal', () => { it('calls onSave with updated values', async () => { const user = userEvent.setup(); - const { getByLabelText, getByText } = renderWithI18n( + const { getByLabelText, getByText } = render( , ); @@ -51,7 +45,7 @@ describe('EditTabDefinitionModal', () => { it('calls onCancel when cancel button is clicked', async () => { const user = userEvent.setup(); - const { getByText } = renderWithI18n( + const { getByText } = render( , ); diff --git a/src/components/interactive-builder/interactive-builder.component.test.tsx b/src/components/interactive-builder/interactive-builder.component.test.tsx index f1f1821..73b95fd 100644 --- a/src/components/interactive-builder/interactive-builder.component.test.tsx +++ b/src/components/interactive-builder/interactive-builder.component.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom'; import { showModal, showSnackbar } from '@openmrs/esm-framework'; import InteractiveBuilder from './interactive-builder.component'; import { v4 as uuidv4 } from 'uuid'; diff --git a/src/components/view-editor/view-editor.component.test.tsx b/src/components/view-editor/view-editor.component.test.tsx new file mode 100644 index 0000000..07a4e23 --- /dev/null +++ b/src/components/view-editor/view-editor.component.test.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import { ContentPackagesEditorContent } from './view-editor.component'; +import userEvent from '@testing-library/user-event'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key) => key, + }), +})); + +beforeEach(() => { + localStorage.clear(); +}); + +describe('ContentPackagesEditorContent', () => { + it('renders correctly and allows inputting dummy schema', async () => { + render( + + key} /> + , + ); + + expect(screen.getByText('schemaEditor')).toBeInTheDocument(); + const importSchemaButtons = screen.queryAllByText(/importSchema/i); + expect(importSchemaButtons.length).toBeGreaterThan(0); + expect(screen.getByText(/inputDummySchema/i)).toBeInTheDocument(); + + await userEvent.click(screen.getByText(/inputDummySchema/i)); + + expect(screen.getByText(/interactiveBuilderInfo/i)).toBeInTheDocument(); + expect(screen.getByText(/Package One/i)).toBeInTheDocument(); + expect(screen.getByText(/clinicalViewMenus/i)).toBeInTheDocument(); + expect(screen.getByText(/First Menu/i)).toBeInTheDocument(); + await userEvent.click(screen.getByRole('button', { name: /First Menu/i })); + expect(screen.getByText(/menuSlot\s*:\s*first-menu-slot/i)).toBeInTheDocument(); + const firstHelperText = screen.getAllByText(/helperTextForAddDasboards/i)[0]; + expect(firstHelperText).toBeInTheDocument(); + + const firstConfigureButton = screen.getAllByRole('button', { name: /configureDashboard/i })[0]; + expect(firstConfigureButton).toBeInTheDocument(); + expect(screen.getByText(/Second Menu/i)).toBeInTheDocument(); + + await userEvent.click(screen.getByRole('button', { name: /Second Menu/i })); + expect(screen.getByText(/menuSlot\s*:\s*second-menu-slot/i)).toBeInTheDocument(); + const secondHelperText = screen.getAllByText(/helperTextForAddDasboards/i)[1]; + expect(secondHelperText).toBeInTheDocument(); + + const secondConfigureButton = screen.getAllByRole('button', { name: /configureDashboard/i })[1]; + expect(secondConfigureButton).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /addClinicalViewSubMenu/i })).toBeInTheDocument(); + }); +}); diff --git a/src/components/view-editor/view-editor.component.tsx b/src/components/view-editor/view-editor.component.tsx index b6b7627..0c22fc9 100644 --- a/src/components/view-editor/view-editor.component.tsx +++ b/src/components/view-editor/view-editor.component.tsx @@ -17,7 +17,7 @@ interface TranslationFnProps { t: TFunction; } -const ContentPackagesEditorContent: React.FC = ({ t }) => { +export const ContentPackagesEditorContent: React.FC = ({ t }) => { const [isMaximized, setIsMaximized] = useState(false); const [schema, setSchema] = useState(); @@ -295,7 +295,7 @@ const ContentPackagesEditorContent: React.FC = ({ t }) => { ); }; -function BackButton({ t }: TranslationFnProps) { +export function BackButton({ t }: TranslationFnProps) { return (
@@ -311,7 +311,7 @@ function BackButton({ t }: TranslationFnProps) { ); } -function ClinicalViewsEditor() { +const ClinicalViewsEditor: React.FC = () => { const { t } = useTranslation(); return ( @@ -321,6 +321,6 @@ function ClinicalViewsEditor() { ); -} +}; export default ClinicalViewsEditor; diff --git a/src/setup-tests.ts b/src/setup-tests.ts index 7b0828b..f326fdc 100644 --- a/src/setup-tests.ts +++ b/src/setup-tests.ts @@ -1 +1,9 @@ import '@testing-library/jest-dom'; + +window.URL.createObjectURL = jest.fn(); +window.openmrsBase = '/openmrs'; +window.spaBase = '/spa'; +window.getOpenmrsSpaBase = () => '/openmrs/spa/'; + +// https://github.com/jsdom/jsdom/issues/1695 +window.HTMLElement.prototype.scrollIntoView = function () {}; diff --git a/src/test-helpers.tsx b/src/test-helpers.tsx new file mode 100644 index 0000000..5254479 --- /dev/null +++ b/src/test-helpers.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { SWRConfig } from 'swr'; +import { type RenderOptions, render, screen, waitForElementToBeRemoved } from '@testing-library/react'; + +// This component wraps whatever component is passed to it with an SWRConfig context which provides a global configuration for all SWR hooks. +const swrWrapper = ({ children }: { children: React.ReactNode }) => { + return ( + new Map(), + }} + > + {children} + + ); +}; + +// Render the provided component within the wrapper we created above +export const renderWithSwr = (ui: React.ReactElement, options?: RenderOptions) => + render(ui, { wrapper: swrWrapper, ...options }); + +// Helper function that waits for a loading state to disappear from the screen +export function waitForLoadingToFinish() { + return waitForElementToBeRemoved(() => [...screen.queryAllByRole('progressbar')], { + timeout: 4000, + }); +} diff --git a/webpack.config.js b/webpack.config.js index 2c74029..d34f545 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1 +1,10 @@ -module.exports = require('openmrs/default-webpack-config'); +const config = (module.exports = require('openmrs/default-webpack-config')); +config.scriptRuleConfig.exclude = /(node_modules(?![/\\]@(?:openmrs|ohri)))/; +config.overrides.resolve = { + extensions: ['.tsx', '.ts', '.jsx', '.js', '.scss', '.json'], + alias: { + '@openmrs/esm-framework': '@openmrs/esm-framework/src/internal', + '@openmrs/esm-form-engine-lib': '@openmrs/esm-form-engine-lib/src/index', + }, +}; +module.exports = config; diff --git a/yarn.lock b/yarn.lock index cb8cbbc..da5de3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3127,7 +3127,7 @@ __metadata: "@swc/core": "npm:^1.5.7" "@swc/jest": "npm:^0.2.36" "@testing-library/dom": "npm:^9.3.4" - "@testing-library/jest-dom": "npm:^6.6.1" + "@testing-library/jest-dom": "npm:^6.6.2" "@testing-library/react": "npm:^14.3.1" "@testing-library/user-event": "npm:^14.5.2" "@types/jest": "npm:^29.5.12" @@ -3148,6 +3148,7 @@ __metadata: eslint-plugin-testing-library: "npm:^6.2.2" file-loader: "npm:^6.2.0" fuzzy: "npm:^0.1.3" + globy: "npm:^0.1.8" husky: "npm:^8.0.3" i18next: "npm:^23.11.4" i18next-parser: "npm:^9.0.2" @@ -5722,9 +5723,9 @@ __metadata: languageName: node linkType: hard -"@testing-library/jest-dom@npm:^6.6.1": - version: 6.6.1 - resolution: "@testing-library/jest-dom@npm:6.6.1" +"@testing-library/jest-dom@npm:^6.6.2": + version: 6.6.2 + resolution: "@testing-library/jest-dom@npm:6.6.2" dependencies: "@adobe/css-tools": "npm:^4.4.0" aria-query: "npm:^5.0.0" @@ -5733,7 +5734,7 @@ __metadata: dom-accessibility-api: "npm:^0.6.3" lodash: "npm:^4.17.21" redent: "npm:^3.0.0" - checksum: 10/006357d7547cc50624564a1b0e9986d0f0252365a6e5ac1c7c989032159c47226adfbd9a9ac17eef236738e9597541d344176176b5c3ed0e06e8f4b33387e135 + checksum: 10/c5f1ac369e685ea7c17eff190f2e9996e6e54615a3048c3c00cbbbec48f94c557d348ba935a7e8170efbbb109c035785952f6e46d0c03edb18e9a8cc55f8f118 languageName: node linkType: hard @@ -11489,6 +11490,16 @@ __metadata: languageName: node linkType: hard +"globy@npm:^0.1.8": + version: 0.1.8 + resolution: "globy@npm:0.1.8" + dependencies: + nan: "npm:^1.2.0" + node-gyp: "npm:latest" + checksum: 10/83c3217aa066ad9d9299413b575f6400534271f4a750aa6365cb5cda122757a70fbe5883fa65c337adc14937e4d541142f8b64d077cc35b3608e215a2d6758e6 + languageName: node + linkType: hard + "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -14392,6 +14403,13 @@ __metadata: languageName: node linkType: hard +"nan@npm:^1.2.0": + version: 1.9.0 + resolution: "nan@npm:1.9.0" + checksum: 10/2a52437a19e6d63da98264011352719cfcec139d41f4d59f6f1a6ecb19110f9eac6527c51fa9186c9d61ec608572be5107b5a84ccf0ba166f4289e5763ab70a3 + languageName: node + linkType: hard + "nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7"