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

Updated User and Admin portal with file uploads (GSoC 24) #2408

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from

Conversation

chandel-aman
Copy link
Contributor

@chandel-aman chandel-aman commented Nov 4, 2024

What kind of change does this PR introduce?

Feature

Issue Number:

Fixes #2400

Did you add tests for your changes?

Yes

Summary

This PR brings about these changes:

  • Updated user and admin post feed management.
    • Performing CRUD operations using REST APIs for file uploads.
    • Updated user feed to show the post preview and handle media correctly.
    • Allowing user to upload both image and video in their post + added editing of media for the user.
  • Updates the existing queries and mutation to use file for uploaded media.
  • Unit tests for all the changes + updated mocks.

Does this PR introduce a breaking change?

No

Other information

GSoC'24 project: Improved files uploads and security.

Have you read the contributing guide?

Yes

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced error handling with new user feedback messages for post creation and management across multiple languages.
    • Improved media handling in various components, allowing for better file uploads and previews.
    • Added structured GraphQL queries for funds and campaigns to enhance data retrieval capabilities.
  • Bug Fixes

    • Fixed issues related to post creation failures and improved error messaging.
  • Refactor

    • Transitioned from GraphQL mutations to REST API calls for post creation, streamlining data handling.
    • Consolidated media file handling into a single state for better management.
    • Updated the handling of organization data and error management in the dashboard for improved user experience.
  • Tests

    • Expanded test coverage for components, including new tests for media handling and error scenarios.
    • Improved assertions in tests for toast notifications to enhance clarity and accuracy.
  • Style

    • Updated styles for improved layout and responsiveness, particularly for media previews and buttons.

…nt in post and user feeds

- Added edit post functionality for user feed
- Implemented media preview for user post creation
- Updated delete post feature for users
- Enhanced admin post feed with post creation, update, and delete options
- Integrated new APIs for file upload and management across user and admin feeds
Copy link

coderabbitai bot commented Nov 4, 2024

Walkthrough

This pull request includes several significant changes across multiple files. Key modifications involve the removal of the .env.example file, updates to the pre-commit hook to include linting checks, and enhancements to translation files for improved error handling in various languages. Additionally, the OrgPostCard and PostCard components have been refactored to streamline media handling and improve state management. The OrgPost component has transitioned from GraphQL to REST for post creation, and various interfaces have been updated to reflect these changes. Testing suites have also been enhanced for better coverage and clarity.

Changes

File Path Change Summary
.env.example Deleted; previously served as a template for environment variable configuration.
.husky/pre-commit Uncommented the npm run lint:fix command to enforce linting during pre-commit checks.
public/locales/en/translation.json Added new translation keys for error messages related to post creation and updates.
public/locales/fr/translation.json Similar updates for French translations, adding new keys for error messages.
public/locales/hi/translation.json Added new keys for Hindi translations related to post management errors.
public/locales/sp/translation.json Similar updates for Spanish translations, enhancing error messaging.
public/locales/zh/translation.json Added new keys for Chinese translations related to post management errors.
src/GraphQl/Queries/OrganizationQueries.ts Updated ORGANIZATION_POST_LIST query to replace imageUrl and videoUrl with a new file object.
src/components/OrgPostCard/OrgPostCard.test.tsx Enhanced tests for pinning posts, error handling, and rendering without data.
src/components/OrgPostCard/OrgPostCard.tsx Refactored to consolidate media handling and improve error handling in post updates.
src/components/UserPortal/CommentCard/CommentCard.module.css Added styles for creator images.
src/components/UserPortal/CommentCard/CommentCard.tsx Updated interface to include optional creator image property.
src/components/UserPortal/PostCard/PostCard.module.css Modified styles for layout and media handling.
src/components/UserPortal/PostCard/PostCard.test.tsx Enhanced tests for media handling and edit functionality.
src/components/UserPortal/PostCard/PostCard.tsx Significant updates to support media uploads and improve rendering logic.
src/components/UserPortal/PromotedPost/PromotedPost.tsx Added data-testid attribute for testing.
src/components/UserPortal/StartPostModal/StartPostModal.module.css Renamed and updated styles for media previews.
src/components/UserPortal/StartPostModal/StartPostModal.test.tsx Enhanced tests with new mocks and scenarios.
src/components/UserPortal/StartPostModal/StartPostModal.tsx Refactored state management and media handling logic.
src/screens/OrgPost/OrgPost.test.tsx Updated testing framework and mock implementations for better coverage.
src/screens/OrgPost/OrgPost.tsx Transitioned to REST API for post creation, updated state management and error handling.
src/screens/OrganizationDashboard/OrganizationDashboardMocks.ts Updated mock data structure for posts.
src/screens/UserPortal/Posts/Posts.module.css Modified styles for the start post button.
src/screens/UserPortal/Posts/Posts.test.tsx Enhanced tests with new mock data structures and improved coverage.
src/screens/UserPortal/Posts/Posts.tsx Updated to support new media file structure and improve layout.
src/utils/interfaces.ts Updated interfaces for posts and media handling.

Assessment against linked issues

Objective Addressed Explanation
Enable full CRUD functionality for admin and user feed management (2400)
Integrate file upload support to handle file creation and deletion (2400)
Revise existing mutations and queries to reflect these updates (2400)

Possibly related PRs

Suggested reviewers

  • pranshugupta54
  • Kevoniat

Poem

🐇 In the meadow where the changes bloom,
With translations bright, dispelling gloom.
Files now upload with ease and grace,
In every post, a welcoming space.
So hop along, let’s celebrate,
For smoother paths, we now create! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Nov 4, 2024

Our Pull Request Approval Process

We have these basic policies to make the approval process smoother for our volunteer team.

Testing Your Code

Please make sure your code passes all tests. Our test code coverage system will fail if these conditions occur:

  1. The overall code coverage drops below the target threshold of the repository
  2. Any file in the pull request has code coverage levels below the repository threshold
  3. Merge conflicts

The process helps maintain the overall reliability of the code base and is a prerequisite for getting your PR approved. Assigned reviewers regularly review the PR queue and tend to focus on PRs that are passing.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

