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

Achieved 100% Code Coverage for OrganizationCard Component #3464

Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 140 additions & 6 deletions src/components/OrganizationCard/OrganizationCard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ describe('OrganizationCard Component', () => {
});
});


// Mutation Tests
describe('Mutations', () => {
it('should handle joining a private organization successfully', async () => {
Expand Down Expand Up @@ -347,24 +348,157 @@ describe('OrganizationCard Component', () => {
});
});

it('should handle membership withdrawal error when request not found', async () => {
it('should log development error and show generic error toast', async () => {
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});

// Mock process.env.NODE_ENV to 'development'
const originalNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'development';

const props = {
...defaultProps,
userId: 'mockUserId',
membershipRequestStatus: 'pending',
membershipRequests: [], // Empty requests to trigger error
membershipRequests: [{ _id: 'requestId', user: { _id: 'mockUserId' } }],
};


const errorMocks: MockedResponse[] = [
{
request: {
query: CANCEL_MEMBERSHIP_REQUEST,
variables: { membershipRequestId: 'requestId' },
},
error: new Error('Withdrawal failed'),
},
];

render(
<TestWrapper mocks={errorMocks}>
<OrganizationCard {...props} isJoined={false} />
</TestWrapper>,
</TestWrapper>
);

const withdrawButton = screen.getByTestId('withdrawBtn');
await fireEvent.click(withdrawButton);

await waitFor(() => {
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Failed to withdraw membership request:',
expect.any(Error)
);
expect(toast.error).toHaveBeenCalledWith('errorOccured');
});

// Restore original environment
process.env.NODE_ENV = originalNodeEnv;
consoleErrorSpy.mockRestore();
});

it('should handle already joined error when joining organization', async () => {
const errorMocksWithAlreadyJoined: MockedResponse[] = [
{
request: {
query: JOIN_PUBLIC_ORGANIZATION,
variables: { organizationId: '123' },
},
result: {
errors: [
{
message: 'Already a member',
extensions: { code: 'ALREADY_MEMBER' },
},
],
},
},
];

render(
<TestWrapper mocks={errorMocksWithAlreadyJoined}>
<OrganizationCard
{...defaultProps}
userRegistrationRequired={false}
isJoined={false}
/>
</TestWrapper>
);

const joinButton = screen.getByText('joinNow');
await fireEvent.click(joinButton);

await waitFor(() => {
expect(toast.error).toHaveBeenCalledWith('AlreadyJoined');
});
});
it('should handle membership request not found', async () => {
// Mock getItem to return a userId that exists
mockGetItem.mockReturnValue('testUserId');

const props = {
...defaultProps,
membershipRequestStatus: 'pending',
// Create a membership requests array with a different user ID
membershipRequests: [
{
_id: 'requestId',
user: {
_id: 'differentUserId' // Different from the mocked userId
}
}
],
};

render(
<TestWrapper mocks={successMocks}>
<OrganizationCard {...props} isJoined={false} />
</TestWrapper>
);

// Find and click the withdraw button
const withdrawButton = screen.getByTestId('withdrawBtn');
await fireEvent.click(withdrawButton);

// Verify that the error toast is shown
await waitFor(() => {
expect(toast.error).toHaveBeenCalledWith('MembershipRequestNotFound');
});

// Verify the mutation was not called
const cancelMutation = successMocks.find(
mock => mock.request.query === CANCEL_MEMBERSHIP_REQUEST
);
expect(cancelMutation?.result).not.toHaveBeenCalled;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect assertion syntax and formatting issues.

The test has an incorrect assertion syntax and multiple formatting issues that need to be addressed.

      const cancelMutation = successMocks.find(
-       mock => mock.request.query === CANCEL_MEMBERSHIP_REQUEST
+       (mock) => mock.request.query === CANCEL_MEMBERSHIP_REQUEST,
      );
-     expect(cancelMutation?.result).not.toHaveBeenCalled;
+     expect(cancelMutation?.result).not.toHaveBeenCalled();

Also, fix the formatting issues in the membership requests array:

        membershipRequests: [
-         { 
-           _id: 'requestId', 
-           user: { 
-             _id: 'differentUserId'  // Different from the mocked userId
-           } 
-         }
+         {
+           _id: 'requestId',
+           user: {
+             _id: 'differentUserId',
+           },
+         },
        ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should handle membership request not found', async () => {
// Mock getItem to return a userId that exists
mockGetItem.mockReturnValue('testUserId');
const props = {
...defaultProps,
membershipRequestStatus: 'pending',
// Create a membership requests array with a different user ID
membershipRequests: [
{
_id: 'requestId',
user: {
_id: 'differentUserId' // Different from the mocked userId
}
}
],
};
render(
<TestWrapper mocks={successMocks}>
<OrganizationCard {...props} isJoined={false} />
</TestWrapper>
);
// Find and click the withdraw button
const withdrawButton = screen.getByTestId('withdrawBtn');
await fireEvent.click(withdrawButton);
// Verify that the error toast is shown
await waitFor(() => {
expect(toast.error).toHaveBeenCalledWith('MembershipRequestNotFound');
});
// Verify the mutation was not called
const cancelMutation = successMocks.find(
mock => mock.request.query === CANCEL_MEMBERSHIP_REQUEST
);
expect(cancelMutation?.result).not.toHaveBeenCalled;
});
it('should handle membership request not found', async () => {
// Mock getItem to return a userId that exists
mockGetItem.mockReturnValue('testUserId');
const props = {
...defaultProps,
membershipRequestStatus: 'pending',
// Create a membership requests array with a different user ID
membershipRequests: [
{
_id: 'requestId',
user: {
_id: 'differentUserId',
},
},
],
};
render(
<TestWrapper mocks={successMocks}>
<OrganizationCard {...props} isJoined={false} />
</TestWrapper>
);
// Find and click the withdraw button
const withdrawButton = screen.getByTestId('withdrawBtn');
await fireEvent.click(withdrawButton);
// Verify that the error toast is shown
await waitFor(() => {
expect(toast.error).toHaveBeenCalledWith('MembershipRequestNotFound');
});
// Verify the mutation was not called
const cancelMutation = successMocks.find(
(mock) => mock.request.query === CANCEL_MEMBERSHIP_REQUEST,
);
expect(cancelMutation?.result).not.toHaveBeenCalled();
});
🧰 Tools
🪛 ESLint

[error] 432-432: Delete ··

(prettier/prettier)


[error] 433-433: Delete ··

(prettier/prettier)


[error] 434-434: Delete ··

(prettier/prettier)


[error] 435-435: Delete ······

(prettier/prettier)


[error] 436-436: Delete ··

(prettier/prettier)


[error] 437-437: Delete ··

(prettier/prettier)


[error] 438-438: Delete ··

(prettier/prettier)


[error] 439-439: Delete ··

(prettier/prettier)


[error] 440-440: Delete ··

(prettier/prettier)


[error] 441-441: Replace ··{· with {

(prettier/prettier)


[error] 442-442: Replace ············_id:·'requestId',· with ··········_id:·'requestId',

(prettier/prettier)


[error] 443-443: Replace ··user:·{· with user:·{

(prettier/prettier)


[error] 444-444: Replace ··············_id:·'differentUserId'· with ············_id:·'differentUserId',

(prettier/prettier)


[error] 445-445: Replace ··}· with },

(prettier/prettier)


[error] 446-446: Replace ··········} with ········},

(prettier/prettier)


[error] 447-447: Delete ··

(prettier/prettier)


[error] 448-448: Replace ······ with ····

(prettier/prettier)


[error] 449-449: Delete ······

(prettier/prettier)


[error] 450-450: Delete ··

(prettier/prettier)


[error] 451-451: Delete ··

(prettier/prettier)


[error] 452-452: Replace ·········· with ········

(prettier/prettier)


[error] 453-453: Replace ··</TestWrapper> with </TestWrapper>,

(prettier/prettier)


[error] 454-454: Delete ··

(prettier/prettier)


[error] 455-455: Delete ······

(prettier/prettier)


[error] 456-456: Delete ··

(prettier/prettier)


[error] 457-457: Delete ··

(prettier/prettier)


[error] 458-458: Delete ··

(prettier/prettier)


[error] 459-459: Delete ······

(prettier/prettier)


[error] 460-460: Delete ··

(prettier/prettier)


[error] 461-461: Delete ··

(prettier/prettier)


[error] 462-462: Delete ··

(prettier/prettier)


[error] 463-463: Delete ··

(prettier/prettier)


[error] 464-464: Delete ······

(prettier/prettier)


[error] 465-465: Delete ··

(prettier/prettier)


[error] 466-466: Replace ······ with ····

(prettier/prettier)


[error] 467-467: Replace ········mock·=>·mock.request.query·===·CANCEL_MEMBERSHIP_REQUEST with ······(mock)·=>·mock.request.query·===·CANCEL_MEMBERSHIP_REQUEST,

(prettier/prettier)


[error] 468-468: Delete ··

(prettier/prettier)


[error] 469-469: Delete ··

(prettier/prettier)


[error] 469-469: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 469-469: Matchers must be called to assert

(jest/valid-expect)

🪛 GitHub Actions: PR Workflow

[warning] Code style issues found. File needs to be formatted using Prettier.




it('should handle withdrawal attempt with no userId', async () => {
// Mock getItem to return null to simulate no userId
mockGetItem.mockReturnValue(null);

const props = {
...defaultProps,
membershipRequestStatus: 'pending',
membershipRequests: [{ _id: 'requestId', user: { _id: 'mockUserId' } }],
};

render(
<TestWrapper mocks={successMocks}>
<OrganizationCard {...props} isJoined={false} />
</TestWrapper>
);

const withdrawButton = screen.getByTestId('withdrawBtn');
await fireEvent.click(withdrawButton);

await waitFor(() => {
expect(toast.error).toHaveBeenCalledWith('MembershipRequestNotFound');
expect(toast.error).toHaveBeenCalledWith('UserIdNotFound');
});

// Verify that the cancelMembershipRequest mutation was not called
expect(
successMocks.find(
mock => mock.request.query === CANCEL_MEMBERSHIP_REQUEST
)?.result
).not.toHaveBeenCalled;
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

Fix assertion syntax and use proper translation key for error message

The test has two issues that need to be addressed:

  1. Incorrect assertion syntax (missing parentheses)
  2. Using non-existent translation key 'UserIdNotFound'
      expect(
        successMocks.find(
          (mock) => mock.request.query === CANCEL_MEMBERSHIP_REQUEST
        )?.result
-     ).not.toHaveBeenCalled;
+     ).not.toHaveBeenCalled();

     await waitFor(() => {
-      expect(toast.error).toHaveBeenCalledWith('UserIdNotFound');
+      expect(toast.error).toHaveBeenCalledWith('notFound');
     });

Consider using an existing translation key like 'notFound' or add a new specific translation key for this error case in the translation files.

🔗 Analysis chain

Fix assertion syntax and verify error message translation.

The test has multiple issues that need to be addressed:

  1. Incorrect assertion syntax
  2. The error message 'UserIdNotFound' might not be defined in the translations
      expect(
        successMocks.find(
-         mock => mock.request.query === CANCEL_MEMBERSHIP_REQUEST
+         (mock) => mock.request.query === CANCEL_MEMBERSHIP_REQUEST,
        )?.result
-     ).not.toHaveBeenCalled;
+     ).not.toHaveBeenCalled();

Let's verify if the error message is defined in the translations:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for 'UserIdNotFound' in translation files
rg -l "UserIdNotFound" --type json

Length of output: 34


Script:

#!/bin/bash
# First, let's find all potential translation/locale files
echo "=== Searching for translation files ==="
fd -e json -e yaml -e yml . | grep -iE 'translation|locale|i18n|lang'

echo -e "\n=== Searching for similar message patterns ==="
# Search for various forms of the message
rg -i "userid.*not.*found|user.*id.*not.*found|user_id_not_found" --type json --type ts --type tsx

echo -e "\n=== Looking for message constants ==="
# Search for message definitions in TypeScript/JavaScript files
ast-grep --pattern 'const $_ = $$$"UserIdNotFound"$$$'

Length of output: 1021


Script:

#!/bin/bash
# Check content of error translation files
echo "=== Content of English error translations ==="
cat public/locales/en/errors.json

echo -e "\n=== Searching for any user-related error messages ==="
rg -i "user" public/locales/en/errors.json

Length of output: 1245

🧰 Tools
🪛 ESLint

[error] 477-477: Delete ····

(prettier/prettier)


[error] 483-483: Delete ····

(prettier/prettier)


[error] 487-487: Insert ,

(prettier/prettier)


[error] 489-489: Delete ····

(prettier/prettier)


[error] 492-492: Delete ····

(prettier/prettier)


[error] 496-496: Delete ····

(prettier/prettier)


[error] 498-502: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 500-500: Replace mock·=>·mock.request.query·===·CANCEL_MEMBERSHIP_REQUEST with (mock)·=>·mock.request.query·===·CANCEL_MEMBERSHIP_REQUEST,

(prettier/prettier)


[error] 501-501: Insert ,

(prettier/prettier)


[error] 502-502: Matchers must be called to assert

(jest/valid-expect)

🪛 GitHub Actions: PR Workflow

[warning] Code style issues found. File needs to be formatted using Prettier.

});
});
Loading