Skip to content

Commit

Permalink
feat: SB-734 Finished update user migration
Browse files Browse the repository at this point in the history
* [SB-734] Finished update user migration

* Resolved PR comments and adjusted previous migrations hooks

* Resolved PR comments


Approved-by: Sebastian Drejkarz
  • Loading branch information
worldsbestdeveloper committed Jan 31, 2023
1 parent 3d7f112 commit 463bf7e
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 184 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { act, screen } from '@testing-library/react';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createMockEnvironment, MockPayloadGenerator } from 'relay-test-utils';
import { GraphQLError } from 'graphql/error/GraphQLError';
import { createMockEnvironment } from 'relay-test-utils';

import { render } from '../../../../../tests/utils/rendering';
import { EditProfileForm } from '../editProfileForm.component';
import { currentUserFactory } from '../../../../../mocks/factories';
import { Role } from '../../../../../modules/auth/auth.types';

import { snackbarActions } from '../../../../../modules/snackbar';

import { authUpdateUserProfileMutation } from '../editProfileForm.graphql';
import { Role } from '../../../../../modules/auth/auth.types';

import { fillCommonQueryWithUser } from '../../../../utils/commonQuery';
import { currentUserFactory } from '../../../../../mocks/factories';

const mockDispatch = jest.fn();
jest.mock('react-redux', () => {
Expand All @@ -17,35 +22,62 @@ jest.mock('react-redux', () => {
};
});

const formData = {
firstName: 'updated-first-name',
lastName: 'updated-last-name',
};

const requestMock = (error?: GraphQLError[]) => ({
request: {
query: authUpdateUserProfileMutation,
variables: {
input: {
firstName: formData.firstName,
lastName: formData.lastName,
},
},
},
result: {
data: {
updateCurrentUser: {
userProfile: {
id: '1',
user: {
firstName: formData.firstName,
lastName: formData.lastName,
},
},
},
},
errors: error,
},
});

//TODO Waiting for commonQuery implementation
const renderComponent = (error?: GraphQLError[]) => {
const relayEnvironment = createMockEnvironment();
const currentUser = currentUserFactory({
firstName: 'Jack',
lastName: 'White',
email: 'jack.white@mail.com',
roles: [Role.ADMIN, Role.USER],
});
fillCommonQueryWithUser(relayEnvironment, currentUser);
return {
...render(<EditProfileForm />, { relayEnvironment, apolloMocks: [requestMock(error)] }),
relayEnvironment,
updatedUser: {
...currentUser,
...formData,
},
};
};

describe('EditProfileForm: Component', () => {
beforeEach(() => {
mockDispatch.mockReset();
});

const formData = {
firstName: 'updated-first-name',
lastName: 'updated-last-name',
};

const renderComponent = () => {
const relayEnvironment = createMockEnvironment();
const currentUser = currentUserFactory({
firstName: 'Jack',
lastName: 'White',
email: 'jack.white@mail.com',
roles: [Role.ADMIN, Role.USER],
});
fillCommonQueryWithUser(relayEnvironment, currentUser);
return {
...render(<EditProfileForm />, { relayEnvironment }),
relayEnvironment,
updatedUser: {
...currentUser,
...formData,
},
};
};

const getFirstNameField = () => screen.getByLabelText(/first name/i);
const getLastNameField = () => screen.getByLabelText(/last name/i);

Expand All @@ -60,119 +92,51 @@ describe('EditProfileForm: Component', () => {
await userEvent.click(screen.getByRole('button', { name: /update personal data/i }));
};

it('should call updateProfile action when submitted', async () => {
const { relayEnvironment } = renderComponent();
it('should show success message after success action', async () => {
renderComponent();

await fillForm();
await submitForm();
expect(relayEnvironment).toHaveLatestOperation('authUpdateUserProfileMutation');
expect(relayEnvironment).toLatestOperationInputEqual(formData);
});

describe('action completes successfully', () => {
it('should show success message', async () => {
const { relayEnvironment } = renderComponent();

await fillForm();
await submitForm();

await act(async () => {
const operation = relayEnvironment.mock.getMostRecentOperation();
relayEnvironment.mock.resolve(operation, MockPayloadGenerator.generate(operation));
});

await waitFor(() => {
expect(mockDispatch).toHaveBeenCalledWith(
snackbarActions.showMessage({
text: 'Personal data successfully changed.',
id: 1,
})
);
});
});

it('should display updated values', async () => {
const { relayEnvironment } = renderComponent();
await fillForm();
await submitForm();
it('should display updated values', async () => {
renderComponent();

await act(async () => {
const operation = relayEnvironment.mock.getMostRecentOperation();
relayEnvironment.mock.resolve(operation, MockPayloadGenerator.generate(operation));
});
await fillForm();
await submitForm();

expect(screen.getByDisplayValue(formData.firstName)).toBeInTheDocument();
expect(screen.getByDisplayValue(formData.lastName)).toBeInTheDocument();
});
expect(screen.getByDisplayValue(formData.firstName)).toBeInTheDocument();
expect(screen.getByDisplayValue(formData.lastName)).toBeInTheDocument();
});

it('should show error if value is too long', async () => {
const { relayEnvironment } = renderComponent();
renderComponent();
await userEvent.type(screen.getByLabelText(/first name/i), '_'.repeat(41));
await userEvent.type(screen.getByLabelText(/last name/i), formData.lastName);
await submitForm();
expect(relayEnvironment).not.toHaveLatestOperation('authUpdateUserProfileMutation');
expect(mockDispatch).not.toHaveBeenCalledWith();
expect(screen.getByText('First name is too long')).toBeInTheDocument();
});

it('should show field error if action throws error', async () => {
const { relayEnvironment } = renderComponent();
await fillForm();
await submitForm();

expect(relayEnvironment).toHaveLatestOperation('authUpdateUserProfileMutation');

const errorMessage = 'Provided value is invalid';
await act(async () => {
const operation = relayEnvironment.mock.getMostRecentOperation();
relayEnvironment.mock.resolve(operation, {
...MockPayloadGenerator.generate(operation),
errors: [
{
message: 'GraphQlValidationError',
extensions: {
firstName: [
{
message: errorMessage,
code: 'invalid',
},
],
},
},
],
} as any);
});

expect(screen.getByText(errorMessage)).toBeInTheDocument();
expect(mockDispatch).not.toHaveBeenCalledWith();
expect(screen.getByText('First name is too long')).toBeInTheDocument();
});

it('should show generic form error if action throws error', async () => {
const { relayEnvironment } = renderComponent();
await fillForm();
await submitForm();

expect(relayEnvironment).toHaveLatestOperation('authUpdateUserProfileMutation');
const errorMessage = 'Invalid data';
const error = [new GraphQLError(errorMessage)];
renderComponent(error);

await act(async () => {
const operation = relayEnvironment.mock.getMostRecentOperation();
relayEnvironment.mock.resolve(operation, {
...MockPayloadGenerator.generate(operation),
errors: [
{
message: 'GraphQlValidationError',
extensions: {
nonFieldErrors: [
{
message: errorMessage,
code: 'invalid',
},
],
},
},
],
} as any);
});
await fillForm();
await submitForm();

expect(screen.getByText(errorMessage)).toBeInTheDocument();
expect(await screen.findByText(errorMessage)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const EditProfileForm = () => {
},
genericError,
hasGenericErrorOnly,
loading,
handleUpdate,
} = useEditProfileForm();

Expand Down Expand Up @@ -59,7 +60,7 @@ export const EditProfileForm = () => {
</FormFieldsRow>

{hasGenericErrorOnly && <ErrorMessage>{genericError}</ErrorMessage>}
<SubmitButton>
<SubmitButton disabled={loading}>
<FormattedMessage defaultMessage="Update personal data" id="Auth / Update profile/ Submit button" />
</SubmitButton>
</Form>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { gql } from '../../../services/graphqlApi/__generated/gql';

export const authUpdateUserProfileMutation = gql(/* GraphQL */ `
mutation authUpdateUserProfileMutation($input: UpdateCurrentUserMutationInput!) {
updateCurrentUser(input: $input) {
userProfile {
id
user {
...commonQueryCurrentUserFragment
}
}
}
}
`);
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { useIntl } from 'react-intl';
import { ApolloError, useMutation } from '@apollo/client';

import authUpdateUserProfileMutationGraphql, {
authUpdateUserProfileMutation,
} from '../../../../modules/auth/__generated__/authUpdateUserProfileMutation.graphql';
import { useApiForm } from '../../../hooks/useApiForm';
import { useAuth } from '../../../hooks/useAuth/useAuth';
import { usePromiseMutation } from '../../../services/graphqlApi/usePromiseMutation';

import { useSnackbar } from '../../../../modules/snackbar';
import { authUpdateUserProfileMutation } from './editProfileForm.graphql';
import { UpdateProfileFormFields } from './editProfileForm.types';

export const useEditProfileForm = () => {
Expand All @@ -20,31 +19,29 @@ export const useEditProfileForm = () => {
},
});

const { handleSubmit, setGraphQLResponseErrors } = form;
const { handleSubmit, setApolloGraphQLResponseErrors } = form;

const [commitUpdateUserMutation] = usePromiseMutation<authUpdateUserProfileMutation>(
authUpdateUserProfileMutationGraphql
);
const [commitUpdateUserMutation, { loading }] = useMutation(authUpdateUserProfileMutation, {
onCompleted: () => {
snackbar.showMessage(
intl.formatMessage({
defaultMessage: 'Personal data successfully changed.',
id: 'Auth / Update profile/ Success message',
})
);
},
onError: (error) => {
if (error instanceof ApolloError) setApolloGraphQLResponseErrors(error.graphQLErrors);
},
});

const handleUpdate = handleSubmit(async (input: UpdateProfileFormFields) => {
try {
const { errors } = await commitUpdateUserMutation({
variables: {
input,
},
});
if (errors) {
setGraphQLResponseErrors(errors);
} else {
snackbar.showMessage(
intl.formatMessage({
defaultMessage: 'Personal data successfully changed.',
id: 'Auth / Update profile/ Success message',
})
);
}
} catch {}
const handleUpdate = handleSubmit((input: UpdateProfileFormFields) => {
commitUpdateUserMutation({
variables: {
input,
},
});
});

return { ...form, handleUpdate };
return { ...form, loading, handleUpdate };
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const LoginForm = () => {
},
hasGenericErrorOnly,
genericError,
loading,
handleLogin,
} = useLoginForm();

Expand Down Expand Up @@ -78,7 +79,7 @@ export const LoginForm = () => {

{hasGenericErrorOnly && <ErrorMessage>{genericError}</ErrorMessage>}

<SubmitButton>
<SubmitButton disabled={loading}>
<FormattedMessage defaultMessage="Log in" id="Auth / login button" />
</SubmitButton>
</Container>
Expand Down
Loading

0 comments on commit 463bf7e

Please sign in to comment.