</>
{previewUrl && (
<div className={styles.preview}>
<img src={previewUrl} alt={t('postImage')} />

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
<video controls>
<source src={postformState.postvideo} type="video/mp4" />
<video controls data-testid="videoPreview">
<source src={previewUrl} type="video/mp4" />

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
{mediaPreviewUrl ? (
editedMediaType === 'image' ? (
<img
src={mediaPreviewUrl}

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
/>
) : (
<video
src={mediaPreviewUrl}

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
) : (
<video controls data-testid="videoPreview">
<source
src={previewUrl}

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
<img
src={postformState.addMedia}
src={previewUrl}

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
<source src={postformState.addMedia} type={file.type} />(
{t('tag')})
<source
src={previewUrl}

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
@chandel-aman chandel-aman changed the title Feature/feed file upload Updated User and Admin portal with file uploads (GSoC 24) Nov 4, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 39

🧹 Outside diff range and nitpick comments (53)
.husky/pre-commit (1)

Line range hint 4-11: Consider improving the pre-commit hook's robustness.

The script could benefit from some improvements:

  1. Error handling to prevent commits if any command fails
  2. More selective git staging to avoid accidentally staging unrelated changes

Consider applying these improvements:

 #!/usr/bin/env sh
 . "$(dirname -- "$0")/_/husky.sh"
 
+# Exit on error
+set -e
+
 # npm run format:fix
 npm run lint:fix
 npm run lint-staged
 npm run typecheck
 npm run update:toc
 
-git add .
+# Only stage files that were modified by the hooks
+git diff --name-only | xargs git add
src/components/UserPortal/CommentCard/CommentCard.module.css (1)

17-22: Consider consolidating duplicate image styling.

There appears to be some style duplication between .creator img and .personImage classes, both handling circular images. Consider consolidating these styles into a shared class for better maintainability.

+.circularImage {
+  border-radius: 50%;
+}
+
 .creator img {
   width: 2rem;
   height: 2rem;
-  border-radius: 100%;
+  composes: circularImage;
   object-fit: cover;
 }

 .personImage {
-  border-radius: 50%;
+  composes: circularImage;
   margin-right: 20px;
 }

Also applies to: 24-27

src/components/UserPortal/StartPostModal/StartPostModal.module.css (1)

21-29: Consider enhancing preview container for multiple file support.

The current layout using justify-content: center might not handle multiple file previews optimally. Consider using a grid or flex-wrap layout for better multi-file preview support.

 .preview {
   display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
   position: relative;
   width: 100%;
   margin-top: 10px;
-  justify-content: center;
+  justify-content: flex-start;
   overflow: hidden;
   border-radius: 1rem;
 }
src/components/UserPortal/PromotedPost/PromotedPost.tsx (3)

Line range hint 6-10: Update interface to align with new file handling structure

The interface should be updated to match the new file handling approach mentioned in the PR objectives and summary. The current simple image string property doesn't align with the new media file structure being implemented across the application.

Consider updating the interface:

 interface InterfacePostCardProps {
   id: string;
-  image: string;
+  mediaFile?: {
+    url: string;
+    fileType: string;
+    fileName: string;
+  };
   title: string;
 }

Line range hint 41-43: Add alt text for accessibility

The image element lacks an alt attribute, which is essential for accessibility.

Apply this change:

- <img src={props.image} className={styles.imageContainer} />
+ <img 
+   src={props.image} 
+   className={styles.imageContainer} 
+   alt={`Promoted content: ${props.title}`}
+ />

Line range hint 36-39: Remove duplicate content display

The title is currently displayed twice - in both Card.Title and Card.Text. This seems redundant and might be confusing to users.

Consider either:

  1. Removing the duplicate Card.Text, or
  2. Adding a separate description field to the interface for meaningful content in Card.Text
src/screens/UserPortal/Posts/Posts.module.css (1)

130-130: Consider documenting the style organization.

Since core styles have been moved out of this module, it would be helpful to add a comment indicating where these styles are now maintained (e.g., shared components, design system, etc.) to help other developers understand the styling structure.

Add a comment like:

+/* Core button styles moved to shared/components/Button.css */
.startPostBtn {
  border-radius: 2rem;
}
src/components/UserPortal/PostCard/PostCard.module.css (1)

182-188: Consider making media width responsive

While combining image and video styles reduces duplication, the fixed width of 20rem might cause issues on smaller screens.

Consider using this more responsive approach:

 .postImage,
 .postVideo {
-  width: 20rem;
+  width: 100%;
+  max-width: 20rem;
   aspect-ratio: 1.5;
   object-fit: cover;
   border-radius: 0;
 }
src/GraphQl/Queries/OrganizationQueries.ts (1)

30-45: Well-structured file object design!

The new file object structure is comprehensive and well-designed for robust media handling. It includes essential metadata, security features (hash), and proper MinIO integration (objectKey).

Consider these operational recommendations:

  1. Ensure proper indexing on _id and hash.value fields for efficient file deduplication.
  2. Implement caching for frequently accessed files using the hash.value as cache key.
  3. Consider implementing a cleanup job for files with status indicating temporary or failed uploads.
src/components/UserPortal/StartPostModal/StartPostModal.test.tsx (3)

24-26: Consider using a more type-safe fetch mock.

The current fetch mock could benefit from TypeScript types to ensure type safety across test cases.

const mockFetch = jest.fn() as jest.MockedFunction<typeof global.fetch>;
global.fetch = mockFetch;

33-63: Consider moving mock data to a separate test utils file.

The mockUserData and mockProps objects could be moved to a separate test utils file (e.g., StartPostModal.test.utils.ts) to improve maintainability and reusability across related test files.


79-296: Improve test organization with describe blocks.

Consider grouping related tests using describe blocks for better organization and readability. For example:

describe('StartPostModal Component', () => {
  // ... beforeEach setup ...

  describe('Post Creation', () => {
    // Tests related to creating posts
    it('should handle successful post creation', ...);
    it('should handle post creation error', ...);
    it('should use default error message when no specific error is returned', ...);
  });

  describe('Media Handling', () => {
    // Tests related to media upload and preview
    it('should display image preview when an image file is selected', ...);
    it('should display video preview when a video file is selected', ...);
    it('should remove preview when the selected file is removed', ...);
  });

  describe('UI Interactions', () => {
    // Tests related to UI state and interactions
    it('should disable post button when post body is empty', ...);
    it('should consider whitespace-only content as empty', ...);
    it('should trigger file input click when Add Media button is clicked', ...);
  });
});
🧰 Tools
🪛 Biome

[error] 222-225: Disallow duplicate setup and teardown hooks.

Disallow beforeEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

src/screens/UserPortal/Posts/Posts.test.tsx (4)

30-153: LGTM! Consider adding TypeScript interfaces for mock data.

The mock data structures are well-organized and comprehensive, properly representing the file upload capabilities. They include all necessary fields for both posts and advertisements.

Consider defining TypeScript interfaces for these mock data structures to improve type safety and maintainability:

interface MockFile {
  _id: string;
  fileName: string;
  mimeType: string;
  size: number;
  hash: {
    value: string;
    algorithm: string;
  };
  uri: string;
  metadata: {
    objectKey: string;
  };
  visibility: string;
  status: string;
}

interface MockPost {
  _id: string;
  title: string;
  text: string;
  file: MockFile | null;
  // ... other fields
}

251-259: Consider extracting timeout values to constants.

The timeout value of 2000ms is used multiple times throughout the tests. Consider extracting it to a constant for better maintainability.

const DEFAULT_TIMEOUT = 2000;

await waitFor(
  () => {
    expect(
      screen.getByText(/Start a post, with text or media/i),
    ).toBeInTheDocument();
  },
  { timeout: DEFAULT_TIMEOUT },
);

Line range hint 219-226: Consider using wait function consistently.

The test file has both wait and waitFor functions. Consider standardizing on waitFor as it provides better error messages and is the recommended approach in React Testing Library.


408-426: LGTM! Consider adding more edge cases.

The redirect test is good, but consider adding tests for:

  • Invalid organization IDs
  • Permission-related redirects
  • Session timeout scenarios
public/locales/en/translation.json (4)

688-690: Fix spelling in error message keys.

The error messages look good, but there's a spelling inconsistency in the key names.

Apply this diff to maintain consistent naming:

-    "postCreationFailed":"Failed to create the post."
+    "postCreationFailed":"Post creation failed."

730-733: Consider consolidating duplicate error messages.

The error messages noTitleAndDescription and postCreationFailed are duplicated from the orgPost section. Consider using a common section for shared error messages.

Consider moving these messages to a common errors or common section to promote reuse and easier maintenance.


1048-1052: Fix spelling in success message.

There's a spelling error in the success message key.

Apply this diff to fix the spelling:

-    "deletionSuccessfull": "Successfully deleted the post.",
+    "deletionSuccessful": "Successfully deleted the post."

Also, consider using consistent message formats. Some messages start with "Successfully" while others use "Failed to".


1070-1072: Ensure consistent error message structure.

The error messages in this section should follow the same pattern as other sections for consistency.

Consider applying this diff to maintain consistency:

-    "noTitleAndDescription": "Can not create a post with an empty body.",
+    "noTitleAndDescription": "Title and description cannot be empty.",
public/locales/fr/translation.json (1)

1048-1052: Consider improving the date placeholder translation.

The translations are accurate, but the date placeholder could be more explicit for better localization.

Consider updating line 1048:

-    "postedOn": "Publié le {{date}}",
+    "postedOn": "Publié le {{date, DD/MM/YYYY}}",

This makes the expected date format more explicit for French localization.

src/components/UserPortal/StartPostModal/StartPostModal.tsx (1)

201-201: Allow posting media without text content

Currently, the "Post" button is disabled when the text input is empty, even if a media file is selected. Consider allowing users to post media without requiring text input.

Apply this diff:

- disabled={!postformState.postinfo.trim()}
+ disabled={!postformState.postinfo.trim() && !postformState.mediaFile}
src/screens/UserPortal/Posts/Posts.tsx (7)

Line range hint 62-62: Specify Type for 'posts' State Variable

Consider specifying the type for the posts state variable in useState to enhance type safety and prevent potential runtime errors.

Apply this diff to specify the type:

-const [posts, setPosts] = useState([]);
+const [posts, setPosts] = useState<{ node: InterfacePostNode }[]>([]);

Line range hint 63-63: Specify Type for 'pinnedPosts' State Variable

Similarly, specify the type for the pinnedPosts state variable to maintain consistency and improve type safety.

Apply this diff:

-const [pinnedPosts, setPinnedPosts] = useState([]);
+const [pinnedPosts, setPinnedPosts] = useState<{ node: InterfacePostNode }[]>([]);

212-212: Remove Debugging Statement 'console.log'

The console.log statement should be removed to prevent unnecessary logging in production environments.

Apply this diff:

-          console.log(promotedPostsData.organizations[0].advertisements);

333-336: Internationalize Button Label

The button label "Start a post, with text or media" is hardcoded. For consistency and to support multiple languages, consider using the translation function t().

Apply this diff:

-          Start a post, with text or media
+          {t('startPostWithMedia')}

Also, add the key startPostWithMedia to your translation files.


345-350: Move Inline Styles to CSS Module

The inline styles applied to the div can be moved to the CSS module to enhance readability and maintainability.

In your Posts.module.css, add:

.carouselContainer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 1rem;
}

Then, apply the class:

-    <div
-      style={{
-        justifyContent: `space-between`,
-        alignItems: `center`,
-        marginTop: `1rem`,
-      }}
-    >
+    <div className={styles.carouselContainer}>

Line range hint 98-100: Ensure Safe Access to Nested Data in 'useEffect'

When accessing data.organizations[0].posts.edges, there is a risk of encountering TypeError if any property is undefined. Consider adding additional checks or using optional chaining.

Apply this diff:

-  if (data) {
+  if (data?.organizations?.[0]?.posts?.edges) {

Alternatively, handle the case where organizations array might be empty.

if (data?.organizations?.length) {
  setPosts(data.organizations[0].posts.edges);
}

Line range hint 157-159: Validate 'userId' Before Using in Query

userId may be null, which can cause unexpected behavior in the useQuery hook for USER_DETAILS. Ensure userId is valid before making the query.

Apply this diff:

-const { data: userData } = useQuery(USER_DETAILS, {
-  variables: { id: userId },
-});
+const { data: userData } = useQuery(USER_DETAILS, {
+  variables: { id: userId },
+  skip: !userId,
+});

This way, the query will be skipped if userId is null.

src/utils/interfaces.ts (2)

186-201: Define a dedicated interface for the file object

The file object within InterfaceQueryOrganizationPostListItem is complex. Defining a separate interface enhances readability and maintainability.

Suggested refactor:

+interface InterfaceFile {
+  _id: string;
+  fileName: string;
+  mimeType: string;
+  size: number;
+  hash: {
+    value: string;
+    algorithm: string;
+  };
+  uri: string;
+  metadata: {
+    objectKey: string;
+  };
+  visibility: FileVisibility;
+  status: FileStatus;
+}

 export interface InterfaceQueryOrganizationPostListItem {
   posts: {
     edges: {
       node: {
         _id: string;
         title: string;
         text: string;
-        file: {
-          // file properties
-        };
+        file: InterfaceFile;
         creator: {
           _id: string;
           firstName: string;
           lastName: string;
           email: string;
         };
         // other properties
       };
       cursor: string;
     }[];
     pageInfo: {
       // page info properties
     };
     totalCount: number;
   };
 }

199-200: Use enums for visibility and status properties

Using string literals for visibility and status can lead to typos and inconsistent values. Defining enums improves type safety and code clarity.

Define enums:

enum FileVisibility {
  PUBLIC = 'PUBLIC',
  PRIVATE = 'PRIVATE',
  // add other visibility options if necessary
}

enum FileStatus {
  ACTIVE = 'ACTIVE',
  INACTIVE = 'INACTIVE',
  // add other status options if necessary
}

Update the InterfaceFile:

 interface InterfaceFile {
   _id: string;
   fileName: string;
   mimeType: string;
   size: number;
   hash: {
     value: string;
     algorithm: string;
   };
   uri: string;
   metadata: {
     objectKey: string;
   };
-  visibility: string;
-  status: string;
+  visibility: FileVisibility;
+  status: FileStatus;
 }
src/components/OrgPostCard/OrgPostCard.tsx (2)

237-237: Remove console.log statement from production code

The console.log(data); statement may expose sensitive information in the console. Consider removing it to avoid potential security risks.

Apply this diff:

      const data = await response.json();
-     console.log(data);
      if (!response.ok) {

264-266: Revoke object URLs to prevent memory leaks

When using URL.createObjectURL, it's important to revoke the object URL when it's no longer needed to prevent memory leaks. Consider revoking the previous URL before setting a new one.

Here's how you could modify the code:

if (file) {
+   if (previewUrl) {
+     URL.revokeObjectURL(previewUrl);
+   }
    const newPreviewUrl = URL.createObjectURL(file);
    setPreviewUrl(newPreviewUrl);
}

Also, ensure to revoke the object URL when the component unmounts.

src/screens/OrgPost/OrgPost.tsx (2)

138-139: Remove commented-out code to maintain code cleanliness

The commented-out code for useMutation is no longer necessary due to the transition to using REST APIs. Keeping such code can clutter the codebase and cause confusion.

Apply this diff to remove the unused code:

- // const [create, { loading: createPostLoading }] =
- //   useMutation(CREATE_POST_MUTATION);

248-250: Revoke object URLs immediately after usage to free up memory

While you have a cleanup function in the useEffect, it's good practice to revoke the object URL right after the file is no longer needed to prevent memory leaks.

Consider updating the clearMedia function to revoke the URL:

const clearMedia = (): void => {
  setPostFormState((prev) => ({ ...prev, mediaFile: null }));
  if (previewUrl) {
+   URL.revokeObjectURL(previewUrl);
    setPreviewUrl(null);
  }

  const fileInput = document.getElementById('mediaFile') as HTMLInputElement;
  if (fileInput) {
    fileInput.value = '';
  }
};

Also applies to: 221-226

src/components/UserPortal/PostCard/PostCard.tsx (5)

48-48: Ensure optional handling of the new 'image' property in 'creator' object

The addition of the image property to the creator object in InterfaceCommentCardProps is appropriate. Please ensure that all components consuming this interface, such as CommentCard, can handle cases where creator.image may be null or undefined to prevent potential runtime errors.


289-289: Correct typo in success message

There is a typo in the toast success message. It should be "deletionSuccessful" instead of "deletionSuccessfull".

Apply this diff to fix the typo:

- toast.success(t('deletionSuccessfull'));
+ toast.success(t('deletionSuccessful'));

306-306: Update error message for unsupported media types

When an unsupported file type is selected, the error message "noMediaSelected" might confuse users. Consider updating it to inform users that the selected file type is not supported.

Apply this diff to improve user feedback:

- toast.error(t('noMediaSelected'));
+ toast.error(t('unsupportedMediaType'));

Ensure that the translation key unsupportedMediaType is added to your localization files with an appropriate message.


311-315: Revoke object URLs to prevent memory leaks

When using URL.createObjectURL, it's important to revoke the object URL after usage to prevent memory leaks. Consider revoking the URL when the component unmounts or when the media is cleared.

You can modify your code as follows:

const previewUrl = URL.createObjectURL(file);
setMediaPreviewUrl(previewUrl);

+ // Clean up the object URL when it's no longer needed
+ useEffect(() => {
+   return () => {
+     if (mediaPreviewUrl) {
+       URL.revokeObjectURL(mediaPreviewUrl);
+     }
+   };
+ }, [mediaPreviewUrl]);

Alternatively, revoke the URL in the handleClearMedia function before setting mediaPreviewUrl to null.


317-324: Revoke object URL in handleClearMedia

In the handleClearMedia function, ensure that you revoke the object URL to free up memory resources.

Update the function as follows:

const handleClearMedia = () => {
+ if (mediaPreviewUrl) {
+   URL.revokeObjectURL(mediaPreviewUrl);
+ }
  setEditedMedia(null);
  setEditedMediaType(null);
  setMediaPreviewUrl(null);
  if (fileInputRef.current) {
    fileInputRef.current.value = '';
  }
};
src/components/UserPortal/PostCard/PostCard.test.tsx (5)

666-668: Simplify condition using optional chaining

You can simplify the condition by using optional chaining for better readability.

Apply this diff to simplify the code:

-if (global.URL && global.URL.createObjectURL) {
+if (global.URL?.createObjectURL) {
   global.URL.createObjectURL = jest.fn();
 }
🧰 Tools
🪛 Biome

[error] 666-666: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


753-753: Simplify redundant logical expression

At line 753, the expression 'Update failed' || 'Failed to update the post' is redundant because the logical OR will always return the first truthy value.

Apply this diff to simplify the expression:

-json: () => Promise.resolve({ error: 'Update failed' || 'Failed to update the post'}),
+json: () => Promise.resolve({ error: 'Update failed' }),

782-785: Default error handling improvement

In the failed update test case without an error message (lines 782-785), consider providing a default error message to handle cases where the response doesn't include one.

Apply this diff to improve error handling:

-        expect(toast.error).toHaveBeenCalledWith('Update failed');
+        expect(toast.error).toHaveBeenCalledWith('Update failed' || 'An unexpected error occurred');

Ensure that your component displays a generic error message if none is provided by the API.


882-882: Typo in error message

There's a typo in the error message: "Please select and image or video file." It should be "Please select an image or video file."

Apply this diff to correct the typo:

-expect(toast.error).toHaveBeenCalledWith('Please select and image or video file.');
+expect(toast.error).toHaveBeenCalledWith('Please select an image or video file.');

Ensure the same correction is made in the component code where this message originates.


902-904: Simplify condition using optional chaining

Similar to the previous instance, you can use optional chaining for cleaner code.

Apply this diff:

-if (global.URL && global.URL.createObjectURL) {
+if (global.URL?.createObjectURL) {
   global.URL.createObjectURL = jest.fn();
 }
🧰 Tools
🪛 Biome

[error] 902-902: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/screens/OrgPost/OrgPost.test.tsx (5)

1049-1051: Use userEvent.upload instead of fireEvent.change for file uploads

To simulate user interactions more accurately when uploading files, consider using userEvent.upload instead of fireEvent.change. This aligns with best practices for testing user interactions.

Apply this diff to the affected lines:

- fireEvent.change(fileInput, { target: { files: [file] } });
+ await userEvent.upload(fileInput, file);

Also applies to: 1126-1128, 1175-1177


568-577: Avoid redeclaring renderWithProviders inside the describe block

Defining renderWithProviders again inside the describe block (lines 568-577) is unnecessary since it's already defined globally. Remove the inner declaration to prevent confusion.

Apply this diff:

- // Remove the redeclared function inside the describe block
- const renderWithProviders = () => {
-   return render(
-     <MockedProvider mocks={generateMocks()} addTypename={false}>
-       <BrowserRouter>
-         <I18nextProvider i18n={i18nForTest}>
-           <OrgPost />
-         </I18nextProvider>
-       </BrowserRouter>
-     </MockedProvider>,
-   );
- };

591-806: Optimize the use of beforeEach for common setup

The beforeEach block (lines 579-589) sets up mocks for global.fetch and global.URL and is duplicated before some test suites. Consider moving this setup to a higher scope to avoid repetition.


591-806: Avoid directly modifying global objects

Directly assigning to global.fetch and global.URL in tests can lead to side effects. Consider using libraries like jest-fetch-mock to mock fetch or utilize jest.spyOn for more controlled mocking.


68-120: Keep mock data consistent and DRY

The mock data for posts is defined multiple times with similar structures (lines 68-120 and lines 222-326). Consider refactoring the mock data to a separate module or utility function to avoid duplication and ensure consistency.

Also applies to: 222-326

src/components/OrgPostCard/OrgPostCard.test.tsx (3)

21-21: Remove commented-out import statement

The import of convertToBase64 is commented out at line 21 but re-imported at line 24. Consider removing the commented-out import to clean up the code.

Apply this diff to remove the commented-out import:

- // import convertToBase64 from 'utils/convertToBase64';

Also applies to: 24-24


105-115: Consolidate props definitions to reduce duplication

Multiple props objects (props, props2, mockProps) with similar structures are defined throughout the tests. Consider creating a single base props object and extending it as needed to reduce code duplication and improve maintainability.

Example:

const baseProps = {
  id: '12',
  postID: '123',
  postTitle: 'Event Info',
  postInfo: 'Time change',
  postAuthor: 'John Doe',
  postPhoto: 'test.png',
  postVideo: 'test.mp4',
  pinned: false,
  refetch: jest.fn(),
};

const mockProps = { ...baseProps, postTitle: 'Test Post', postInfo: 'Test Info', postAuthor: 'Test Author', postPhoto: 'test.jpg', postVideo: null };

const props2 = { ...baseProps, id: '', pinned: true };

Also applies to: 148-148, 272-272, 324-324, 403-403


993-1007: Handle API responses and errors consistently

In the tests for form submissions, ensure that API responses and errors are handled consistently. Mock the fetch API to return appropriate responses and verify that the component behaves as expected in success and failure scenarios.

Also applies to: 1031-1039, 1062-1071, 1009-1041

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2fffd90 and e4fa134.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (26)
  • .env.example (0 hunks)
  • .husky/pre-commit (1 hunks)
  • public/locales/en/translation.json (4 hunks)
  • public/locales/fr/translation.json (4 hunks)
  • public/locales/hi/translation.json (4 hunks)
  • public/locales/sp/translation.json (4 hunks)
  • public/locales/zh/translation.json (4 hunks)
  • src/GraphQl/Queries/OrganizationQueries.ts (2 hunks)
  • src/components/OrgPostCard/OrgPostCard.test.tsx (15 hunks)
  • src/components/OrgPostCard/OrgPostCard.tsx (17 hunks)
  • src/components/UserPortal/CommentCard/CommentCard.module.css (1 hunks)
  • src/components/UserPortal/CommentCard/CommentCard.tsx (2 hunks)
  • src/components/UserPortal/PostCard/PostCard.module.css (3 hunks)
  • src/components/UserPortal/PostCard/PostCard.test.tsx (23 hunks)
  • src/components/UserPortal/PostCard/PostCard.tsx (13 hunks)
  • src/components/UserPortal/PromotedPost/PromotedPost.tsx (1 hunks)
  • src/components/UserPortal/StartPostModal/StartPostModal.module.css (1 hunks)
  • src/components/UserPortal/StartPostModal/StartPostModal.test.tsx (1 hunks)
  • src/components/UserPortal/StartPostModal/StartPostModal.tsx (3 hunks)
  • src/screens/OrgPost/OrgPost.test.tsx (2 hunks)
  • src/screens/OrgPost/OrgPost.tsx (14 hunks)
  • src/screens/OrganizationDashboard/OrganizationDashboardMocks.ts (1 hunks)
  • src/screens/UserPortal/Posts/Posts.module.css (1 hunks)
  • src/screens/UserPortal/Posts/Posts.test.tsx (5 hunks)
  • src/screens/UserPortal/Posts/Posts.tsx (8 hunks)
  • src/utils/interfaces.ts (4 hunks)
💤 Files with no reviewable changes (1)
  • .env.example
🧰 Additional context used
🪛 GitHub Check: CodeQL
src/components/OrgPostCard/OrgPostCard.tsx

[warning] 557-557: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.


[warning] 585-585: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

src/components/UserPortal/PostCard/PostCard.tsx

[warning] 606-606: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.


[warning] 613-613: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

src/components/UserPortal/StartPostModal/StartPostModal.tsx

[warning] 170-170: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

src/screens/OrgPost/OrgPost.tsx

[warning] 570-570: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.


[warning] 577-577: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

🪛 Biome
src/components/UserPortal/PostCard/PostCard.test.tsx

[error] 666-666: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 902-902: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/components/UserPortal/StartPostModal/StartPostModal.test.tsx

[error] 222-225: Disallow duplicate setup and teardown hooks.

Disallow beforeEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

🔇 Additional comments (29)
src/components/UserPortal/CommentCard/CommentCard.module.css (1)

17-22: LGTM! Clean and well-structured CSS for creator images.

The new CSS rule appropriately handles avatar styling with proper sizing and aspect ratio maintenance.

src/components/UserPortal/PromotedPost/PromotedPost.tsx (1)

28-28: LGTM: Test ID addition

The addition of the data-testid attribute is a good improvement for testing capabilities.

src/screens/UserPortal/Posts/Posts.module.css (1)

130-130: Verify the migration of removed styles.

The change from 25px to 2rem for border-radius is a good improvement for responsive design. However, several core styles (background, border, color) were removed from .startPostBtn.

Let's verify where these styles are maintained now:

✅ Verification successful

Let me gather more information about the styling changes by searching for related files and components.


Let me try one more search to find the Posts component and its styling:


Based on the gathered information, I can now generate the final response:

The removed styles are handled by Bootstrap's Button component

The .startPostBtn class now only needs the border-radius property because:

  1. The button is rendered using Bootstrap's Button component (<Button variant="light" className={styles.startPostBtn}.../>)
  2. Core styles are provided by Bootstrap's btn and btn-light classes
  3. Additional styling is applied through Bootstrap utility classes: w-100, text-start, text-muted, py-3, d-flex, align-items-center, gap-2, and border

The change from 25px to 2rem for border-radius is appropriate as it maintains consistency with the responsive design approach used throughout the component.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for potential style definitions for startPostBtn
rg -l "startPostBtn" --type css
rg -l "start-post-btn" --type css

# Search for potential component styling
ast-grep --pattern 'styled.$_`
  $$$
  border-radius: $_;
  $$$
`'

Length of output: 180


Script:

#!/bin/bash
# Search for StartPostBtn component
rg -l "StartPostBtn" --type-add 'jsx:*.{jsx,tsx}'  --type jsx

# Look for styled-components usage in Posts directory
fd . src/screens/UserPortal/Posts -e tsx -e jsx --exec rg -l "styled\."

# Check for common style imports in Posts.tsx/jsx
fd Posts.tsx Posts.jsx -x rg "import.*style" {}

# Look for any global styles or theme files
fd "theme|style|design" -e ts -e js -e css

Length of output: 360


Script:

#!/bin/bash
# Find and examine Posts component file
fd Posts -e tsx -e jsx -x cat {}

# Look for any MUI or other UI library imports that might handle styling
fd Posts -e tsx -e jsx -x rg "import.*@mui|import.*@material" {}

# Check for any style-related imports in Posts directory
fd . src/screens/UserPortal/Posts -e tsx -e jsx -x rg "import.*css|import.*style" {}

Length of output: 22895

src/components/UserPortal/PostCard/PostCard.module.css (2)

22-24: LGTM! Improved creator section styling

The changes to creator section styling improve consistency by:

  • Maintaining vertical alignment of elements
  • Using margin instead of padding for spacing
  • Adding proper circular avatar styling

Also applies to: 35-39


190-196: LGTM! Well-structured media container

The new media container provides proper centering and overflow handling, following good CSS practices.

src/components/UserPortal/CommentCard/CommentCard.tsx (1)

20-20: LGTM! Well-structured interface change.

The addition of the optional image property to the creator object is clean and maintains backward compatibility.

public/locales/zh/translation.json (2)

1048-1052: Verify date format placeholder usage.

The translation for "postedOn" uses a placeholder {{date}} which is good practice for dynamic content. This ensures proper date formatting based on locale settings.


688-690: 🛠️ Refactor suggestion

Review translations for consistency across sections.

The error messages in the "orgPost" section have been translated appropriately, but they appear to be duplicated in other sections with slightly different translations. Consider using consistent translations for the same error messages across all sections.

Standardize these translations across all sections:

  "orgPost": {
-   "noTitleAndDescription": "无法创建内容为空的帖子。",
-   "postCreationFailed": "创建帖子失败。"
+   "noTitleAndDescription": "标题和描述不能为空。",
+   "postCreationFailed": "创建帖子失败。"
  }

This matches the translation used in the "orgPostCard" section (line 731) for better consistency.

Likely invalid or redundant comment.

public/locales/hi/translation.json (1)

1048-1052: LGTM! New translations for post card actions are clear and meaningful.

The added translations for post date formatting, update/deletion status, media selection, and comments are well-written and grammatically correct in Hindi.

public/locales/fr/translation.json (3)

688-690: LGTM! Error message translations are clear and consistent.

The added translations for post creation error messages are well-structured and maintain consistency with the application's error handling patterns.


730-733: LGTM! Post-related message translations are accurate.

The translations for API unavailability, post creation errors, and UI elements are properly formatted and maintain consistency with the rest of the application.


1070-1072: LGTM! Post creation message translations are consistent.

The translations for post creation success/failure messages maintain consistency with similar messages throughout the application.

public/locales/sp/translation.json (3)

689-691: LGTM! Clear and consistent error message translations.

The Spanish translations for navigation and error messages in the orgPost section are well-structured and meaningful.


1049-1053: LGTM! Well-formatted post-related message translations.

The translations for post-related messages are clear and properly use the date placeholder {{date}}. The error and status messages are meaningful in Spanish.


1071-1073: LGTM! Consistent error message translations.

The translations for post creation error messages maintain consistency with similar messages in other sections of the application.

src/components/UserPortal/StartPostModal/StartPostModal.tsx (1)

170-170: Ensure safe rendering of user-generated media

Ensure that rendering user-uploaded media does not introduce security vulnerabilities. Verify that previewUrl is safely handled and that user-supplied files cannot lead to XSS attacks.

While URL.createObjectURL is generally safe, it's important to validate the media files. Consider implementing client-side validation for file types and sizes.

🧰 Tools
🪛 GitHub Check: CodeQL

[warning] 170-170: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

src/screens/UserPortal/Posts/Posts.tsx (2)

367-375: Ensure Unique Keys in List Rendering

While rendering adContent, ensure that each child in a list has a unique key prop. Verify that post._id is unique across all advertisements.

If post._id is not guaranteed to be unique, consider using a combination of indexes or another unique identifier.


395-405: Ensure Proper Key Usage in Posts Mapping

When mapping over posts, ensure that each div has a unique key prop to prevent rendering issues.

Confirm that node._id is unique for each post.

-<div data-testid="post-card" key={node._id}>
+<div data-testid="post-card" key={`post-${node._id}`}>

Optionally, prefix keys to reduce the risk of key collisions.

src/screens/OrganizationDashboard/OrganizationDashboardMocks.ts (6)

160-161: Confirm handling of null file values in posts

In the second post, the file field is set to null. Ensure that the application correctly handles posts without attached files and that setting file to null is acceptable according to the schema. This prevents potential runtime errors when accessing properties of a null file object.

Check that the GraphQL schema allows file to be null and that UI components gracefully handle posts without files.


167-167: Ensure the image field in creator aligns with the schema

As with the previous creator object, verify that including the image field is consistent with the application's User type definition.


180-181: Validate pagination cursors for accuracy

The startCursor and endCursor values have been updated. Confirm that these cursors accurately represent the first and last items in the edges array. This ensures that pagination functions correctly during testing.

Ensure that startCursor matches the cursor of the first post and endCursor matches that of the last post.


185-185: Confirm totalCount reflects the actual number of posts

The totalCount is set to 2, which should correspond to the number of posts provided in the edges array. Verifying this ensures consistency and correctness in pagination and total count displays.


116-116: Ensure the image field in creator aligns with the schema

The creator object now includes an image field. Verify that the User type in the application schema includes this image property. This ensures the frontend can display user images without encountering undefined fields.

Run the following script to check if the User type includes an image field:

#!/bin/bash
# Description: Verify if the `image` field exists in the `User` type.

# Test: Search for the `image` field in `User` type. Expect: Field exists.
ast-grep --pattern $'type User {
  $$$
  image: $_
  $$$
}' --lang graphql schema.graphql

95-110: Verify the file object structure matches the application's schema

The file object added to the post mock data includes fields like _id, fileName, mimeType, size, hash, uri, metadata, visibility, and status. Please ensure that the structure and field names align with the File type defined in the application's GraphQL schema. Consistency is crucial for accurate testing and to prevent potential mismatches during integration.

Run the following script to confirm that the File type in the schema matches the mock data structure:

src/screens/OrgPost/OrgPost.test.tsx (3)

1047-1051: Validate file type and size during file upload tests

The tests simulate uploading files but do not include validations for file type and size, which are important for file upload features. Consider adding assertions to check how the component handles invalid file types or oversized files.

Also applies to: 1125-1129, 1173-1177


591-806: 🛠️ Refactor suggestion

Ensure test cases cover error handling scenarios

While the test cases cover successful operations, they don't include scenarios where the fetch request fails or returns an error. Consider adding tests to verify how the component handles network errors or unsuccessful responses.

Would you like assistance in creating additional test cases for error handling?


54-59: Ensure correct mocking of useLocalstorage

The mock for utils/useLocalstorage returns an object with a getItem method but does not mock other potential methods like setItem. Ensure that all necessary methods are mocked to prevent unexpected errors during testing.

Run the following script to check for usage of setItem:

✅ Verification successful

Based on the comprehensive review of the codebase, I can now generate the final response:

The mock implementation of useLocalstorage is sufficient for this test file

The current mock implementation of useLocalstorage that returns only getItem is appropriate for this test file because:

  1. Looking at OrgPost.tsx, the component only uses getItem from useLocalstorage to retrieve the access token for post creation.
  2. The component doesn't use setItem or any other methods from the hook.
  3. The test scenarios in OrgPost.test.tsx focus on post creation, searching, sorting, and pagination functionality, none of which require storage modification.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if `setItem` from `useLocalstorage` is used in the codebase.

# Test: Search for occurrences of `setItem` in combination with `useLocalstorage`.

rg -A 2 "useLocalstorage.*\.setItem"

Length of output: 38


Script:

#!/bin/bash
# Let's first check the implementation of useLocalstorage to understand its interface
rg -l "useLocalstorage"

# Then examine the actual implementation and usages
ast-grep --pattern 'export $_ function useLocalstorage() { $$$ }'

# Also search for any direct usage of setItem
rg "setItem"

# And check the test file content to understand the context better
cat src/screens/OrgPost/OrgPost.test.tsx

Length of output: 66288


Script:

#!/bin/bash
# Let's check the implementation of useLocalstorage to understand its interface
cat src/utils/useLocalstorage.ts

# And check if there are any usages of setItem in OrgPost.tsx
cat src/screens/OrgPost/OrgPost.tsx

Length of output: 22067

src/components/OrgPostCard/OrgPostCard.test.tsx (2)

117-120: Good practice: Clear mocks before each test

The addition of beforeEach to clear all mocks ensures that each test runs in isolation and prevents side effects from previous tests.


945-975: Ensure form validation handles empty inputs correctly

When testing form submission with empty spaces, verify that the form validation prevents submission and provides user feedback when fields contain only whitespace.

If the form allows submission with empty fields, consider updating the validation logic to trim inputs and check for empty strings.

@@ -2,7 +2,7 @@
. "$(dirname -- "$0")/_/husky.sh"

# npm run format:fix
# npm run lint:fix
npm run lint:fix
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Remove redundant lint:fix command from pre-commit hook

The lint:fix command is redundant as lint-staged already includes ESLint fixing for TypeScript files:

  • lint:fix runs: eslint --fix "**/*.{ts,tsx}"
  • lint-staged runs: eslint --fix on **/*.{ts, tsx, json}

The lint-staged configuration is more comprehensive as it:

  • Handles the same file types as lint:fix
  • Additionally includes JSON files
  • Also runs Prettier formatting
  • Only processes staged files instead of all files
🔗 Analysis chain

Consider potential redundancy with lint-staged.

The addition of npm run lint:fix before lint-staged might be redundant as lint-staged typically handles linting of staged files. Consider verifying if both commands are necessary or if they serve different purposes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the configuration of lint-staged and lint:fix to understand their scope
# Expected: Different configurations or purposes for each command

echo "Checking package.json for lint:fix and lint-staged configurations:"
cat package.json | jq '.scripts["lint:fix"], ."lint-staged"'

Length of output: 350

Comment on lines +30 to +33
.preview img {
width: 400px;
height: auto;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve image preview responsiveness.

The fixed width of 400px might cause overflow on smaller screens. Consider using max-width and relative units for better responsiveness.

 .preview img {
-  width: 400px;
+  width: 100%;
+  max-width: 400px;
   height: auto;
 }
📝 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
.preview img {
width: 400px;
height: auto;
}
.preview img {
width: 100%;
max-width: 400px;
height: auto;
}

Comment on lines +34 to +36
.preview video {
width: 400px;
height: auto;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance video preview styling and functionality.

  1. Apply the same responsive improvements as images
  2. Consider adding styles for video controls and play overlay
 .preview video {
-  width: 400px;
+  width: 100%;
+  max-width: 400px;
   height: auto;
+  background-color: #000;
+  object-fit: contain;
 }
+
+.preview video::-webkit-media-controls {
+  background-color: rgba(0, 0, 0, 0.5);
+}
📝 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
.preview video {
width: 400px;
height: auto;
.preview video {
width: 100%;
max-width: 400px;
height: auto;
background-color: #000;
object-fit: contain;
}
.preview video::-webkit-media-controls {
background-color: rgba(0, 0, 0, 0.5);
}

Comment on lines +198 to +202
.editPostImage,
.editPostVideo {
width: 100%;
object-fit: cover;
border-radius: 0;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add aspect ratio for consistent edit mode display

The edit mode styles should maintain the same aspect ratio as the display mode for consistency.

Apply this change:

 .editPostImage,
 .editPostVideo {
   width: 100%;
+  aspect-ratio: 1.5;
   object-fit: cover;
   border-radius: 0;
 }
📝 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
.editPostImage,
.editPostVideo {
width: 100%;
object-fit: cover;
border-radius: 0;
.editPostImage,
.editPostVideo {
width: 100%;
aspect-ratio: 1.5;
object-fit: cover;
border-radius: 0;

Comment on lines +121 to +126
<div className={`${styles.creator} d-flex align-items-center gap-2`}>
{props?.creator?.image ? (
<img src={props?.creator?.image} alt="creator image" />
) : (
<AccountCircleIcon className="my-2" />
)}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve image handling and accessibility.

While the implementation works, there are several areas for improvement:

  1. Remove redundant optional chaining since creator is required per interface
  2. Add better error handling for images
  3. Enhance accessibility

Consider this improved implementation:

 <div className={`${styles.creator} d-flex align-items-center gap-2`}>
-  {props?.creator?.image ? (
-    <img src={props?.creator?.image} alt="creator image" />
+  {props.creator.image ? (
+    <img
+      src={props.creator.image}
+      alt={`${creatorName}'s profile picture`}
+      onError={(e) => {
+        e.currentTarget.onerror = null;
+        e.currentTarget.src = ''; // Clear src
+        setImageError(true);
+      }}
+    />
   ) : (
-    <AccountCircleIcon className="my-2" />
+    <AccountCircleIcon 
+      className="my-2" 
+      aria-label={`${creatorName}'s default profile icon`}
+    />
   )}

You'll need to add state for handling image errors:

const [imageError, setImageError] = React.useState(false);

Comment on lines 1059 to 1060
fireEvent.click(submitButton);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Wrap asynchronous operations with await act

When simulating user events that trigger asynchronous operations, it's important to wrap them with await act(async () => { ... }) to ensure all effects are processed.

Apply this diff to ensure proper handling of asynchronous events:

- fireEvent.click(submitButton);
+ await act(async () => {
+   fireEvent.click(submitButton);
+ });

Also applies to: 1101-1103, 1150-1151

Comment on lines +800 to +806
// Mock getElementById for image input
const mockImageInput = document.createElement('input');
mockImageInput.setAttribute('id', 'postImageUrl');
mockImageInput.value = 'test.jpg';
document.getElementById = jest.fn((id) =>
id === 'postImageUrl' ? mockImageInput : null,
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid overriding 'document.getElementById' globally

Overriding document.getElementById globally with jest.fn() can affect other tests and lead to unpredictable results. Use jest.spyOn to mock document.getElementById temporarily within the test scope.

Apply this diff to use jest.spyOn:

- document.getElementById = jest.fn((id) =>
-   id === 'postImageUrl' ? mockImageInput : null,
- );

+ const getElementByIdSpy = jest.spyOn(document, 'getElementById').mockImplementation((id) =>
+   id === 'postImageUrl' ? mockImageInput : null,
+ );

+ // Cleanup after test
+ getElementByIdSpy.mockRestore();

Also applies to: 841-844

Comment on lines +1270 to 1271
});
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Test case contains no assertions

The test 'opens edit modal with video post and verifies state' does not contain any assertions after setting up the component and interactions. Ensure that the test includes assertions to verify the expected state changes or behaviors.

Comment on lines +78 to +81
jest.mock('utils/errorHandler', () => ({
errorHandler: jest.fn(),
}));

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Duplicate mocks of 'react-toastify' and 'errorHandler'

The jest.mock('react-toastify', ...) and jest.mock('utils/errorHandler', ...) are declared multiple times in the file. Having multiple mocks for the same module can lead to unexpected behavior. Consider consolidating each mock into a single declaration.

Apply this diff to remove the duplicate mocks:

- jest.mock('utils/errorHandler', () => ({
-   errorHandler: jest.fn(),
- }));

- jest.mock('react-toastify', () => ({
-   toast: {
-     success: jest.fn(),
-     warn: jest.fn(),
-     error: jest.fn(),
-   },
- }));
📝 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
jest.mock('utils/errorHandler', () => ({
errorHandler: jest.fn(),
}));

Comment on lines +905 to +910
const clearButton = screen.getByTestId('closeimage');
fireEvent.click(clearButton, { preventDefault: jest.fn() });

// Verify preventDefault was called
expect(clearButton.onclick).toBeDefined();
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect usage of 'preventDefault' in 'fireEvent.click'

The fireEvent.click method does not accept a preventDefault option. Instead, create a mock event object and verify that preventDefault is called within the event handler.

Apply this diff to mock the event properly:

- fireEvent.click(clearButton, { preventDefault: jest.fn() });

+ const mockEvent = { preventDefault: jest.fn() };
+ fireEvent.click(clearButton, mockEvent);
+ expect(mockEvent.preventDefault).toHaveBeenCalled();
📝 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
const clearButton = screen.getByTestId('closeimage');
fireEvent.click(clearButton, { preventDefault: jest.fn() });
// Verify preventDefault was called
expect(clearButton.onclick).toBeDefined();
});
const clearButton = screen.getByTestId('closeimage');
const mockEvent = { preventDefault: jest.fn() };
fireEvent.click(clearButton, mockEvent);
expect(mockEvent.preventDefault).toHaveBeenCalled();
// Verify preventDefault was called
expect(clearButton.onclick).toBeDefined();
});

{
method: 'POST',
body: expect.any(FormData),
headers: { Authorization: 'Bearer dummy-token' },

Check failure

Code scanning / CodeQL

Hard-coded credentials Critical

The hard-coded value "Bearer dummy-token" is used as
authorization header
.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 15

🧹 Outside diff range and nitpick comments (37)
src/components/TableLoader/TableLoader.test.tsx (1)

73-77: Consider enhancing error test specificity.

The error test could be more specific by asserting the expected error message. This would ensure that the correct error is being thrown for the right reason.

Consider updating the test like this:

    expect(() => {
      render(
        <BrowserRouter>
          <TableLoader {...props} />
        </BrowserRouter>,
-    }).toThrow();
+    }).toThrow('Either noOfCols or headerTitles must be provided');
src/components/AddOn/core/AddOnRegister/AddOnRegister.test.tsx (1)

Line range hint 1-240: Consider improving test structure and reliability.

A few suggestions to enhance the test suite:

  1. Standardize the provider setup across tests (currently mixing MockedProvider and ApolloProvider).
  2. Replace hardcoded wait times (e.g., wait(3000)) with proper async utilities like waitFor to make tests more reliable.
  3. Consider extracting mock data and setup into a separate test utilities file for better maintainability.

Example refactor for the wait time:

-    await wait(3000); // Waiting for 3 seconds to reload the page
+    await waitFor(
+      () => {
+        expect(mockNavigate).toHaveBeenCalledWith(0);
+      },
+      { timeout: 3000 }
+    );
src/components/UserPortal/StartPostModal/StartPostModal.tsx (2)

13-13: Remove unused import.

The error import from 'console' appears to be unused in the code.

-import { error } from 'console';

197-204: Enhance user feedback during post creation.

Consider these UI improvements:

  1. Use translation for "Add Media" button text
  2. Add loading state during post creation
 <Button
   variant="primary"
   onClick={handlePost}
   disabled={!postformState.postinfo.trim()}
   data-testid="createPostBtn"
+ className={isLoading ? 'loading' : ''}
 >
-  Post
+  {isLoading ? t('posting') : t('post')}
 </Button>

Additional changes needed:

  1. Add isLoading state
  2. Set loading state during post creation
  3. Add translation keys for "posting" and "post"
src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx (2)

Line range hint 193-246: Uncomment and fix the error test case.

The commented-out error test is valuable for ensuring proper error handling. However, it needs fixing as the assertion expect(toast.error).toBeCalledWith() is incomplete - it's not verifying the error message.

Consider uncommenting and updating the test with proper error message assertion:

-  //   await waitFor(() => {
-  //     expect(toast.error).toBeCalledWith();
-  //   });
+    await waitFor(() => {
+      expect(toast.error).toHaveBeenCalledWith(
+        expect.stringContaining('error')  // Replace with actual error message from translations
+      );
+    });

Line range hint 1-246: Add test coverage for file upload scenarios.

Given that this PR introduces file upload features, consider adding test cases for:

  • File upload validation
  • File type restrictions
  • Upload success/failure scenarios
  • File deletion

Would you like me to help generate test cases for these scenarios?

src/components/UserPortal/Register/Register.test.tsx (2)

263-265: LGTM! Clear success case assertion with next steps.

The success message effectively communicates the registration outcome and next steps to the user.

Consider adding a test case to verify the registration state/UI updates while waiting for admin approval.


Line range hint 1-265: Consider enhancing test coverage for edge cases.

While the current test suite is solid, consider adding tests for:

  • Network error handling
  • Loading states during registration
  • Specific validation patterns (email format, password requirements)
  • Registration state management

This would provide more comprehensive coverage for error handling and user experience edge cases.

src/components/UserPortal/OrganizationCard/OrganizationCard.test.tsx (2)

Line range hint 27-121: Update mock data to support file upload operations.

The current mock structure needs to be extended to support the new file upload features. Consider adding:

  1. Mocks for file upload mutations/queries
  2. Test cases for file validation errors
  3. Sample responses with file metadata

Here's a suggested addition to your mocks:

const FILE_UPLOAD_MOCKS = [
  {
    request: {
      query: UPLOAD_ORGANIZATION_MEDIA,
      variables: {
        organizationId: '1',
        file: null, // Apollo will inject the actual file
      },
    },
    result: {
      data: {
        uploadOrganizationMedia: {
          _id: '1',
          fileName: 'test.jpg',
          fileType: 'image/jpeg',
          fileSize: 1024,
          url: 'https://example.com/test.jpg'
        }
      }
    }
  },
  // Add error case mock
  {
    request: {
      query: UPLOAD_ORGANIZATION_MEDIA,
      variables: {
        organizationId: '1',
        file: null,
      },
    },
    error: new Error('File upload failed')
  }
];

Line range hint 123-130: Enhance async operation handling.

While the current wait function works, consider using more reliable async utilities:

  • Replace fixed timeouts with waitFor
  • Add explicit assertions in wait conditions
  • Use findBy* queries where appropriate

Here's a suggested improvement:

// Replace the wait function with more specific utilities
import { waitFor } from '@testing-library/react';

// Instead of:
await wait();
expect(element).toBeInTheDocument();

// Use:
await waitFor(() => {
  expect(element).toBeInTheDocument();
});

// Or use findBy queries:
const element = await screen.findByTestId('joinBtn');
src/components/UserPortal/StartPostModal/StartPostModal.test.tsx (1)

14-22: Consider enhancing mock type safety and reusability.

The toast mock could benefit from type safety, and common mocks could be extracted for reuse.

Consider applying these improvements:

// Create a new file: src/test/mocks/toast.mock.ts
+import { toast as realToast } from 'react-toastify';
+
+type ToastType = typeof realToast;
+
+export const mockToast: jest.Mocked<ToastType> = {
+  error: jest.fn(),
+  info: jest.fn(),
+  success: jest.fn(),
+  dismiss: jest.fn(),
+  // Add other methods as needed
+} as jest.Mocked<ToastType>;
+
+jest.mock('react-toastify', () => ({
+  toast: mockToast
+}));
src/components/AgendaItems/AgendaItemsContainer.test.tsx (1)

Line range hint 472-472: Implement missing test cases for drag and drop functionality.

The TODO comment indicates that test coverage for drag and drop functionality (lines 172-202) is missing. This is important to ensure the reliability of this key user interaction.

Would you like me to help generate test cases for the drag and drop functionality? I can provide examples using react-beautiful-dnd testing patterns.

src/screens/UserPortal/Events/Events.test.tsx (1)

Line range hint 405-408: Remove or restore commented-out test code.

These commented lines appear to be part of the calendar view test. Either restore the test functionality or remove the comments to maintain code cleanliness.

-    // await wait();
-
-    // userEvent.click(screen.getByTestId('modeChangeBtn'));
-    // userEvent.click(screen.getByTestId('modeBtn1'));
src/components/RecurrenceOptions/RecurrenceOptions.test.tsx (1)

Line range hint 1-586: Consider refactoring test suite for better maintainability.

While the test coverage is comprehensive, consider these improvements:

  1. Extract common setup code into a helper function to reduce duplication
  2. Create constants for configuration values (e.g., 30000 timeout)
  3. Break down long test cases into smaller, focused scenarios using describe blocks

Example refactor for common setup:

const setupTest = async () => {
  const rendered = render(
    <MockedProvider addTypename={false} link={link}>
      <BrowserRouter>
        <Provider store={store}>
          <LocalizationProvider dateAdapter={AdapterDayjs}>
            <ThemeProvider theme={theme}>
              <I18nextProvider i18n={i18n}>
                <OrganizationEvents />
              </I18nextProvider>
            </ThemeProvider>
          </LocalizationProvider>
        </Provider>
      </BrowserRouter>
    </MockedProvider>
  );
  
  await wait();
  await waitFor(() => {
    expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
  });
  
  return rendered;
};
src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.test.tsx (1)

Line range hint 1-653: Consider refactoring test setup for better maintainability.

The test file has good coverage but could benefit from some refactoring:

  1. Extract common setup code into beforeEach/beforeAll hooks
  2. Create a helper function for managing timers (useFakeTimers/useRealTimers)
  3. Consider using test.each for similar test cases with different inputs

This would reduce code duplication and make the tests more maintainable.

src/components/OrgPostCard/OrgPostCard.tsx (4)

151-157: Optimize useEffect dependencies

The useEffect hook should include all external values in its dependency array to prevent stale closures.

  useEffect(() => {
    setPostFormState({
      posttitle: postTitle,
      postinfo: postInfo,
      mediaFile: null,
      pinned: pinned,
    });
    setPreviewUrl(postPhoto || postVideo);
-  }, []);
+  }, [postTitle, postInfo, postPhoto, postVideo, pinned]);

