From d3390853a140a9a0b7d3e2345937b9611b6afe2a Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Wed, 22 Jan 2025 12:19:59 +0100 Subject: [PATCH 1/3] Add link to Foreman Resource Quota docs Link from Resource Quota welcome page to Foreman docs on limiting host resources: https://docs.theforeman.org/nightly/Administering_Project/index-katello.html#limiting-host-resources Co-authored-by: Maximilian Kolb --- .../ResourceQuotaEmptyState/index.js | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/webpack/components/ResourceQuotaEmptyState/index.js b/webpack/components/ResourceQuotaEmptyState/index.js index 85c6aff..d70206f 100644 --- a/webpack/components/ResourceQuotaEmptyState/index.js +++ b/webpack/components/ResourceQuotaEmptyState/index.js @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { Button, Modal, ModalVariant } from '@patternfly/react-core'; import { translate as __ } from 'foremanReact/common/I18n'; +import { getDocsURL } from 'foremanReact/common/helpers'; import EmptyStatePattern from 'foremanReact/components/common/EmptyState/EmptyStatePattern'; import ResourceQuotaForm from '../ResourceQuotaForm'; @@ -28,16 +29,32 @@ const ResourceQuotaEmptyState = () => { {__('Create resource quota')} ); + + const description = ( + + {__( + 'Resource Quotas help admins to manage resources including CPUs, memory, and disk space among users or user groups.' + )} +
+ {__( + 'Define a Resource Quota here and apply it to users to guarantee a fair share of your resources.' + )} +
+
+ ); + const documentation = { + url: getDocsURL('Administering_Project', 'limiting-host-resources'), + }; + return (
Date: Wed, 22 Jan 2025 16:30:43 +0100 Subject: [PATCH 2/3] Add JS dependency for testing * Add dependency @testing-library/user-event to simulate user click events during tests * Update package-lock.json with new dependency --- package-lock.json | 14 ++++++++++++++ package.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 00310e9..0b098e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@babel/core": "^7.23.0", "@sheerun/mutationobserver-shim": "^0.3.3", "@testing-library/react": "^16.2.0", + "@testing-library/user-event": "^14.6.1", "@theforeman/builder": ">= 12.0.1", "@theforeman/eslint-plugin-foreman": ">= 12.0.1", "@theforeman/find-foreman": ">= 12.0.1", @@ -4219,6 +4220,19 @@ "react-test-renderer": ">=16.9.0" } }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@theforeman/builder": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/@theforeman/builder/-/builder-14.0.0.tgz", diff --git a/package.json b/package.json index 9276f44..5aa623e 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,11 @@ "peerDependencies": { "@theforeman/vendor": ">= 12.0.1" }, - "dependencies": {}, "devDependencies": { "@babel/core": "^7.23.0", "@sheerun/mutationobserver-shim": "^0.3.3", "@testing-library/react": "^16.2.0", + "@testing-library/user-event": "^14.6.1", "@theforeman/builder": ">= 12.0.1", "@theforeman/eslint-plugin-foreman": ">= 12.0.1", "@theforeman/find-foreman": ">= 12.0.1", From 2c7fb3f1ce8f46a083b180b2de3ede30e7e71b59 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Wed, 22 Jan 2025 16:31:36 +0100 Subject: [PATCH 3/3] Add tests for ResourceQuotaEmptyState component * Add snapshot test * Add functional test for Modal opening mechanism * Add test_helper (cc foreman_ansible) for mocking React/Redux contexts. --- .../__test__/ResourceQuotaEmptyState.test.js | 35 ++++++++ .../ResourceQuotaEmptyState.test.js.snap | 80 +++++++++++++++++++ webpack/test_helper.js | 49 ++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 webpack/components/ResourceQuotaEmptyState/__test__/ResourceQuotaEmptyState.test.js create mode 100644 webpack/components/ResourceQuotaEmptyState/__test__/__snapshots__/ResourceQuotaEmptyState.test.js.snap create mode 100644 webpack/test_helper.js diff --git a/webpack/components/ResourceQuotaEmptyState/__test__/ResourceQuotaEmptyState.test.js b/webpack/components/ResourceQuotaEmptyState/__test__/ResourceQuotaEmptyState.test.js new file mode 100644 index 0000000..743840f --- /dev/null +++ b/webpack/components/ResourceQuotaEmptyState/__test__/ResourceQuotaEmptyState.test.js @@ -0,0 +1,35 @@ +import React from 'react'; +import '@testing-library/jest-dom'; + +import { mount, testComponentSnapshotsWithFixtures } from '@theforeman/test'; +// Notice: (not) importing Modal affects the snapshot test since it fills +// the components data dynamically in snapshots as soon as it can find the component. +import { Modal } from '@patternfly/react-core'; + +import { withMockedProvider, withRedux } from '../../../test_helper'; +import ResourceQuotaForm from '../../ResourceQuotaForm'; +import ResourceQuotaEmptyState from '../index'; + +const TestComponent = withRedux(withMockedProvider(ResourceQuotaEmptyState)); + +describe('ResourceQuotaEmptyState', () => { + testComponentSnapshotsWithFixtures(ResourceQuotaEmptyState, { + 'should render': {}, // component has no props + }); + + test('opens the modal on clicking "Create resource quota" button', () => { + const wrapper = mount(); + + expect(wrapper.find(Modal).prop('isOpen')).toBe(false); // check we provide the correct input to Modal + expect(wrapper.find(ResourceQuotaForm).exists()).toBe(false); + + wrapper + .find('button') + .filterWhere(button => button.text() === 'Create resource quota') + .simulate('click'); + wrapper.update(); + + expect(wrapper.find(Modal).prop('isOpen')).toBe(true); + expect(wrapper.find(ResourceQuotaForm).exists()).toBe(true); + }); +}); diff --git a/webpack/components/ResourceQuotaEmptyState/__test__/__snapshots__/ResourceQuotaEmptyState.test.js.snap b/webpack/components/ResourceQuotaEmptyState/__test__/__snapshots__/ResourceQuotaEmptyState.test.js.snap new file mode 100644 index 0000000..169b829 --- /dev/null +++ b/webpack/components/ResourceQuotaEmptyState/__test__/__snapshots__/ResourceQuotaEmptyState.test.js.snap @@ -0,0 +1,80 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ResourceQuotaEmptyState should render 1`] = ` +
+ + Create resource quota + + } + description={ + + Resource Quotas help admins to manage resources including CPUs, memory, and disk space among users or user groups. +
+ Define a Resource Quota here and apply it to users to guarantee a fair share of your resources. +
+
+ } + documentation={ + Object { + "url": "/links/docs/Administering_Project?chapter=limiting-host-resources", + } + } + header="Resource Quotas" + icon="pficon pficon-cluster" + iconType="pf" + secondaryActions={Array []} + /> + } + aria-describedby="" + aria-label="" + aria-labelledby="" + className="" + hasNoBodyWrapper={false} + isOpen={false} + onClose={[Function]} + ouiaId="foreman-resource-quota-create-modal" + ouiaSafe={true} + showClose={true} + title="Create resource quota" + titleIconVariant={null} + titleLabel="" + variant="small" + > + + +
+`; diff --git a/webpack/test_helper.js b/webpack/test_helper.js new file mode 100644 index 0000000..82d3d62 --- /dev/null +++ b/webpack/test_helper.js @@ -0,0 +1,49 @@ +/* Credits: https://github.com/theforeman/foreman_ansible/blob/master/webpack/testHelper.js */ +import React, { useState } from 'react'; +import { applyMiddleware, createStore, compose, combineReducers } from 'redux'; +import { reducers as apiReducer, APIMiddleware } from 'foremanReact/redux/API'; +import { Provider } from 'react-redux'; +import { MockedProvider } from '@apollo/react-testing'; +import thunk from 'redux-thunk'; + +import ConfirmModal, { + reducers as confirmModalReducers, +} from 'foremanReact/components/ConfirmModal'; +import { getForemanContext } from 'foremanReact/Root/Context/ForemanContext'; + +const reducers = combineReducers({ ...apiReducer, ...confirmModalReducers }); +export const generateStore = () => + createStore(reducers, compose(applyMiddleware(thunk, APIMiddleware))); + +// use to resolve async mock requests for apollo MockedProvider +export const tick = () => new Promise(resolve => setTimeout(resolve, 0)); + +export const withRedux = Component => props => ( + + + + +); + +export const withMockedProvider = Component => props => { + const [context, setContext] = useState({ + metadata: { + UISettings: { + perPage: 20, + }, + }, + }); + const contextData = { context, setContext }; + const ForemanContext = getForemanContext(contextData); + + // eslint-disable-next-line react/prop-types + const { mocks, ...rest } = props; + + return ( + + + + + + ); +};