Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'Clean up' button to Host Details page #1623

Merged
merged 16 commits into from
Jul 18, 2023
Merged

Conversation

jamie-suse
Copy link
Contributor

@jamie-suse jamie-suse commented Jul 12, 2023

CleanUpHostDetails.webm

Description

Adds the 'Clean up' button to the Host Details page. Allows the user to deregister a host.

How was this tested?

Added frontend unit tests to test the behaviour of the button.

@jamie-suse jamie-suse added enhancement New feature or request javascript Pull requests that update Javascript code labels Jul 12, 2023
Copy link
Contributor

@arbulu89 arbulu89 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!
Only 2 things.

  • Could you add a gif on how it works?
  • Change fireEvent by userEvent
    We can merge after that XD


const dispatch = useDispatch();

const openDeregistrationModal = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use directly the setCleanUpModalOpen(true) , right?
I know that you did to be consistent with the other.
Anyway, up to you, not a big deal hehe

@@ -1,5 +1,6 @@
import React from 'react';
import { screen } from '@testing-library/react';
import { screen, fireEvent } from '@testing-library/react';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use userEvent instead of fireEvent. It is the recommended library.
You will need to setup the user, etc
Check other test files with this approach

@@ -66,4 +67,115 @@ describe('HostDetails component', () => {
)
).toBeInTheDocument();
});

it("should not display 'Clean up' button when host is not deregisterable", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double quotes? I guess the ci will complain about this hehe
I would remove the single quotes in any case from Clean up and put them in the test string

@@ -66,4 +67,115 @@ describe('HostDetails component', () => {
)
).toBeInTheDocument();
});

it("should not display 'Clean up' button when host is not deregisterable", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could maybe add a deregistration describe to group the tests about this topic.
Again, not strong opinions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


const cleanUpHost = () => {
setCleanUpModalOpen(false);
dispatch(deregisterHost(host));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should get the host as argument in the function.
If we use the global variable, we have the risk of having strange things.
For example, if the function was used when host is not loaded, things wouldn't work.

Copy link
Contributor

@arbulu89 arbulu89 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okey!
Once we have the navigation sorted out the code looks good


const cleanUpHost = (selectedHost) => {
setCleanUpModalOpen(false);
dispatch(deregisterHost({ host: selectedHost, navigate }));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We careful with this!
If you change the payload format for the function, you need to change it every place that it is used, the host list view in this case.
I would suggest to go with deregisterHost({ ...selectedHost, navigate }), but if you prefer this way don't forget to change the host list view call as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes this is true. I thought tests would catch this in HostsList.test.jsx, but it didn't. Is there a way to have tests detect this kind of change? 👀

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most probably not...

@@ -52,6 +52,7 @@ export function* deregisterHost({ payload }) {
yield put(setHostDeregistering(payload));
try {
yield call(del, `/hosts/${payload.id}`);
payload.navigate?.('/hosts');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never seen this format before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fancy Optional chaining with function calls.

Basically, if payload.navigate doesn't exist, then when we try to call payload.navigate(), we instead just returned undefined, acting like a NOOP.

If we didn't do this, then when calling payload.navigate(), we would get the error thrown: Uncaught TypeError: payload.navigate is not a function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to do this because we only want to navigate back to HostsList from HostDetails. It doesn't really make sense to do it from HostsList because we are already there and it is updated by websocket events. So as a result we don't pass in navigate to the payload to make this a NOOP.

@@ -52,6 +52,7 @@ export function* deregisterHost({ payload }) {
yield put(setHostDeregistering(payload));
try {
yield call(del, `/hosts/${payload.id}`);
payload.navigate?.('/hosts');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
payload.navigate?.('/hosts');
const { navigate } = payload;
navigate('/hosts');

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if payload.navigate doesn't exist and then we try to call navigate() we will get Uncaught TypeError: navigate is not a function.

With optional chaining if navigate doesn't exist, the call to navigate() just returns undefined, essentially a NOOP.

We need to do this because we only want to navigate back to HostsList from HostDetails. It doesn't really make sense to do it from HostsList because we are already there and it is updated by websocket events. So as a result we don't pass in navigate to the payload to make this a NOOP.

@@ -52,6 +52,7 @@ export function* deregisterHost({ payload }) {
yield put(setHostDeregistering(payload));
try {
yield call(del, `/hosts/${payload.id}`);
payload.navigate?.('/hosts');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree to remove the optional chaining and unify the flows, we need to redirect always even on the host list, it's a no-op for the router, and we avoid to pass or not pass parameters, we don't have types and this cause only confusion imho.

Unify the things, pass the navigate always and remove the optional chaining

@@ -84,15 +84,15 @@ describe('Hosts sagas', () => {
it('should send host deregister request', async () => {
const host = hostFactory.build();

const mockNavigate = (route) => route;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we have chosen to always provide a function navigate() to the action payload for the deregisterHost() Saga, now in the test we need to provide a mocked navigate function. Is this function fine?

Copy link
Contributor

@dottorblaster dottorblaster Jul 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could have just a () => {} but this is fine as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed!

Copy link
Member

@CDimonaco CDimonaco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

assets/js/components/HostDetails/HostDetails.jsx Outdated Show resolved Hide resolved
@@ -66,7 +66,9 @@ function HostsList() {

const cleanUpHost = (host) => {
setCleanUpModalOpen(false);
dispatch(deregisterHost({ ...host, navigate }));

const { id, hostname } = host;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls destructure on the function argument level 👁️

@jamie-suse jamie-suse merged commit 4180201 into main Jul 18, 2023
16 checks passed
@jamie-suse jamie-suse deleted the deregister-host-details branch July 18, 2023 14:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request javascript Pull requests that update Javascript code
Development

Successfully merging this pull request may close these issues.

4 participants