236-236: Remove console.log statement

Debug statements should not be present in production code.

-      console.log(data);

237-239: Enhance error handling

The error handling could be more specific by checking for different HTTP status codes.

-      if (!response.ok) {
-        throw new Error(data?.error || t('postCreationFailed'));
-      }
+      if (response.status === 413) {
+        throw new Error(t('fileTooLarge'));
+      } else if (response.status === 415) {
+        throw new Error(t('unsupportedFileType'));
+      } else if (!response.ok) {
+        throw new Error(data?.error || t('postCreationFailed'));
+      }

Line range hint 282-367: Simplify media preview rendering

The nested conditional rendering can be simplified for better maintainability.

- {postVideo && (
-   // video card
- )}
- {postPhoto ? (
-   // photo card
- ) : !postVideo ? (
-   // default card
- ) : (
-   ''
- )}
+ {(() => {
+   if (postVideo) return renderVideoCard();
+   if (postPhoto) return renderPhotoCard();
+   return renderDefaultCard();
+ })()}

Consider extracting the card rendering logic into separate components for better maintainability.

src/components/RecurrenceOptions/CustomRecurrence.test.tsx (1)

Line range hint 1-724: Consider improving test maintainability.

While the tests are comprehensive, there are a few opportunities for improvement:

  1. Extract common setup code into beforeEach blocks
  2. Create test helper functions for repetitive actions like setting up the event form
  3. Consider using a custom render function that includes all providers

