Skip to content

Commit

Permalink
Test suma connection UI (#2453)
Browse files Browse the repository at this point in the history
* Add test suma connection saga

* Add testing connection state in software updates selector

* Add test suma connection button

* Specify classname for api key generation button to avoid clashes with other buttons

* Add same color border for default button for spacing concerns

* Adjust lodash imports
  • Loading branch information
nelsonkopliku authored Mar 22, 2024
1 parent 1d2f080 commit 65b1137
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ function ApiKeySettingsModal({
</div>
<div className="w-1/6 h-4/5">
<Button
className="generate-api-key"
onClick={() => generateApiKeyExpiration()}
disabled={quantityError || loading}
>
Expand Down
2 changes: 1 addition & 1 deletion assets/js/common/Button/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const getButtonClasses = (type) => {
case 'secondary':
return 'bg-persimmon hover:opacity-75 focus:outline-none text-gray-800 w-full transition ease-in duration-200 text-center font-semibold rounded shadow';
case 'default-fit':
return 'bg-jungle-green-500 hover:opacity-75 focus:outline-none text-white w-fit transition ease-in duration-200 text-center font-semibold rounded shadow';
return 'bg-jungle-green-500 hover:opacity-75 focus:outline-none text-white border border-jungle-green-500 w-fit transition ease-in duration-200 text-center font-semibold rounded shadow';
case 'danger':
return 'bg-white hover:opacity-75 focus:outline-none text-red-500 border border-red-500 transition ease-in duration-200 text-center font-semibold rounded shadow';
case 'danger-bold':
Expand Down
11 changes: 11 additions & 0 deletions assets/js/common/SuseManagerConfig/SuseManagerConfig.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ function SuseManagerConfig({
certUploadDate,
onEditClick = noop,
clearSettingsDialogOpen = false,
testConnectionEnabled = false,
onClearClick = noop,
onClearSettings = noop,
onTestConnection = noop,
onCancel = noop,
}) {
return (
Expand All @@ -32,6 +34,15 @@ function SuseManagerConfig({
SUSE Manager Config
</h2>
<span className="float-right">
<Button
className="mr-2"
type="default-fit"
size="small"
disabled={!testConnectionEnabled}
onClick={onTestConnection}
>
Test Connection
</Button>
<Button
className="mr-2"
type="primary-white-fit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ export default {
type: 'boolean',
},
},
testConnectionEnabled: {
description: "Whether the 'Test connection' button is enabled or not",
control: {
type: 'boolean',
},
},
onClearClick: {
description: "Callback used to open 'Clear Settings' dialog",
control: {
Expand Down
26 changes: 26 additions & 0 deletions assets/js/common/SuseManagerConfig/SuseManagerConfig.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ describe('SuseManagerConfig', () => {
expect(screen.getByText('https://')).toBeInTheDocument();
expect(screen.getAllByText('-')).toHaveLength(1);
expect(screen.getAllByText('.....')).toHaveLength(2);
expect(
screen.getByRole('button', { name: 'Test Connection' })
).toBeDisabled();
});

it('renders settings', async () => {
Expand Down Expand Up @@ -41,4 +44,27 @@ describe('SuseManagerConfig', () => {
await user.click(editSettingsButton);
expect(onEditClick).toHaveBeenCalled();
});

it('allows testing connection', async () => {
const user = userEvent.setup();

const onTestConnection = jest.fn();

render(
<SuseManagerConfig
url={faker.internet.url()}
username={faker.animal.cat()}
certUploadDate={faker.date.anytime()}
testConnectionEnabled
onTestConnection={onTestConnection}
/>
);
expect(
screen.getByRole('button', { name: 'Test Connection' })
).toBeEnabled();

const testConnectionButton = screen.getByText('Test Connection');
await user.click(testConnectionButton);
expect(onTestConnection).toHaveBeenCalled();
});
});
3 changes: 3 additions & 0 deletions assets/js/lib/api/softwareUpdatesSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ export const updateSettings = (settings) =>

export const clearSettings = () =>
networkClient.delete(`/settings/suma_credentials`);

export const testConnection = () =>
networkClient.post(`/settings/suma_credentials/test`);
13 changes: 13 additions & 0 deletions assets/js/pages/SettingsPage/SettingsPage.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Transition } from '@headlessui/react';
import { values, isUndefined } from 'lodash';
import { format, parseISO } from 'date-fns';
import classNames from 'classnames';
import { EOS_INFO_OUTLINED } from 'eos-icons-react';
Expand All @@ -20,6 +21,7 @@ import {
updateSoftwareUpdatesSettings,
setEditingSoftwareUpdatesSettings,
clearSoftwareUpdatesSettings,
testSoftwareUpdatesConnection,
} from '@state/softwareUpdatesSettings';
import {
getSoftwareUpdatesSettings,
Expand Down Expand Up @@ -81,12 +83,17 @@ function SettingsPage() {
settings,
loading: softwareUpdatesSettingsLoading,
editing: editingSoftwareUpdatesSettings,
testingConnection: testingSoftwareUpdatesConnection,
} = useSelector(getSoftwareUpdatesSettings);

const suseManagerValidationErrors = useSelector(
getSoftwareUpdatesSettingsErrors
);

const hasSoftwareUpdatesSettings = values(settings).every(
(value) => !isUndefined(value)
);

const hasApiKey = Boolean(apiKey);

return (
Expand Down Expand Up @@ -264,6 +271,12 @@ function SettingsPage() {
setClearingSoftwareUpdatesSettings(false);
dispatch(clearSoftwareUpdatesSettings());
}}
testConnectionEnabled={
hasSoftwareUpdatesSettings && !testingSoftwareUpdatesConnection
}
onTestConnection={() => {
dispatch(testSoftwareUpdatesConnection());
}}
onCancel={() => setClearingSoftwareUpdatesSettings(false)}
/>
)}
Expand Down
18 changes: 18 additions & 0 deletions assets/js/state/sagas/softwareUpdatesSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@ import {
saveSettings,
updateSettings,
clearSettings,
testConnection,
} from '@lib/api/softwareUpdatesSettings';

import {
FETCH_SOFTWARE_UPDATES_SETTINGS,
SAVE_SOFTWARE_UPDATES_SETTINGS,
UPDATE_SOFTWARE_UPDATES_SETTINGS,
CLEAR_SOFTWARE_UPDATES_SETTINGS,
TEST_SOFTWARE_UPDATES_CONNECTION,
startLoadingSoftwareUpdatesSettings,
setSoftwareUpdatesSettings,
setEmptySoftwareUpdatesSettings,
setSoftwareUpdatesSettingsErrors,
setEditingSoftwareUpdatesSettings,
setTestingSoftwareUpdatesConnection,
} from '@state/softwareUpdatesSettings';

export function* fetchSoftwareUpdatesSettings() {
Expand Down Expand Up @@ -78,6 +81,17 @@ export function* clearSoftwareUpdatesSettings() {
}
}

export function* testSoftwareUpdatesConnection() {
yield put(setTestingSoftwareUpdatesConnection(true));
try {
yield call(testConnection);
yield put(notify({ text: `Connection succeeded!`, icon: '✅' }));
} catch (error) {
yield put(notify({ text: `Connection failed!`, icon: '❌' }));
}
yield put(setTestingSoftwareUpdatesConnection(false));
}

export function* watchSoftwareUpdateSettings() {
yield takeEvery(
FETCH_SOFTWARE_UPDATES_SETTINGS,
Expand All @@ -92,4 +106,8 @@ export function* watchSoftwareUpdateSettings() {
CLEAR_SOFTWARE_UPDATES_SETTINGS,
clearSoftwareUpdatesSettings
);
yield takeEvery(
TEST_SOFTWARE_UPDATES_CONNECTION,
testSoftwareUpdatesConnection
);
}
35 changes: 35 additions & 0 deletions assets/js/state/sagas/softwareUpdatesSettings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import {
setSoftwareUpdatesSettingsErrors,
setEmptySoftwareUpdatesSettings,
setEditingSoftwareUpdatesSettings,
setTestingSoftwareUpdatesConnection,
} from '@state/softwareUpdatesSettings';

import {
fetchSoftwareUpdatesSettings,
saveSoftwareUpdatesSettings,
updateSoftwareUpdatesSettings,
clearSoftwareUpdatesSettings,
testSoftwareUpdatesConnection,
} from './softwareUpdatesSettings';

describe('Software Updates Settings saga', () => {
Expand Down Expand Up @@ -230,4 +232,37 @@ describe('Software Updates Settings saga', () => {
]);
});
});

describe('Testing connection with Software Updates provider', () => {
it('should notify on successful connection test', async () => {
const axiosMock = new MockAdapter(networkClient);

axiosMock.onPost('/settings/suma_credentials/test').reply(200);

const dispatched = await recordSaga(testSoftwareUpdatesConnection);

expect(dispatched).toEqual([
setTestingSoftwareUpdatesConnection(true),
notify({ text: `Connection succeeded!`, icon: '✅' }),
setTestingSoftwareUpdatesConnection(false),
]);
});

it.each([400, 404, 422, 500])(
'should notify on failed connection test',
async (errorStatus) => {
const axiosMock = new MockAdapter(networkClient);

axiosMock.onPost('/settings/suma_credentials/test').reply(errorStatus);

const dispatched = await recordSaga(testSoftwareUpdatesConnection);

expect(dispatched).toEqual([
setTestingSoftwareUpdatesConnection(true),
notify({ text: `Connection failed!`, icon: '❌' }),
setTestingSoftwareUpdatesConnection(false),
]);
}
);
});
});
3 changes: 2 additions & 1 deletion assets/js/state/selectors/softwareUpdatesSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { createSelector } from '@reduxjs/toolkit';

export const getSoftwareUpdatesSettings = createSelector(
[({ softwareUpdatesSettings }) => softwareUpdatesSettings],
({ settings, errors, loading, editing }) => ({
({ settings, errors, loading, editing, testingConnection }) => ({
settings,
errors,
loading,
editing,
testingConnection,
})
);

Expand Down
1 change: 1 addition & 0 deletions assets/js/state/selectors/softwareUpdatesSettings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('Software Updates Settings selector', () => {
},
errors: null,
editing: false,
testingConnection: false,
},
];

Expand Down
11 changes: 11 additions & 0 deletions assets/js/state/softwareUpdatesSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const initialState = {
settings: emptySettings,
networkError: null,
editing: false,
testingConnection: false,
errors: [],
};

Expand Down Expand Up @@ -42,6 +43,9 @@ export const softwareUpdatesSettingsSlice = createSlice({
setEditingSoftwareUpdatesSettings: (state, { payload }) => {
state.editing = payload;
},
setTestingSoftwareUpdatesConnection: (state, { payload }) => {
state.testingConnection = payload;
},
},
});

Expand All @@ -53,6 +57,9 @@ export const UPDATE_SOFTWARE_UPDATES_SETTINGS =
export const CLEAR_SOFTWARE_UPDATES_SETTINGS =
'CLEAR_SOFTWARE_UPDATES_SETTINGS';

export const TEST_SOFTWARE_UPDATES_CONNECTION =
'TEST_SOFTWARE_UPDATES_CONNECTION';

export const fetchSoftwareUpdatesSettings = createAction(
FETCH_SOFTWARE_UPDATES_SETTINGS
);
Expand All @@ -65,13 +72,17 @@ export const updateSoftwareUpdatesSettings = createAction(
export const clearSoftwareUpdatesSettings = createAction(
CLEAR_SOFTWARE_UPDATES_SETTINGS
);
export const testSoftwareUpdatesConnection = createAction(
TEST_SOFTWARE_UPDATES_CONNECTION
);

export const {
startLoadingSoftwareUpdatesSettings,
setSoftwareUpdatesSettings,
setEmptySoftwareUpdatesSettings,
setSoftwareUpdatesSettingsErrors,
setEditingSoftwareUpdatesSettings,
setTestingSoftwareUpdatesConnection,
} = softwareUpdatesSettingsSlice.actions;

export default softwareUpdatesSettingsSlice.reducer;
27 changes: 27 additions & 0 deletions assets/js/state/softwareUpdatesSettings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import softwareUpdatesSettingsReducer, {
setEmptySoftwareUpdatesSettings,
setSoftwareUpdatesSettingsErrors,
setEditingSoftwareUpdatesSettings,
setTestingSoftwareUpdatesConnection,
} from './softwareUpdatesSettings';

describe('SoftwareUpdateSettings reducer', () => {
Expand Down Expand Up @@ -144,4 +145,30 @@ describe('SoftwareUpdateSettings reducer', () => {
editing: true,
});
});

it('should mark that the connection is being tested', () => {
const initialState = {
loading: false,
settings: {
url: 'https://valid.url',
username: 'username',
ca_uploaded_at: '2021-01-01T00:00:00Z',
},
networkError: null,
errors: [],
editing: false,
testingConnection: false,
};

[true, false].forEach((isTestingConnection) => {
const action = setTestingSoftwareUpdatesConnection(isTestingConnection);

const actual = softwareUpdatesSettingsReducer(initialState, action);

expect(actual).toEqual({
...initialState,
testingConnection: isTestingConnection,
});
});
});
});
2 changes: 1 addition & 1 deletion test/e2e/cypress/e2e/settings.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ context('Settings page', () => {
it('should generate a new api key', () => {
cy.get('button').contains('Generate Key').click();
cy.get('.rc-input-number-input').as('quantityInput');
cy.get('.bg-jungle-green-500').as('generateButton');
cy.get('.generate-api-key').as('generateButton');

cy.get('@quantityInput').type('2');
cy.get('@generateButton').click();
Expand Down

0 comments on commit 65b1137

Please sign in to comment.