diff --git a/ui/package-lock.json b/ui/package-lock.json index 2874c5896a..f464e679f8 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -12,7 +12,7 @@ "@hookform/resolvers": "^3.1.0", "@js-temporal/polyfill": "^0.4.4", "@kubernetes/client-node": "github:scality/kubernetes-client-javascript.git#browser-0.10.4-64-ge7c6721", - "@scality/core-ui": "git+https://github.com/scality/core-ui#62aef0b544c50e47c5a8ceb3697fe090eb3c6a9f", + "@scality/core-ui": "git+https://github.com/scality/core-ui#95efcf1ca77e07e43401ad119bbb15ef3b3ffcd7", "@scality/module-federation": "git+https://github.com/scality/module-federation#c571388783a2a51ae3bf5d36ae66753c8b014bb5", "axios": "^0.21.1", "formik": "2.2.5", @@ -5545,8 +5545,8 @@ }, "node_modules/@scality/core-ui": { "version": "0.151.0", - "resolved": "git+ssh://git@github.com/scality/core-ui.git#62aef0b544c50e47c5a8ceb3697fe090eb3c6a9f", - "integrity": "sha512-J1k8BtcEPGbzyFexYXkDdS/6sk1jBY+gN/QeljvuloMRr29OlVqTaoUtOgr6C8i0OvcanNx2OiWcWfw8za2Q7Q==", + "resolved": "git+ssh://git@github.com/scality/core-ui.git#95efcf1ca77e07e43401ad119bbb15ef3b3ffcd7", + "integrity": "sha512-4zVZDu/Z7D1s29+S+EqwUzc3gyWj77JNKb9qQqIf5VFIt9Si4YzuGKX1SEQVa5lCKAtvttnTT15+eg+2PF+Nyw==", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@floating-ui/dom": "^1.6.3", @@ -32226,9 +32226,9 @@ } }, "@scality/core-ui": { - "version": "git+ssh://git@github.com/scality/core-ui.git#62aef0b544c50e47c5a8ceb3697fe090eb3c6a9f", - "integrity": "sha512-J1k8BtcEPGbzyFexYXkDdS/6sk1jBY+gN/QeljvuloMRr29OlVqTaoUtOgr6C8i0OvcanNx2OiWcWfw8za2Q7Q==", - "from": "@scality/core-ui@git+https://github.com/scality/core-ui#62aef0b544c50e47c5a8ceb3697fe090eb3c6a9f", + "version": "git+ssh://git@github.com/scality/core-ui.git#95efcf1ca77e07e43401ad119bbb15ef3b3ffcd7", + "integrity": "sha512-4zVZDu/Z7D1s29+S+EqwUzc3gyWj77JNKb9qQqIf5VFIt9Si4YzuGKX1SEQVa5lCKAtvttnTT15+eg+2PF+Nyw==", + "from": "@scality/core-ui@git+https://github.com/scality/core-ui#95efcf1ca77e07e43401ad119bbb15ef3b3ffcd7", "requires": { "@floating-ui/dom": "^1.6.3", "@fortawesome/fontawesome-free": "^5.10.2", diff --git a/ui/package.json b/ui/package.json index 556d13e533..97d573454d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -7,7 +7,7 @@ "@hookform/resolvers": "^3.1.0", "@js-temporal/polyfill": "^0.4.4", "@kubernetes/client-node": "github:scality/kubernetes-client-javascript.git#browser-0.10.4-64-ge7c6721", - "@scality/core-ui": "git+https://github.com/scality/core-ui#62aef0b544c50e47c5a8ceb3697fe090eb3c6a9f", + "@scality/core-ui": "git+https://github.com/scality/core-ui#95efcf1ca77e07e43401ad119bbb15ef3b3ffcd7", "@scality/module-federation": "git+https://github.com/scality/module-federation#c571388783a2a51ae3bf5d36ae66753c8b014bb5", "axios": "^0.21.1", "formik": "2.2.5", diff --git a/ui/src/alert-configuration/ConfigureAlerting.test.tsx b/ui/src/alert-configuration/ConfigureAlerting.test.tsx index 9a212018e0..cd7c958633 100644 --- a/ui/src/alert-configuration/ConfigureAlerting.test.tsx +++ b/ui/src/alert-configuration/ConfigureAlerting.test.tsx @@ -165,12 +165,8 @@ const commonSetup = async () => { <> - -
Redirected Alert Page
-
- - - + Redirected Alert Page} /> + } />
, ); @@ -259,6 +255,13 @@ describe('', () => { await act(async () => { await userEvent.click(selectors.authSelect.authClick()); + }); + + await waitFor(() => { + expect(selectors.authSelect.optionCramMd5()).toBeInTheDocument(); + }); + + await act(async () => { await userEvent.click(selectors.authSelect.optionCramMd5()); }); @@ -268,6 +271,13 @@ describe('', () => { await act(async () => { await userEvent.click(selectors.authSelect.authClick()); + }); + + await waitFor(() => { + expect(selectors.authSelect.optionPlain()).toBeInTheDocument(); + }); + + await act(async () => { await userEvent.click(selectors.authSelect.optionPlain()); }); @@ -355,8 +365,18 @@ describe('', () => { it('show errors on submit with not all required field', async () => { await commonSetup(); + await waitFor(() => { + expect(selectors.enableConfiguration()).toBeInTheDocument(); + }); await act(async () => { await userEvent.click(selectors.enableConfiguration()); + }); + + await waitFor(() => { + expect(selectors.saveButton()).toBeInTheDocument(); + }); + + await act(async () => { await userEvent.click(selectors.saveButton()); }); @@ -403,7 +423,13 @@ describe('', () => { selectors.recipient(), 'user1@test.com, user2@test.com', ); + }); + await waitFor(() => { + expect(selectors.saveButton()).toBeInTheDocument(); + }); + + await act(async () => { await userEvent.click(selectors.saveButton()); }); @@ -460,19 +486,38 @@ spec: ); await userEvent.clear(selectors.port()); await userEvent.type(selectors.port(), '22'); + }); + await waitFor(() => { + expect(selectors.authSelect.authClick()).toBeInTheDocument(); + }); + + await act(async () => { await userEvent.click(selectors.authSelect.authClick()); + }); + + await waitFor(() => { + expect(selectors.authSelect.optionNoAuth()).toBeInTheDocument(); + }); + + await act(async () => { await userEvent.click(selectors.authSelect.optionNoAuth()); + }); - await userEvent.clear(selectors.sender()); - await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); + await userEvent.clear(selectors.sender()); + await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); - await userEvent.clear(selectors.recipient()); - await userEvent.type( - selectors.recipient(), - 'user1@test.com, user2@test.com', - ); + await userEvent.clear(selectors.recipient()); + await userEvent.type( + selectors.recipient(), + 'user1@test.com, user2@test.com', + ); + + await waitFor(() => { + expect(selectors.saveButton()).toBeInTheDocument(); + }); + await act(async () => { await userEvent.click(selectors.saveButton()); }); @@ -542,7 +587,13 @@ spec: selectors.recipient(), 'user1@test.com, user2@test.com', ); + }); + await waitFor(() => { + expect(selectors.saveButton()).toBeInTheDocument(); + }); + + await act(async () => { await userEvent.click(selectors.saveButton()); }); @@ -602,21 +653,21 @@ spec: 'smtp4dev.default.svc.cluster.local', ); await userEvent.type(selectors.port(), '42'); + }); - await userEvent.click(selectors.authSelect.authClick()); - await userEvent.click(selectors.authSelect.optionLogin()); + await userEvent.click(selectors.authSelect.authClick()); + await userEvent.click(selectors.authSelect.optionLogin()); - await userEvent.type(selectors.username(), 'Renard'); - await userEvent.type(selectors.password(), 'Renard Password'); + await userEvent.type(selectors.username(), 'Renard'); + await userEvent.type(selectors.password(), 'Renard Password'); - await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); - await userEvent.type( - selectors.recipient(), - 'user1@test.com, user2@test.com', - ); + await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); + await userEvent.type( + selectors.recipient(), + 'user1@test.com, user2@test.com', + ); - await userEvent.click(selectors.saveButton()); - }); + await userEvent.click(selectors.saveButton()); await waitForElementToBeRemoved(() => { return screen.getByText(/Saving\.\.\./); @@ -670,21 +721,21 @@ spec: 'smtp4dev.default.svc.cluster.local', ); await userEvent.type(selectors.port(), '42'); + }); - await userEvent.click(selectors.authSelect.authClick()); - await userEvent.click(selectors.authSelect.optionCramMd5()); + await userEvent.click(selectors.authSelect.authClick()); + await userEvent.click(selectors.authSelect.optionCramMd5()); - await userEvent.type(selectors.username(), 'Renard'); - await userEvent.type(selectors.secret(), 'xxxyyyzzz-secret'); + await userEvent.type(selectors.username(), 'Renard'); + await userEvent.type(selectors.secret(), 'xxxyyyzzz-secret'); - await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); - await userEvent.type( - selectors.recipient(), - 'user1@test.com, user2@test.com', - ); + await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); + await userEvent.type( + selectors.recipient(), + 'user1@test.com, user2@test.com', + ); - await userEvent.click(selectors.saveButton()); - }); + await userEvent.click(selectors.saveButton()); await waitForElementToBeRemoved(() => { return screen.getByText(/Saving\.\.\./); @@ -738,22 +789,21 @@ spec: 'smtp4dev.default.svc.cluster.local', ); await userEvent.type(selectors.port(), '42'); + }); + await userEvent.click(selectors.authSelect.authClick()); + await userEvent.click(selectors.authSelect.optionPlain()); - await userEvent.click(selectors.authSelect.authClick()); - await userEvent.click(selectors.authSelect.optionPlain()); - - await userEvent.type(selectors.identity(), 'RenardID'); - await userEvent.type(selectors.username(), 'Renard'); - await userEvent.type(selectors.password(), 'xxxyyyzzz-password'); + await userEvent.type(selectors.identity(), 'RenardID'); + await userEvent.type(selectors.username(), 'Renard'); + await userEvent.type(selectors.password(), 'xxxyyyzzz-password'); - await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); - await userEvent.type( - selectors.recipient(), - 'user1@test.com, user2@test.com', - ); + await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); + await userEvent.type( + selectors.recipient(), + 'user1@test.com, user2@test.com', + ); - await userEvent.click(selectors.saveButton()); - }); + await userEvent.click(selectors.saveButton()); await waitForElementToBeRemoved(() => { return screen.getByText(/Saving\.\.\./); @@ -876,21 +926,20 @@ spec: 'smtp4dev.default.svc.cluster.local', ); await userEvent.type(selectors.port(), '42'); + }); + await userEvent.click(selectors.authSelect.authClick()); + await userEvent.click(selectors.authSelect.optionLogin()); - await userEvent.click(selectors.authSelect.authClick()); - await userEvent.click(selectors.authSelect.optionLogin()); + await userEvent.type(selectors.username(), 'Renard'); + await userEvent.type(selectors.password(), 'Renard Password'); - await userEvent.type(selectors.username(), 'Renard'); - await userEvent.type(selectors.password(), 'Renard Password'); + await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); + await userEvent.type( + selectors.recipient(), + 'user1@test.com, user2@test.com', + ); - await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); - await userEvent.type( - selectors.recipient(), - 'user1@test.com, user2@test.com', - ); - - await userEvent.click(selectors.sendTestingEmailButton()); - }); + await userEvent.click(selectors.sendTestingEmailButton()); expect(selectors.sendTestingEmailButton()).toBeDisabled(); await waitForElementToBeRemoved(() => { @@ -950,22 +999,22 @@ spec: 'smtp4dev.default.svc.cluster.local', ); await userEvent.type(selectors.port(), '42'); + }); - await userEvent.click(selectors.authSelect.authClick()); - await userEvent.click(selectors.authSelect.optionPlain()); + await userEvent.click(selectors.authSelect.authClick()); + await userEvent.click(selectors.authSelect.optionPlain()); - await userEvent.type(selectors.identity(), 'Renard ID'); - await userEvent.type(selectors.username(), 'Renard'); - await userEvent.type(selectors.password(), 'Renard Password'); + await userEvent.type(selectors.identity(), 'Renard ID'); + await userEvent.type(selectors.username(), 'Renard'); + await userEvent.type(selectors.password(), 'Renard Password'); - await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); - await userEvent.type( - selectors.recipient(), - 'user1@test.com, user2@test.com', - ); + await userEvent.type(selectors.sender(), 'renard.admin@scality.com'); + await userEvent.type( + selectors.recipient(), + 'user1@test.com, user2@test.com', + ); - await userEvent.click(selectors.sendTestingEmailButton()); - }); + await userEvent.click(selectors.sendTestingEmailButton()); expect(selectors.sendTestingEmailButton()).toBeDisabled(); await waitForElementToBeRemoved(() => { diff --git a/ui/src/components/__TEST__/util.tsx b/ui/src/components/__TEST__/util.tsx index 29ccb2a2b5..d84b3e79ff 100644 --- a/ui/src/components/__TEST__/util.tsx +++ b/ui/src/components/__TEST__/util.tsx @@ -1,23 +1,28 @@ +import { MetricsTimeSpanProvider } from '@scality/core-ui/dist/components/linetemporalchart/MetricTimespanProvider'; +import { + render, + screen, + waitForElementToBeRemoved, +} from '@testing-library/react'; import React from 'react'; -import { ThemeProvider } from 'styled-components'; -import { render } from '@testing-library/react'; -import { QueryClient, QueryClientProvider } from 'react-query'; -import { screen, waitForElementToBeRemoved } from '@testing-library/react'; import { IntlProvider } from 'react-intl'; -import AlertProvider from '../../containers/AlertProvider'; +import { QueryClient, QueryClientProvider } from 'react-query'; import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; import { applyMiddleware, compose, createStore } from 'redux'; import createSagaMiddleware from 'redux-saga'; -import { BrowserRouter as Router } from 'react-router-dom'; -import { createMemoryHistory } from 'history'; -import { StyleSheetManager, StylisPlugin } from 'styled-components'; -import { MetricsTimeSpanProvider } from '@scality/core-ui/dist/components/linetemporalchart/MetricTimespanProvider'; +import { + StyleSheetManager, + StylisPlugin, + ThemeProvider, +} from 'styled-components'; +import AlertProvider from '../../containers/AlertProvider'; +import { ToastProvider } from '@scality/core-ui'; +import { coreUIAvailableThemes } from '@scality/core-ui/dist/style/theme'; +import StartTimeProvider from '../../containers/StartTimeProvider'; import reducer from '../../ducks/reducer'; import translations_en from '../../translations/en.json'; -import StartTimeProvider from '../../containers/StartTimeProvider'; -import { coreUIAvailableThemes } from '@scality/core-ui/dist/style/theme'; -import { ToastProvider } from '@scality/core-ui'; const composeEnhancers = // @ts-expect-error - FIXME when you are working on it @@ -71,8 +76,6 @@ export const metalK8sConfig = { }; export const AllTheProviders = (initialPath: string = '/') => { return ({ children }: { children: React.ReactNode }) => { - const history = createMemoryHistory(); - history.push(initialPath); const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -85,8 +88,9 @@ export const AllTheProviders = (initialPath: string = '/') => { // When you use jest-preview, you need to set the environment variable JEST_PREVIEW at on. if (process.env.JEST_PREVIEW === 'on') { + console.log('JEST_PREVIEW is on'); return ( - + @@ -102,7 +106,7 @@ export const AllTheProviders = (initialPath: string = '/') => { - + ); } @@ -111,7 +115,7 @@ export const AllTheProviders = (initialPath: string = '/') => { stylisPlugins={[simplifiedStylesPlugin]} disableVendorPrefixes > - + @@ -127,7 +131,7 @@ export const AllTheProviders = (initialPath: string = '/') => { - + ); }; diff --git a/ui/src/containers/App.tsx b/ui/src/containers/App.tsx index 48fb6562b2..4d07c54558 100644 --- a/ui/src/containers/App.tsx +++ b/ui/src/containers/App.tsx @@ -3,11 +3,14 @@ import AlertProvider from './AlertProvider'; import FederatedIntlProvider from './IntlProvider'; import Layout from './Layout'; import StartTimeProvider from './StartTimeProvider'; +import { useLocation } from 'react-router-dom'; const App = () => { + const location = useLocation(); + return ( - + diff --git a/ui/src/services/utils.test.ts b/ui/src/services/utils.test.ts index 7356d3312f..45320c5554 100644 --- a/ui/src/services/utils.test.ts +++ b/ui/src/services/utils.test.ts @@ -1,13 +1,11 @@ import { NAN_STRING } from '@scality/core-ui/dist/components/constants'; -import { renderHook } from '@testing-library/react-hooks'; import { STATUS_CRITICAL, STATUS_HEALTH, STATUS_WARNING } from '../constants'; import { + allSizeUnitsToBytes, fromMilliSectoAge, - useTableSortURLSync, - linuxDrivesNamingIncrement, - getSegments, getNaNSegments, - allSizeUnitsToBytes, + getSegments, + linuxDrivesNamingIncrement, } from './utils'; describe('allSizeUnitsToBytes', () => { it('should convert B to B', () => { @@ -43,45 +41,19 @@ it('should return 1d1m instead of 1d1m1s or 1d1s', () => { expect(result).toEqual('1d1m'); }); // Mocking history from react-router to test the URL sync hook -const mockHistoryReplace = jest.fn(); +const mockNavigate = jest.fn(); +let mockLocation = new URL('http://test.test'); + jest.mock('react-router-dom', () => { - let location = new URL('http://test.test'); return { ...jest.requireActual('react-router-dom'), - useHistory: () => ({ - replace: (newLocation) => { - location = new URL('http://test.test' + newLocation); - mockHistoryReplace(newLocation); - }, + useNavigate: () => mockNavigate, + useLocation: () => ({ + pathname: mockLocation.pathname, + search: mockLocation.search, }), - useLocation: () => location, }; }); -describe('useTableSortURLSync hook', () => { - it('should not set anything in the URL if data is not ready', () => { - renderHook(() => useTableSortURLSync('name', false, [], 'key')); - expect(mockHistoryReplace).not.toHaveBeenCalled(); - }); - it('should set a name sorting in the URL', () => { - renderHook(() => useTableSortURLSync('name', false, ['foo'], 'key')); - expect(mockHistoryReplace).toHaveBeenCalledWith('?sort=name'); - }); - it('should set a status sorting in the URL with a desc parameter', () => { - // @ts-expect-error - FIXME when you are working on it - renderHook(() => useTableSortURLSync('status', true, ['foo']), 'key'); - expect(mockHistoryReplace).toHaveBeenCalledWith('?sort=status&desc=true'); - }); - it('should clear the URL params if status goes back to default (health)', () => { - let status = 'status'; - const { rerender } = renderHook((props) => { - return useTableSortURLSync(status, false, ['foo'], 'health'); - }); - expect(mockHistoryReplace).toHaveBeenCalledWith('?sort=status'); - status = 'health'; - rerender(); - expect(mockHistoryReplace).toHaveBeenCalledWith('?'); - }); -}); // the tests of the recommended device path it('should return next driver', () => { const result = linuxDrivesNamingIncrement('/dev/vda', 1); diff --git a/ui/src/services/utils.ts b/ui/src/services/utils.ts index 3c4376df6f..ead6232971 100644 --- a/ui/src/services/utils.ts +++ b/ui/src/services/utils.ts @@ -471,49 +471,6 @@ export const formatSizeForDisplay = (value) => { else return value || null; }; -/** - * Custom hook that stores table sorting choice in the URL queries - * - * @param {string} sorted - * @param {boolean} desc - * @param {array} data - * @param {string} defaultSortKey default sorting key - */ -export const useTableSortURLSync = (sorted, desc, data, defaultSortKey) => { - const navigate = useNavigate(); - const location = useLocation(); - useEffect(() => { - const query = new URLSearchParams(location.search); - const querySort = query.get('sort'); - const queryDesc = query.get('desc'); - - if (data.length && (sorted !== querySort || desc !== queryDesc)) { - if (sorted) { - sorted ? query.set('sort', sorted) : query.delete('sort'); - desc ? query.set('desc', desc) : query.delete('desc'); - - // if the current sorting is the default sorting, remove the query parameter - if (sorted === defaultSortKey && desc === false) { - query.delete('sort'); - query.delete('desc'); - } - } else if (!sorted && querySort) { - query.delete('sort'); - query.delete('desc'); - } - - // We replace the current url only if expected current query params are different - // than the expected one. This avoid triggering redirection loop if one of consumer - // of this hooks get frequently renderred. - if ( - query.toString() !== new URLSearchParams(location.search).toString() - ) { - navigate(`?${query.toString()}`, { replace: true }); - } - } - }, [sorted, desc, data.length, navigate, location, defaultSortKey]); -}; - /* ** Custom hook to define chart dimension based on their container ** By default this calculates the width for 3 rows of 2 charts diff --git a/ui/src/setupTests.tsx b/ui/src/setupTests.tsx index 07be388025..d63c1b809c 100644 --- a/ui/src/setupTests.tsx +++ b/ui/src/setupTests.tsx @@ -4,6 +4,7 @@ import '@testing-library/jest-dom/extend-expect'; import 'babel-polyfill'; import { Alert } from './services/alertUtils'; import React from 'react'; +import { TextEncoder, TextDecoder } from 'util'; setupLocalStorageMock(); window.fetch = (url, ...rest) => @@ -138,3 +139,6 @@ jest.mock('./containers/PrivateRoute', () => ({ }; }), })); + +(global as any).TextEncoder = TextEncoder; +(global as any).TextDecoder = TextDecoder;