Example implementation:

// test-utils.tsx
const AllTheProviders = ({ children }) => {
  return (
    <MockedProvider addTypename={false} link={link}>
      <BrowserRouter>
        <Provider store={store}>
          <LocalizationProvider dateAdapter={AdapterDayjs}>
            <ThemeProvider theme={theme}>
              <I18nextProvider i18n={i18nForTest}>
                {children}
              </I18nextProvider>
            </ThemeProvider>
          </LocalizationProvider>
        </Provider>
      </BrowserRouter>
    </MockedProvider>
  );
};

const customRender = (ui: React.ReactElement) => {
  return render(ui, { wrapper: AllTheProviders });
};

// Helper function
const fillEventForm = async (formData) => {
  userEvent.type(screen.getByPlaceholderText(/Enter Title/i), formData.title);
  userEvent.type(
    screen.getByPlaceholderText(/Enter Description/i),
    formData.description
  );
  // ... other form fields
};

This would simplify your tests to:

describe('Testing recurring events with custom recurrence patterns', () => {
  beforeEach(async () => {
    customRender(<OrganizationEvents />);
    await wait();
  });

  test('Creating a daily recurring event', async () => {
    await waitFor(() => {
      expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
    });
    userEvent.click(screen.getByTestId('createEventModalBtn'));
    await fillEventForm(formData);
    // ... rest of the test
  });
});
src/components/UserPortal/PostCard/PostCard.tsx (2)

94-99: Consider using a discriminated union type for media state

Instead of managing separate state variables for media type and file, consider using a discriminated union type for better type safety and state management.

-const [editedMedia, setEditedMedia] = useState<File | null>(null);
-const [editedMediaType, setEditedMediaType] = useState<'image' | 'video' | null>(null);
+type MediaState =
+  | { type: null; file: null }
+  | { type: 'image' | 'video'; file: File };
+const [mediaState, setMediaState] = useState<MediaState>({ type: null, file: null });

653-653: Add loading state to edit button

The edit button should reflect the loading state to prevent multiple submissions.

 <Button
   size="sm"
   variant="success"
   className="px-4"
   data-testid="editPostBtn"
+  disabled={isEditing}
   onClick={handleEditPost}
 >
-  {t('editPost')}
+  {isEditing ? t('saving') : t('editPost')}
 </Button>
src/screens/UserPortal/Settings/Settings.tsx (6)

Line range hint 31-31: Rename component to follow React naming convention

React components should use PascalCase naming convention. Rename settings to Settings.

-export default function settings(): JSX.Element {
+export default function Settings(): JSX.Element {

Line range hint 39-47: Optimize resize handler with debounce

The resize handler could trigger frequent re-renders. Consider debouncing the handler for better performance.

+import { debounce } from 'lodash';
+
 const handleResize = (): void => {
   if (window.innerWidth <= 820) {
     setHideDrawer(!hideDrawer);
   }
 };
+
+const debouncedResize = debounce(handleResize, 250);
 
 useEffect(() => {
   handleResize();
-  window.addEventListener('resize', handleResize);
+  window.addEventListener('resize', debouncedResize);
   return () => {
-    window.removeEventListener('resize', handleResize);
+    window.removeEventListener('resize', debouncedResize);
+    debouncedResize.cancel();
   };
 }, []);

Line range hint 290-307: Add file validation for image uploads

The image upload handler should validate file size and type before processing. Also, consider adding loading state during image conversion.

 onChange={async (e: React.ChangeEvent<HTMLInputElement>): Promise<void> => {
   const target = e.target as HTMLInputElement;
   const file = target.files && target.files[0];
   if (file) {
+    // Validate file size (e.g., 5MB limit)
+    if (file.size > 5 * 1024 * 1024) {
+      toast.error(t('fileSizeError'));
+      return;
+    }
+    
+    // Validate file type
+    if (!file.type.startsWith('image/')) {
+      toast.error(t('fileTypeError'));
+      return;
+    }
+    
+    setUserDetails(prev => ({ ...prev, isLoading: true }));
     const image = await convertToBase64(file);
-    setUserDetails({ ...userDetails, image });
+    setUserDetails(prev => ({ ...prev, image, isLoading: false }));
   }
 }}

Line range hint 108-134: Add loading state for user details update

The update handler should show loading state during the API call to prevent multiple submissions and improve user experience.

+const [isUpdating, setIsUpdating] = useState(false);

 const handleUpdateUserDetails = async (): Promise<void> => {
   try {
+    setIsUpdating(true);
     let updatedUserDetails = { ...userDetails };
     if (updatedUserDetails.image === originalImageState.current) {
       updatedUserDetails = { ...updatedUserDetails, image: '' };
     }
     const { data } = await updateUserDetails({
       variables: updatedUserDetails,
     });
     if (data) {
       toast.success(tCommon('updatedSuccessfully', { item: 'Profile' }) as string);
       setTimeout(() => {
         window.location.reload();
       }, 500);
       const userFullName = `${userDetails.firstName} ${userDetails.lastName}`;
       setItem('name', userFullName);
     }
   } catch (error: unknown) {
     errorHandler(t, error);
   } finally {
+    setIsUpdating(false);
   }
 };

Line range hint 246-257: Add phone number validation and formatting

The phone number input should include proper validation and formatting to ensure correct data entry.

+import { parsePhoneNumberFromString, AsYouType } from 'libphonenumber-js';

+const formatPhoneNumber = (value: string, country: string): string => {
+  const formatter = new AsYouType(country);
+  return formatter.input(value);
+};

+const validatePhoneNumber = (value: string, country: string): boolean => {
+  const phoneNumber = parsePhoneNumberFromString(value, country);
+  return phoneNumber ? phoneNumber.isValid() : false;
+};

 <Form.Control
   type="tel"
   id="phoneNo"
   placeholder="1234567890"
   value={userDetails.phoneNumber}
-  onChange={(e) => handleFieldChange('phoneNumber', e.target.value)}
+  onChange={(e) => {
+    const formatted = formatPhoneNumber(e.target.value, userDetails.country);
+    handleFieldChange('phoneNumber', formatted);
+  }}
+  isInvalid={userDetails.phoneNumber && !validatePhoneNumber(userDetails.phoneNumber, userDetails.country)}
   className={`${styles.cardControl}`}
   data-testid="inputPhoneNumber"
 />
+{userDetails.phoneNumber && !validatePhoneNumber(userDetails.phoneNumber, userDetails.country) && (
+  <Form.Control.Feedback type="invalid">
+    {t('invalidPhoneNumber')}
+  </Form.Control.Feedback>
+)}

Line range hint 466-478: Add form validation before submission

Add form validation to ensure required fields are filled and valid before allowing submission.

+const validateForm = (): boolean => {
+  const required = ['firstName', 'lastName', 'email'];
+  const hasEmptyRequired = required.some(field => !userDetails[field]);
+  const hasInvalidPhone = userDetails.phoneNumber && 
+    !validatePhoneNumber(userDetails.phoneNumber, userDetails.country);
+  return !hasEmptyRequired && !hasInvalidPhone;
+};

 <Button
   onClick={handleUpdateUserDetails}
+  disabled={!validateForm() || isUpdating}
   data-testid="updateUserBtn"
   className={`${styles.cardButton}`}
 >
-  {tCommon('saveChanges')}
+  {isUpdating ? tCommon('saving') : tCommon('saveChanges')}
 </Button>
src/components/UserPortal/PostCard/PostCard.test.tsx (3)

370-370: Use a test constant instead of hard-coded token

Replace the hard-coded Bearer token with a constant defined in a test utilities file. This improves maintainability and follows security best practices.

- headers: { Authorization: 'Bearer dummy-token' },
+ headers: { Authorization: `Bearer ${TEST_AUTH_TOKEN}` },
🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 370-370: Hard-coded credentials
The hard-coded value "Bearer dummy-token" is used as authorization header.


912-912: Fix typo in error message

There's a typo in the error message text.

- 'Please select and image or video file.',
+ 'Please select an image or video file.',

688-690: Refactor duplicate cleanup code

The URL.createObjectURL cleanup code is duplicated. Consider extracting it into a helper function and using optional chaining as suggested by the linter.

+ const cleanupObjectURL = () => {
+   global.URL?.createObjectURL = jest.fn();
+ };

- if (global.URL && global.URL.createObjectURL) {
-   global.URL.createObjectURL = jest.fn();
- }

+ cleanupObjectURL();

Also applies to: 937-939

🧰 Tools
🪛 Biome

[error] 688-688: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/screens/OrgPost/OrgPost.test.tsx (2)

17-29: Consider using a more type-safe mock implementation for react-i18next.

The current mock implementation could be improved by adding proper TypeScript types and handling edge cases.

 jest.mock('react-i18next', () => ({
   useTranslation: () => ({
-    t: (key: string) => key,
+    t: (key: string, options?: Record<string, unknown>) => {
+      if (typeof key !== 'string') return '';
+      return options ? `${key} ${JSON.stringify(options)}` : key;
+    },
     i18n: {
-      changeLanguage: () => new Promise(() => {}),
+      changeLanguage: () => Promise.resolve(),
     },
   }),
   initReactI18next: {
     type: '3rdParty',
     init: () => {},
   },
   I18nextProvider: ({ children }: { children: React.ReactNode }) => children,
 }));

66-119: Consider extracting mock data to a separate file.

The mock data is well-structured but could be moved to a separate test utilities file to improve maintainability and reusability across different test files.

Consider creating a new file src/screens/OrgPost/__mocks__/mockData.ts and moving the mock data there:

// src/screens/OrgPost/__mocks__/mockData.ts
export const mockPostsData = {
  organizations: [
    {
      posts: {
        edges: [
          {
            node: {
              _id: '1',
              title: 'Test Post 1',
              // ... rest of the mock data
            },
            cursor: 'cursor1',
          },
        ],
        pageInfo: {
          // ... pageInfo mock data
        },
        totalCount: 1,
      },
    },
  ],
};
src/components/OrgPostCard/OrgPostCard.test.tsx (1)

573-612: Good test coverage for pin toggle edge cases.

The test properly verifies behavior when no data is returned from the mutation. Consider adding a test for network failures as well.

Add a test case for network failure:

test('should handle network error when toggling pin', async () => {
  const networkErrorMock = {
    request: {
      query: TOGGLE_PINNED_POST,
      variables: { id: '12' },
    },
    error: new Error('Network error'),
  };

  const errorLink = new StaticMockLink([networkErrorMock], true);

  render(
    <MockedProvider link={errorLink} addTypename={false}>
      <I18nextProvider i18n={i18nForTest}>
        <OrgPostCard {...mockProps} />
      </I18nextProvider>
    </MockedProvider>,
  );

  userEvent.click(screen.getByTestId('cardStructure'));
  userEvent.click(screen.getByTestId('moreiconbtn'));
  userEvent.click(screen.getByTestId('pinpostBtn'));

  await waitFor(() => {
    expect(errorHandler).toHaveBeenCalledWith(
      expect.anything(),
      new Error('Network error'),
    );
  });
});
src/components/UsersTableItem/UserTableItem.test.tsx (1)

506-507: Consider consolidating navigation assertions.

The code tests both window.location.replace and mockNavgatePush for the same navigation action. While thorough, this might be redundant as they serve the same purpose. Consider choosing one navigation method based on your application's primary navigation strategy.

-    expect(window.location.replace).toHaveBeenCalledWith('/orgdash/abc');
-    expect(mockNavgatePush).toHaveBeenCalledWith('/orgdash/abc');
+    expect(mockNavgatePush).toHaveBeenCalledWith('/orgdash/abc');
src/screens/UserPortal/Chat/Chat.test.tsx (2)

Line range hint 1548-1587: Enhance test coverage for direct chat creation.

The current test only verifies UI element presence. Consider adding the following test scenarios:

  1. Successful chat creation with selected user
  2. Validation of required fields
  3. Error handling scenarios
  4. Verification of API calls

Example additions:

test('create new direct chat', async () => {
  // ... existing setup ...

  // Test user selection
  const userSelect = await screen.findByTestId('userSelect');
  fireEvent.change(userSelect, { target: { value: 'Test User' } });
  expect(screen.getByText('Test User')).toBeInTheDocument();

  // Test successful submission
  fireEvent.click(submitBtn);
  await waitFor(() => {
    expect(screen.getByText('Chat created successfully')).toBeInTheDocument();
  });

  // Test validation
  fireEvent.click(submitBtn);
  await waitFor(() => {
    expect(screen.getByText('Please select a user')).toBeInTheDocument();
  });
});

Line range hint 1588-1628: Improve group chat creation test coverage.

The test only checks basic UI rendering. For comprehensive coverage, add tests for:

  1. Group name validation
  2. Member selection (minimum/maximum members)
  3. Successful group creation
  4. Error scenarios

Example improvements:

test('create new group chat', async () => {
  // ... existing setup ...

  // Test group name validation
  const groupNameInput = await screen.findByTestId('groupName');
  fireEvent.change(groupNameInput, { target: { value: '' } });
  fireEvent.click(submitBtn);
  expect(screen.getByText('Group name is required')).toBeInTheDocument();

  // Test member selection
  const memberSelect = await screen.findByTestId('memberSelect');
  fireEvent.change(memberSelect, { target: { value: ['user1', 'user2'] } });
  expect(screen.getAllByTestId('selected-member')).toHaveLength(2);

  // Test successful submission
  fireEvent.change(groupNameInput, { target: { value: 'Test Group' } });
  fireEvent.click(submitBtn);
  await waitFor(() => {
    expect(screen.getByText('Group created successfully')).toBeInTheDocument();
  });
});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e4fa134 and 3e71c6e.

📒 Files selected for processing (25)
  • src/GraphQl/Queries/fundQueries.ts (0 hunks)
  • src/components/AddOn/core/AddOnRegister/AddOnRegister.test.tsx (1 hunks)
  • src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.test.tsx (4 hunks)
  • src/components/AgendaCategory/AgendaCategoryContainer.test.tsx (2 hunks)
  • src/components/AgendaItems/AgendaItemsContainer.test.tsx (1 hunks)
  • src/components/EventListCard/EventListCard.test.tsx (8 hunks)
  • src/components/OrgPostCard/OrgPostCard.test.tsx (15 hunks)
  • src/components/OrgPostCard/OrgPostCard.tsx (17 hunks)
  • src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx (1 hunks)
  • src/components/RecurrenceOptions/CustomRecurrence.test.tsx (2 hunks)
  • src/components/RecurrenceOptions/RecurrenceOptions.test.tsx (2 hunks)
  • src/components/TableLoader/TableLoader.test.tsx (1 hunks)
  • src/components/UserPortal/OrganizationCard/OrganizationCard.test.tsx (1 hunks)
  • src/components/UserPortal/PostCard/PostCard.test.tsx (24 hunks)
  • src/components/UserPortal/PostCard/PostCard.tsx (13 hunks)
  • src/components/UserPortal/Register/Register.test.tsx (7 hunks)
  • src/components/UserPortal/StartPostModal/StartPostModal.test.tsx (1 hunks)
  • src/components/UserPortal/StartPostModal/StartPostModal.tsx (3 hunks)
  • src/components/UsersTableItem/UserTableItem.test.tsx (3 hunks)
  • src/screens/OrgPost/OrgPost.test.tsx (2 hunks)
  • src/screens/OrganizationDashboard/OrganizationDashboard.tsx (0 hunks)
  • src/screens/OrganizationEvents/OrganizationEvents.test.tsx (3 hunks)
  • src/screens/UserPortal/Chat/Chat.test.tsx (2 hunks)
  • src/screens/UserPortal/Events/Events.test.tsx (2 hunks)
  • src/screens/UserPortal/Settings/Settings.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • src/GraphQl/Queries/fundQueries.ts
  • src/screens/OrganizationDashboard/OrganizationDashboard.tsx
🧰 Additional context used
🪛 GitHub Check: CodeQL
src/components/OrgPostCard/OrgPostCard.tsx

[warning] 556-556: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.


[warning] 584-584: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

src/components/UserPortal/PostCard/PostCard.test.tsx

[failure] 370-370: Hard-coded credentials
The hard-coded value "Bearer dummy-token" is used as authorization header.

src/components/UserPortal/PostCard/PostCard.tsx

[warning] 617-617: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.


[warning] 624-624: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

src/components/UserPortal/StartPostModal/StartPostModal.tsx

[warning] 169-169: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

🪛 Biome
src/components/UserPortal/PostCard/PostCard.test.tsx

[error] 688-688: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 937-937: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/components/UserPortal/StartPostModal/StartPostModal.test.tsx

[error] 232-235: Disallow duplicate setup and teardown hooks.

Disallow beforeEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

🔇 Additional comments (39)
src/components/TableLoader/TableLoader.test.tsx (1)

76-76: LGTM! Good simplification of the error assertion.

The change from .toThrowError() to .toThrow() is a good improvement as it's more concise while maintaining the same functionality.

src/components/AddOn/core/AddOnRegister/AddOnRegister.test.tsx (2)

158-158: LGTM! Good update to Jest assertion method.

The change from toBeCalledWith to toHaveBeenCalledWith aligns with Jest best practices and improves test readability.


158-158: 🛠️ Refactor suggestion

Consider adding test coverage for file upload functionality.

Given that this PR introduces file upload features for user and admin portals (as per PR objectives), consider adding test cases to verify:

  • File upload validation
  • Successful file upload scenarios
  • Error handling for failed uploads
  • File size and type restrictions

Let's check if there are any file upload related tests in the codebase:

src/components/UserPortal/StartPostModal/StartPostModal.tsx (2)

32-40: LGTM! Well-structured state management.

The state management is well-organized with proper TypeScript types and clean initialization.


181-181: Previous review comment about useRef is still valid.

The current implementation using document.getElementById should be replaced with useRef as suggested in the previous review.

src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx (1)

188-190: LGTM! Good improvement in test assertion.

The change to toHaveBeenCalledWith follows Jest's preferred syntax and improves readability.

src/components/UserPortal/Register/Register.test.tsx (3)

107-107: LGTM! Updated to modern Jest assertion syntax.

The change from toBeCalledWith to toHaveBeenCalledWith follows Jest's recommended assertion syntax.


127-127: LGTM! Consistent validation error assertions.

The toast error assertions have been uniformly updated to use modern Jest syntax while maintaining consistent error messaging across all validation scenarios.

Also applies to: 148-148, 172-172, 198-198


226-228: LGTM! Clear password mismatch error assertion.

The multi-line assertion format improves readability while maintaining consistent modern Jest syntax.

src/components/UserPortal/OrganizationCard/OrganizationCard.test.tsx (2)

2-2: LGTM! Clean import consolidation.

The consolidation of testing library imports improves code organization while maintaining all necessary functionality.


Line range hint 1-359: Add test coverage for new file upload features.

Given that this PR introduces file upload capabilities, consider adding test cases for:

  • Rendering organization cards with uploaded media
  • Handling different media types (images, videos)
  • Media upload error scenarios
  • Media preview functionality

This will ensure the new features are properly tested and maintain reliability.

Let's verify the component's media-related props and functionality:

Would you like me to help draft the additional test cases?

src/components/UserPortal/StartPostModal/StartPostModal.test.tsx (1)

79-305: LGTM! Test suite structure is well-organized.

The test suite has good coverage of the main functionality with well-structured test cases. The component is properly unmounted after each test by the testing library.

🧰 Tools
🪛 Biome

[error] 232-235: Disallow duplicate setup and teardown hooks.

Disallow beforeEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

src/components/AgendaCategory/AgendaCategoryContainer.test.tsx (1)

272-274: LGTM! Improved test assertion readability.

The changes to use toHaveBeenCalledWith instead of toBeCalledWith improve readability while maintaining the same functionality. This update is consistent with similar changes across other test files in the codebase.

Also applies to: 367-369

src/components/AgendaItems/AgendaItemsContainer.test.tsx (1)

374-376: LGTM! Improved assertion syntax.

The update to use toHaveBeenCalledWith instead of toBeCalledWith follows Jest's best practices for mock function assertions.

src/screens/OrganizationEvents/OrganizationEvents.test.tsx (3)

285-285: LGTM! Assertion method updated to follow Jest's best practices.

The change from toBeCalledWith to toHaveBeenCalledWith aligns with Jest's recommended assertion methods for spies and mocks.


455-455: LGTM! Success message assertion is consistent.

The assertion for successful event creation maintains consistency with the updated Jest assertion method.


Line range hint 1-463: Consider adding test coverage for file upload scenarios.

Given that this PR introduces file upload capabilities for posts, consider adding test cases to verify:

  • File upload during event creation
  • Validation of file types and sizes
  • Error handling for failed uploads

Let's check if there are any file upload-related tests in the codebase:

#!/bin/bash
# Description: Search for existing file upload test coverage
# Expected: Find test files that cover file upload functionality

# Look for file upload related test files
rg -l "upload.*test|test.*upload" --type typescript --type tsx

# Search for file upload related test cases
ast-grep --pattern 'test($_, $_) {
  $$$
  upload
  $$$
}'
src/screens/UserPortal/Events/Events.test.tsx (3)

339-341: LGTM! Good update to modern Jest practices.

The change from toBeCalledWith to toHaveBeenCalledWith aligns with current Jest best practices and provides better error messages.


382-384: LGTM! Maintains consistency in assertion methods.

The update to toHaveBeenCalledWith maintains consistency with modern Jest practices across test cases.


Line range hint 1-577: Consider adding tests for file upload functionality.

Given that this PR introduces file upload capabilities for user and admin feeds (as mentioned in the PR objectives), consider adding test cases to verify:

  • File upload functionality
  • File type validation
  • Upload size limits
  • Error handling for failed uploads

Let me check if there are any file upload related tests in other files:

Would you like me to help create test cases for the file upload functionality?

src/components/RecurrenceOptions/RecurrenceOptions.test.tsx (2)

456-456: LGTM! Standardized assertion method.

The change from toBeCalledWith to toHaveBeenCalledWith aligns with Jest's preferred assertion style. Both methods are functionally equivalent.


578-578: LGTM! Consistent assertion style.

Similar to the previous change, this standardizes the assertion method to toHaveBeenCalledWith.

src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.test.tsx (3)

252-254: LGTM! Improved test assertion.

The update to use toHaveBeenCalledWith follows Jest's best practices for mock function assertions.


468-470: LGTM! Clear error handling for date validation.

The error message is descriptive and the test properly verifies the validation behavior.


595-597: LGTM! Consistent error handling in edit mode.

The error handling maintains consistency with the create mode and properly validates date ranges.

src/components/OrgPostCard/OrgPostCard.tsx (1)

26-26: LGTM: Good addition of refetch prop

The addition of the refetch prop enhances the component's reusability by allowing parent components to refresh their data after post updates.

src/components/RecurrenceOptions/CustomRecurrence.test.tsx (1)

584-584: LGTM! Good update to use the preferred Jest assertion method.

The change from toBeCalledWith to toHaveBeenCalledWith follows Jest's best practices. While both methods work, toHaveBeenCalledWith is the canonical name and provides better readability.

Also applies to: 712-712

src/components/UserPortal/PostCard/PostCard.tsx (4)

24-26: LGTM: New imports for media handling icons

The added imports align well with the new media handling functionality.


48-48: LGTM: Interface update for creator image

The addition of the image property to the creator interface is consistent with the new media handling capabilities.


571-602: LGTM: Well-structured media management UI

The media management UI is well-organized with clear buttons for changing and removing media. The conditional rendering based on media type is implemented correctly.


613-644: ⚠️ Potential issue

Address potential XSS vulnerabilities in media preview

The media preview URLs should be sanitized to prevent XSS attacks. Consider implementing URL validation or using a sanitization library.

Apply this fix to validate URLs:

+const isValidUrl = (url: string): boolean => {
+  try {
+    new URL(url);
+    return true;
+  } catch {
+    return false;
+  }
+};

+const sanitizeUrl = (url: string): string => {
+  if (!isValidUrl(url)) {
+    console.error('Invalid URL detected');
+    return '';
+  }
+  return url;
+};

-src={mediaPreviewUrl}
+src={sanitizeUrl(mediaPreviewUrl)}
🧰 Tools
🪛 GitHub Check: CodeQL

[warning] 617-617: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.


[warning] 624-624: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

src/components/EventListCard/EventListCard.test.tsx (1)

365-365: LGTM! Consistent update of Jest assertions.

The changes correctly update the assertion method from toBeCalledWith to toHaveBeenCalledWith, which is the preferred method name in Jest. The test suite demonstrates excellent practices with comprehensive coverage of component functionality.

Also applies to: 418-418, 462-462, 699-699, 765-765, 826-826, 866-866, 924-924

src/components/UserPortal/PostCard/PostCard.test.tsx (1)

597-940: LGTM! Comprehensive test coverage

The new test cases thoroughly cover media handling functionality, including file type validation, API responses, and error scenarios. The tests are well-structured and follow testing best practices.

🧰 Tools
🪛 Biome

[error] 688-688: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 937-937: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/screens/OrgPost/OrgPost.test.tsx (2)

565-575: Previous comment about duplicate renderWithProviders is still valid.

Also applies to: 426-436


589-597: LGTM: Basic component rendering test is well implemented.

The test case properly checks for loading state and essential UI elements.

src/components/OrgPostCard/OrgPostCard.test.tsx (1)

115-118: LGTM: Good practice to clear mocks before each test.

The addition of beforeEach to clear all mocks helps prevent test pollution and ensures each test starts with a clean slate.

src/components/UsersTableItem/UserTableItem.test.tsx (3)

502-502: LGTM: Updated toast assertion following Jest best practices.

The change from toBeCalledWith to toHaveBeenCalledWith improves test clarity and follows Jest's recommended assertion style.


696-696: LGTM: Consistent update to toast assertion.

The change maintains consistency with the updated Jest assertion style used throughout the file.


723-724: Same feedback applies: Consider consolidating navigation assertions.

This is the same pattern of redundant navigation testing as discussed earlier.

Comment on lines +167 to +173
<video controls data-testid="videoPreview">
<source
src={previewUrl}
type={postformState.mediaFile.type}
/>
({t('tag')})
</video>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential XSS vulnerability in video source.

The video source URL should be properly sanitized before being rendered to prevent XSS attacks.

 <video controls data-testid="videoPreview">
   <source
-    src={previewUrl}
+    src={previewUrl ? encodeURI(previewUrl) : ''}
     type={postformState.mediaFile.type}
   />
   ({t('tag')})
 </video>

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Check: CodeQL

[warning] 169-169: DOM text reinterpreted as HTML
DOM text is reinterpreted as HTML without escaping meta-characters.

Comment on lines +94 to +106
const response = await fetch(
`${process.env.REACT_APP_TALAWA_REST_URL}/create-post`,
{
method: 'POST',
body: formData,
headers: { Authorization: `Bearer ${accessToken}` },
},
});
/* istanbul ignore next */
);

const data = await response.json();
if (!response.ok) {
throw new Error(data?.error || t('postCreationFailed'));
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling and add request timeout.

Consider these improvements for better error handling and user experience:

  1. Add request timeout
  2. Add file size validation
  3. Handle specific HTTP status codes
 const response = await fetch(
   `${process.env.REACT_APP_TALAWA_REST_URL}/create-post`,
   {
     method: 'POST',
     body: formData,
     headers: { Authorization: `Bearer ${accessToken}` },
+    signal: AbortSignal.timeout(30000), // 30 second timeout
   },
 );

+const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+if (postformState.mediaFile && postformState.mediaFile.size > MAX_FILE_SIZE) {
+  throw new Error(t('fileTooBig'));
+}

 const data = await response.json();
 if (!response.ok) {
+  switch (response.status) {
+    case 413:
+      throw new Error(t('fileTooBig'));
+    case 415:
+      throw new Error(t('unsupportedFileType'));
+    default:
       throw new Error(data?.error || t('postCreationFailed'));
+  }
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +80 to +88
beforeEach(() => {
jest.clearAllMocks();
// Reset fetch mock
(global.fetch as jest.Mock).mockReset();
// Reset createObjectURL mock
mockCreateObjectURL.mockClear();
// Reset revokeObjectURL mock
mockRevokeObjectURL.mockClear();
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Merge duplicate beforeEach hooks.

There are two separate beforeEach hooks that should be consolidated.

Apply this change:

describe('StartPostModal Component', () => {
  const mockClick = jest.fn();

  beforeEach(() => {
    jest.clearAllMocks();
    // Reset fetch mock
    (global.fetch as jest.Mock).mockReset();
    // Reset createObjectURL mock
    mockCreateObjectURL.mockClear();
    // Reset revokeObjectURL mock
    mockRevokeObjectURL.mockClear();
+   // Setup document.getElementById mock
+   document.getElementById = jest.fn().mockReturnValue({ click: mockClick });
  });

-  beforeEach(() => {
-    jest.clearAllMocks();
-    document.getElementById = jest.fn().mockReturnValue({ click: mockClick });
-  });

  // ... rest of the tests
});

Also applies to: 232-235

Comment on lines +280 to +305
it('should correctly append file to FormData', async () => {
// Mock API response
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
json: async () => ({ success: true }),
});

// Setup and render
renderStartPostModal();

// Select file
const fileInput = screen.getByTestId('modalFileInput');
const testFile = new File(['test'], 'test.png', { type: 'image/png' });
await userEvent.upload(fileInput, testFile);

// Trigger post (which will create FormData and append file)
const postInput = screen.getByTestId('postInput');
await userEvent.type(postInput, 'test');
await userEvent.click(screen.getByTestId('createPostBtn'));

// Verify file was appended to FormData
await waitFor(() => {
const formData = (global.fetch as jest.Mock).mock.calls[0][1]
.body as FormData;
expect(formData.get('file')).toEqual(testFile);
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add tests for file upload edge cases.

The current test suite lacks coverage for important file upload scenarios.

Add these test cases:

it('should handle file size limit exceeded', async () => {
  renderStartPostModal();
  
  const fileInput = screen.getByTestId('modalFileInput');
  const largeFile = new File(['x'.repeat(5 * 1024 * 1024)], 'large.png', { 
    type: 'image/png' 
  });
  
  await userEvent.upload(fileInput, largeFile);
  
  expect(toast.error).toHaveBeenCalledWith(
    expect.stringContaining('file size exceeds')
  );
});

it('should handle invalid file type', async () => {
  renderStartPostModal();
  
  const fileInput = screen.getByTestId('modalFileInput');
  const invalidFile = new File(['test'], 'test.txt', { 
    type: 'text/plain' 
  });
  
  await userEvent.upload(fileInput, invalidFile);
  
  expect(toast.error).toHaveBeenCalledWith(
    expect.stringContaining('file type not supported')
  );
});

it('should handle network error during upload', async () => {
  (global.fetch as jest.Mock).mockRejectedValueOnce(
    new Error('Network error')
  );
  
  renderStartPostModal();
  
  const fileInput = screen.getByTestId('modalFileInput');
  const file = new File(['test'], 'test.png', { type: 'image/png' });
  await userEvent.upload(fileInput, file);
  
  const postInput = screen.getByTestId('postInput');
  await userEvent.type(postInput, 'test post');
  
  await userEvent.click(screen.getByTestId('createPostBtn'));
  
  await waitFor(() => {
    expect(toast.error).toHaveBeenCalledWith(
      expect.stringContaining('network error')
    );
  });
});

Comment on lines +374 to +376
expect(toast.warning).toHaveBeenCalledWith('Title can not be blank!');
expect(toast.warning).toHaveBeenCalledWith('Description can not be blank!');
expect(toast.warning).toHaveBeenCalledWith('Location can not be blank!');
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Warning messages should be internationalized

The code uses hardcoded English strings for validation messages despite having i18n setup in place. The file already imports and uses useTranslation for other messages, but these validation messages are not using the translation system.

  • In src/screens/OrganizationEvents/OrganizationEvents.tsx, replace hardcoded strings with translation keys:
    toast.warning('Title can not be blank!');
    toast.warning('Description can not be blank!');
    toast.warning('Location can not be blank!');
🔗 Analysis chain

LGTM! Validation messages are properly tested.

The assertions correctly verify that appropriate warning messages are displayed for blank title, description, and location. The update to toHaveBeenCalledWith maintains consistency with Jest's best practices.

Let's verify that these validation messages match the translations:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the validation messages match the translation strings
# Expected: The messages should be defined in the translation files

rg -l "Title can not be blank|Description can not be blank|Location can not be blank" --type yaml

Length of output: 160


Script:

#!/bin/bash
# Let's search for these validation messages in the codebase to find their source
# and any translation files that might contain them

# Search for the validation messages in all files
rg -l "Title can not be blank|Description can not be blank|Location can not be blank"

# Also search for potential translation keys or constants
rg -l "TITLE_BLANK|DESCRIPTION_BLANK|LOCATION_BLANK"

# Look for translation files
fd -e json -e yaml locale

Length of output: 283


Script:

#!/bin/bash
# Let's examine the actual implementation in OrganizationEvents.tsx to see how these messages are used
rg -A 5 -B 5 "Title can not be blank|Description can not be blank|Location can not be blank" src/screens/OrganizationEvents/OrganizationEvents.tsx

# Also check if there's any i18n/translation setup or constants in the file
rg "import.*i18n|useTranslation|messages" src/screens/OrganizationEvents/OrganizationEvents.tsx

Length of output: 825

Comment on lines +973 to +1203
fireEvent.click(submitButton);

// Verify fetch call does not include file in FormData
await waitFor(() => {
expect(global.fetch).toHaveBeenCalled();
const fetchCall = (global.fetch as jest.Mock).mock.calls[0];
const [, { body }] = fetchCall;

expect(body instanceof FormData).toBe(true);
expect(body.get('file')).toBeFalsy();
});
});

it('handles file removal correctly', async () => {
renderComponent();

// Open create post modal
await waitFor(() => {
const createButton = screen.getByTestId('createPostModalBtn');
fireEvent.click(createButton);
});

// Add file
const file = new File(['test file content'], 'test-image.png', {
type: 'image/png',
});
const fileInput = screen.getByTestId('addMediaField');

fireEvent.change(fileInput, { target: { files: [file] } });

// Verify file preview is shown
await waitFor(() => {
expect(screen.getByTestId('mediaPreview')).toBeInTheDocument();
});

// Remove file
const removeButton = screen.getByTestId('mediaCloseButton');
fireEvent.click(removeButton);

// Verify preview is removed
expect(screen.queryByTestId('mediaPreview')).not.toBeInTheDocument();

// Fill required fields and submit
const titleInput = screen.getByTestId('modalTitle');
const infoInput = screen.getByTestId('modalinfo');

fireEvent.change(titleInput, { target: { value: 'Test Title' } });
fireEvent.change(infoInput, { target: { value: 'Test Content' } });

const submitButton = screen.getByTestId('createPostBtn');
fireEvent.click(submitButton);

// Verify fetch call does not include removed file
await waitFor(() => {
expect(global.fetch).toHaveBeenCalled();
const fetchCall = (global.fetch as jest.Mock).mock.calls[0];
const [, { body }] = fetchCall;

expect(body instanceof FormData).toBe(true);
expect(body.get('file')).toBeFalsy();
});
});

it('handles different file types correctly', async () => {
renderComponent();

// Open create post modal
await waitFor(() => {
const createButton = screen.getByTestId('createPostModalBtn');
fireEvent.click(createButton);
});

// Test image file
const imageFile = new File(['image content'], 'test-image.png', {
type: 'image/png',
});
const fileInput = screen.getByTestId('addMediaField');

fireEvent.change(fileInput, { target: { files: [imageFile] } });

// Verify image preview
await waitFor(() => {
expect(screen.getByTestId('imagePreview')).toBeInTheDocument();
});

// Remove image
const removeButton = screen.getByTestId('mediaCloseButton');
fireEvent.click(removeButton);

// Test video file
const videoFile = new File(['video content'], 'test-video.mp4', {
type: 'video/mp4',
});
fireEvent.change(fileInput, { target: { files: [videoFile] } });

// Verify video preview
await waitFor(() => {
expect(screen.getByTestId('videoPreview')).toBeInTheDocument();
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using test.each for file handling test cases.

The media file handling test cases have similar patterns and could be refactored using test.each to reduce code duplication and improve maintainability.

describe('Media File Handling in Post Creation', () => {
  const testCases = [
    {
      name: 'appends file to FormData when media file is present',
      file: new File(['test file content'], 'test-image.png', { type: 'image/png' }),
      expectFile: true,
    },
    {
      name: 'does not append file to FormData when no media file is present',
      file: null,
      expectFile: false,
    },
  ];

  test.each(testCases)('$name', async ({ file, expectFile }) => {
    renderComponent();
    
    // Open modal and fill form
    await act(async () => {
      fireEvent.click(screen.getByTestId('createPostModalBtn'));
    });
    
    // Add file if present
    if (file) {
      const fileInput = screen.getByTestId('addMediaField');
      await act(async () => {
        fireEvent.change(fileInput, { target: { files: [file] } });
      });
    }
    
    // Submit and verify
    await act(async () => {
      fireEvent.click(screen.getByTestId('createPostBtn'));
    });
    
    await waitFor(() => {
      const fetchCall = (global.fetch as jest.Mock).mock.calls[0];
      const [, { body }] = fetchCall;
      expect(body instanceof FormData).toBe(true);
      expect(!!body.get('file')).toBe(expectFile);
    });
  });
});

Comment on lines +1059 to +1072
// Submit form
const submitButton = screen.getByTestId('createPostBtn');
fireEvent.click(submitButton);

// Verify fetch call includes file in FormData
await waitFor(() => {
expect(global.fetch).toHaveBeenCalled();
const fetchCall = (global.fetch as jest.Mock).mock.calls[0];
const [, { body }] = fetchCall;

expect(body instanceof FormData).toBe(true);
expect(body.get('file')).toBeTruthy();
expect(body.get('file')).toEqual(file);
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve async operation handling in form submission test.

The form submission test should use await act() to properly handle async state updates.

-      // Submit form
-      const submitButton = screen.getByTestId('createPostBtn');
-      fireEvent.click(submitButton);
+      // Submit form
+      await act(async () => {
+        const submitButton = screen.getByTestId('createPostBtn');
+        fireEvent.click(submitButton);
+      });
📝 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
// Submit form
const submitButton = screen.getByTestId('createPostBtn');
fireEvent.click(submitButton);
// Verify fetch call includes file in FormData
await waitFor(() => {
expect(global.fetch).toHaveBeenCalled();
const fetchCall = (global.fetch as jest.Mock).mock.calls[0];
const [, { body }] = fetchCall;
expect(body instanceof FormData).toBe(true);
expect(body.get('file')).toBeTruthy();
expect(body.get('file')).toEqual(file);
});
// Submit form
await act(async () => {
const submitButton = screen.getByTestId('createPostBtn');
fireEvent.click(submitButton);
});
// Verify fetch call includes file in FormData
await waitFor(() => {
expect(global.fetch).toHaveBeenCalled();
const fetchCall = (global.fetch as jest.Mock).mock.calls[0];
const [, { body }] = fetchCall;
expect(body instanceof FormData).toBe(true);
expect(body.get('file')).toBeTruthy();
expect(body.get('file')).toEqual(file);
});

Comment on lines +1232 to +1268
test('opens edit modal with video post and verifies state', async () => {
setItem('id', '123');

const videoProps = {
...props,
postPhoto: null,
postVideo: 'test-video.mp4',
pinned: true,
};

render(
<MockedProvider addTypename={false} link={link}>
<I18nextProvider i18n={i18nForTest}>
<OrgPostCard {...videoProps} />
</I18nextProvider>
</MockedProvider>,
);

await wait();

// Open the edit modal
userEvent.click(screen.getByTestId('cardVid'));
userEvent.click(screen.getByTestId('moreiconbtn'));
userEvent.click(screen.getByTestId('editPostModalBtn'));

// Verify the modal is open
expect(screen.getByTestId('modalOrganizationHeader')).toBeInTheDocument();

// Verify that the preview URL is set to the video URL
const videoPreview = screen.getByTestId('videoPreview');
const source = videoPreview.querySelector('source');
expect(source).toHaveAttribute('src', 'test-video.mp4');

// Verify form state is initialized correctly
expect(screen.getByTestId('updateTitle')).toHaveValue(videoProps.postTitle);
expect(screen.getByTestId('updateText')).toHaveValue(videoProps.postInfo);
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add assertions to verify component state.

The test "opens edit modal with video post and verifies state" lacks assertions for modal state and video preview visibility.

Add these assertions:

// Add after line 1267
expect(screen.getByTestId('editPostModal')).toBeInTheDocument();
expect(videoPreview).toBeVisible();
expect(screen.getByTestId('closePreview')).toBeInTheDocument();

Comment on lines +888 to +908
test('prevents default event behavior', () => {
render(
<MockedProvider>
<I18nextProvider i18n={i18nForTest}>
<OrgPostCard {...mockProps} />
</I18nextProvider>
</MockedProvider>,
);

// Open edit modal to access the clear button
fireEvent.click(screen.getByAltText('image'));
fireEvent.click(screen.getByTestId('moreiconbtn'));
fireEvent.click(screen.getByTestId('editPostModalBtn'));

// Get the clear button and simulate click with mock event
const clearButton = screen.getByTestId('closeimage');
fireEvent.click(clearButton, { preventDefault: jest.fn() });

// Verify preventDefault was called
expect(clearButton.onclick).toBeDefined();
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix event mocking in preventDefault test.

The current implementation incorrectly mocks preventDefault. The event object should be created and passed properly.

Apply this fix:

test('prevents default event behavior', () => {
  render(
    <MockedProvider>
      <I18nextProvider i18n={i18nForTest}>
        <OrgPostCard {...mockProps} />
      </I18nextProvider>
    </MockedProvider>
  );

  // Open edit modal
  fireEvent.click(screen.getByAltText('image'));
  fireEvent.click(screen.getByTestId('moreiconbtn'));
  fireEvent.click(screen.getByTestId('editPostModalBtn'));

  const clearButton = screen.getByTestId('closeimage');
  const mockEvent = { preventDefault: jest.fn() };
  
  fireEvent.click(clearButton, mockEvent);
  expect(mockEvent.preventDefault).toHaveBeenCalled();
});

Comment on lines +797 to +825
test('clears image input and resets related state', () => {
// Mock getElementById for image input
const mockImageInput = document.createElement('input');
mockImageInput.setAttribute('id', 'postImageUrl');
mockImageInput.value = 'test.jpg';
document.getElementById = jest.fn((id) =>
id === 'postImageUrl' ? mockImageInput : null,
);

render(
<MockedProvider>
<I18nextProvider i18n={i18nForTest}>
<OrgPostCard {...mockProps} />
</I18nextProvider>
</MockedProvider>,
);

// Open edit modal to access the clear button
fireEvent.click(screen.getByAltText('image'));
fireEvent.click(screen.getByTestId('moreiconbtn'));
fireEvent.click(screen.getByTestId('editPostModalBtn'));

// Trigger clear media input
const clearButton = screen.getByTestId('closeimage');
fireEvent.click(clearButton);

// Check if image input value is cleared
expect(mockImageInput.value).toBe('');
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use jest.spyOn for document.getElementById mocks.

The current implementation directly overwrites document.getElementById which can affect other tests. Use jest.spyOn instead and ensure cleanup.

Apply this pattern to both image and video input tests:

test('clears image input and resets related state', () => {
  const mockImageInput = document.createElement('input');
  mockImageInput.setAttribute('id', 'postImageUrl');
  mockImageInput.value = 'test.jpg';
  
  const spy = jest.spyOn(document, 'getElementById').mockImplementation((id) =>
    id === 'postImageUrl' ? mockImageInput : null
  );

  // ... rest of the test ...

  spy.mockRestore(); // Clean up
});

Also applies to: 827-862

@palisadoes palisadoes added the ignore-sensitive-files-pr The contributor has a legitimate reason for editiing protected files label Nov 5, 2024
@palisadoes
Copy link
Contributor

Please fix the failing tests, the conflicting file and make code rabbit approve the PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ignore-sensitive-files-pr The contributor has a legitimate reason for editiing protected files
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants