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

Development: Migrate client code for posting header and footer components #9932

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

Conversation

asliayk
Copy link
Contributor

@asliayk asliayk commented Dec 2, 2024

Checklist

General

Client

Motivation and Context

  • For the client migration, following files needs to be updated:
-- File Path Standalone Signals Inject
76 /shared/metis/posting-footer/post-footer/post-footer.component.html
77 /shared/metis/posting-footer/post-footer/post-footer.component.ts
78 /shared/metis/posting-footer/posting-footer.directive.ts
79 /shared/metis/posting-header/answer-post-header/answer-post-header.component.html
80 /shared/metis/posting-header/answer-post-header/answer-post-header.component.ts
81 /shared/metis/posting-header/post-header/post-header.component.html
82 /shared/metis/posting-header/post-header/post-header.component.ts
83 /shared/metis/posting-header/posting-header.directive.ts
  • The AnswerPostHeader and PostHeader components are almost identical, causing code duplication, and one of them needs to be removed.

  • In this case, the PostingHeaderDirective would also be used for only a single component, making its existence redundant, and it should be removed as well.

  • Similarly, the AnswerPostFooter component was previously removed due to duplication with the PostFooter component. This means the PostingFooterDirective is now being used for only one component and should also be removed to avoid unnecessary abstraction.

Description

  • The PostHeader and AnswerPostHeader components have been merged into a single component named PostingHeaderComponent.

  • The PostingHeaderDirective and PostingFooterDirective have been removed.

  • The PostingHeaderComponent and PostFooterComponent have been updated to use inject and signals.

  • Tests have been updated accordingly.

Known issue: All signals, except those related to @ViewChild, have been updated. Updating viewChild requires making the components standalone, which I will address in a follow-up PR as it will impact other components.

Steps for Testing

Prerequisites:

  • 1 Instructor/Student
  • 1 Course with Communication enabled
  1. Log in to Artemis
  2. Navigate to Communication section of a course
  3. Test the communication section by viewing channels, sending posts and replies

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Test Coverage

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced PostingHeaderComponent for improved clarity and functionality in post headers.
    • Updated component names for consistency, including changes from <jhi-answer-post-header> to <jhi-posting-header>.
  • Bug Fixes

    • Enhanced error handling for undefined posting objects in the PostingHeaderComponent.
  • Refactor

    • Transitioned from direct property access to function calls for several properties in the post-footer and posting-header components, improving code robustness.
    • Consolidated header functionality by removing legacy components like AnswerPostHeaderComponent and PostHeaderComponent.
  • Tests

    • Expanded test coverage for the PostingHeaderComponent, ensuring accurate rendering and behavior under various conditions.

@asliayk asliayk self-assigned this Dec 2, 2024
@asliayk asliayk requested a review from a team as a code owner December 2, 2024 21:52
@github-actions github-actions bot added tests client Pull requests that update TypeScript code. (Added Automatically!) labels Dec 2, 2024
@asliayk asliayk marked this pull request as draft December 2, 2024 21:57
Copy link

coderabbitai bot commented Dec 2, 2024

Walkthrough

The pull request introduces significant changes to the Angular components related to posting functionality. Key modifications include renaming components for clarity, such as changing <jhi-answer-post-header> to <jhi-posting-header>, and updating property names like isReadOnlyMode to readOnlyMode. The MetisModule has been updated to declare the new PostingHeaderComponent, consolidating header functionalities. Additionally, several components have been refactored to utilize function calls for property access, enhancing the component's behavior and lifecycle management. Several obsolete component files have also been removed.

Changes

File Path Change Summary
src/main/webapp/app/shared/metis/answer-post/answer-post.component.html Component renamed from <jhi-answer-post-header> to <jhi-posting-header>, property isReadOnlyMode changed to readOnlyMode.
src/main/webapp/app/shared/metis/metis.module.ts Removed AnswerPostHeaderComponent and PostHeaderComponent, added PostingHeaderComponent.
src/main/webapp/app/shared/metis/post/post.component.html Renamed <jhi-post-header> to <jhi-posting-header>, <jhi-post-content> to <jhi-posting-content>.
src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.html Updated property access from direct to function calls for several properties.
src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts Changed class to no longer extend PostingFooterDirective, updated input/output property declarations.
src/main/webapp/app/shared/metis/posting-footer/posting-footer.directive.ts File deleted, removing PostingFooterDirective class.
src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html File deleted, removing header component for answer posts.
src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.ts File deleted, removing Angular component for answer post header.
src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.ts File deleted, removing PostHeaderComponent.
src/main/webapp/app/shared/metis/posting-header/posting-header.component.html Updated property access with optional chaining and function calls.
src/main/webapp/app/shared/metis/posting-header/posting-header.component.ts Changed from abstract directive to concrete component, updated input/output declarations.
src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts Updated import path for PostHeaderComponent to PostingHeaderComponent.
src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts Enhanced test suite with new dependency injection methods and updated input handling.
src/test/javascript/spec/component/shared/metis/postings-header/answer-post-header/answer-post-header.component.spec.ts File deleted, removing tests for the AnswerPostHeaderComponent.
src/test/javascript/spec/component/shared/metis/postings-header/post-header/post-header.component.spec.ts File deleted, removing tests for the PostHeaderComponent.
src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts Introduced comprehensive test suite for PostingHeaderComponent, validating various functionalities.

Possibly related PRs

Suggested labels

ready to merge, small, component:Communication

Suggested reviewers

  • JohannesWt
  • florian-glombik
  • HawKhiem
  • sachmii
  • edkaya
  • Feras797
  • rabeatwork

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 or @coderabbitai title 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

@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: 6

🧹 Outside diff range and nitpick comments (6)
src/main/webapp/app/shared/metis/posting-header/post-header/posting-header.component.html (2)

3-14: Consider optimizing profile picture bindings with a local variable.

While the implementation is correct, the multiple posting() function calls could be optimized by storing the author in a local variable.

- @if (posting()?.author) {
+ @if (posting()?.author; as author) {
     <span class="d-inline-flex align-items-start gap-2 flex-wrap">
         <jhi-profile-picture
             imageSizeInRem="2.15"
             fontSizeInRem="0.9"
             imageId="post-profile-picture"
             defaultPictureId="post-default-profile-picture"
             [isGray]="isDeleted()"
-            [authorId]="posting()?.author?.id"
-            [authorName]="posting()?.author?.name"
-            [imageUrl]="posting()?.author?.imageUrl"
-            [isEditable]="currentUser !== undefined && posting()?.author?.id === currentUser.id"
+            [authorId]="author.id"
+            [authorName]="author.name"
+            [imageUrl]="author.imageUrl"
+            [isEditable]="currentUser !== undefined && author.id === currentUser.id"
         >

33-34: Consider extracting creation date to improve readability.

While the implementation is correct, readability could be improved by storing the creation date in a local variable.

+ @if (posting()?.creationDate; as creationDate) {
     <span class="fs-small" [disableTooltip]="postingIsOfToday" 
-         ngbTooltip="{{ posting()?.creationDate | artemisDate: 'time' }}">
+         ngbTooltip="{{ creationDate | artemisDate: 'time' }}">
-         {{ postingIsOfToday ? (posting()?.creationDate | artemisDate: 'time') : (posting()?.creationDate | artemisDate: 'short-date') }}
+         {{ postingIsOfToday ? (creationDate | artemisDate: 'time') : (creationDate | artemisDate: 'short-date') }}
     </span>
+ }
src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (1)

89-94: Avoid mutating input properties directly

The method should avoid mutating properties of sortedAnswerPosts() directly, as it may lead to unexpected side effects. Instead, create a copy before performing operations.

Apply this diff to prevent direct mutation:

  groupAnswerPosts(): void {
      if (!this.sortedAnswerPosts() || this.sortedAnswerPosts().length === 0) {
          this.groupedAnswerPosts = [];
          return;
      }

-     const sortedAnswerPosts = this.sortedAnswerPosts()
+     const sortedAnswerPosts = [...this.sortedAnswerPosts()]
          .reverse()
          .map((post) => {
              (post as any).creationDateDayjs = post.creationDate ? dayjs(post.creationDate) : undefined;
              return post;
          });
src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.html (1)

Line range hint 1-28: Methods used in template may cause performance issues

Several properties in the template are now invoked as functions (e.g., showAnswers(), lastReadDate()). Calling methods or getters in the template can lead to performance degradation due to frequent change detection. Consider using variables instead.

Apply this diff to store function results in variables:

  export class PostFooterComponent implements OnInit, OnDestroy, AfterContentChecked, OnChanges {
+     showAnswersValue = this.showAnswers();
+     lastReadDateValue = this.lastReadDate();
+     readOnlyModeValue = this.readOnlyMode();
+     isCommunicationPageValue = this.isCommunicationPage();
+     isThreadSidebarValue = this.isThreadSidebar();
+     hasChannelModerationRightsValue = this.hasChannelModerationRights();

      // ...
  }

  // In the template, use the variables instead of function calls
  @if (showAnswersValue) {
      <!-- ... -->
      <jhi-answer-post
-         [lastReadDate]="lastReadDate()"
-         [readOnlyMode]="readOnlyMode()"
          <!-- ... -->
-         [isCommunicationPage]="isCommunicationPage()"
-         [isThreadSidebar]="isThreadSidebar()"
          <!-- ... -->
-         [hasChannelModerationRights]="hasChannelModerationRights()"
      />
  }
src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts (2)

8-8: Consider using path aliases instead of relative paths

The import path with multiple parent directory references (../../../../../../../) is fragile and hard to maintain. Consider using TypeScript path aliases to make imports more readable and maintainable.

Example configuration in tsconfig.json:

{
  "compilerOptions": {
    "paths": {
      "@shared/*": ["src/main/webapp/app/shared/*"]
    }
  }
}

Then the import could be simplified to:

import { PostingHeaderComponent } from '@shared/metis/posting-header/post-header/posting-header.component';

Line range hint 84-359: Consider organizing related test cases using describe blocks

While the test cases are comprehensive and follow best practices, the readability could be improved by grouping related tests. Consider organizing the tests into logical describe blocks.

Example organization:

describe('PostComponent', () => {
  describe('Answer Management', () => {
    it('should sort answers', () => {/*...*/});
    it('should not sort empty array of answers', () => {/*...*/});
  });

  describe('Navigation', () => {
    it('should set router link and query params', () => {/*...*/});
    it('should navigate to channel when not on messaging page', () => {/*...*/});
    // ... other navigation tests
  });

  describe('User Interactions', () => {
    it('should open create answer post modal', () => {/*...*/});
    it('should close create answer post modal', () => {/*...*/});
    // ... other interaction tests
  });

  // ... other test groups
});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 701dd72 and 063353a.

📒 Files selected for processing (16)
  • src/main/webapp/app/shared/metis/answer-post/answer-post.component.html (1 hunks)
  • src/main/webapp/app/shared/metis/metis.module.ts (3 hunks)
  • src/main/webapp/app/shared/metis/post/post.component.html (1 hunks)
  • src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.html (3 hunks)
  • src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (4 hunks)
  • src/main/webapp/app/shared/metis/posting-footer/posting-footer.directive.ts (0 hunks)
  • src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html (0 hunks)
  • src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.ts (0 hunks)
  • src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.ts (0 hunks)
  • src/main/webapp/app/shared/metis/posting-header/post-header/posting-header.component.html (2 hunks)
  • src/main/webapp/app/shared/metis/posting-header/post-header/posting-header.component.ts (5 hunks)
  • src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts (2 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts (5 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-header/answer-post-header/answer-post-header.component.spec.ts (0 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-header/post-header/post-header.component.spec.ts (0 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts (1 hunks)
💤 Files with no reviewable changes (6)
  • src/main/webapp/app/shared/metis/posting-footer/posting-footer.directive.ts
  • src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html
  • src/test/javascript/spec/component/shared/metis/postings-header/post-header/post-header.component.spec.ts
  • src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.ts
  • src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.ts
  • src/test/javascript/spec/component/shared/metis/postings-header/answer-post-header/answer-post-header.component.spec.ts
🧰 Additional context used
📓 Path-based instructions (10)
src/main/webapp/app/shared/metis/posting-header/post-header/posting-header.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/metis/metis.module.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/metis/answer-post/answer-post.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/shared/metis/post/post.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/shared/metis/posting-header/post-header/posting-header.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

📓 Learnings (1)
src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts (1)
Learnt from: rabeatwork
PR: ls1intum/Artemis#9103
File: src/test/javascript/spec/component/shared/metis/postings-header/answer-post-header/answer-post-header.component.spec.ts:94-94
Timestamp: 2024-11-12T12:52:03.805Z
Learning: The `#today-flag` ID is only present in the test files and not in the actual component's HTML or TypeScript files.
🪛 Biome (1.9.4)
src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts

[error] 53-58: Disallow duplicate setup and teardown hooks.

Disallow beforeEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

🪛 GitHub Check: client-tests-selected
src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts

[failure] 150-150:
Property 'answers' does not exist on type 'InputSignal<Posting | undefined>'.

🪛 GitHub Check: client-tests
src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts

[failure] 150-150:
Property 'answers' does not exist on type 'InputSignal<Posting | undefined>'.

🔇 Additional comments (18)
src/main/webapp/app/shared/metis/posting-header/post-header/posting-header.component.html (2)

Line range hint 18-24: LGTM! Well-structured role badge implementation.

The implementation correctly handles translations, icons, and null safety.


37-43: LGTM! Clean implementation of resolved post indicator.

The code correctly uses the new @if syntax and properly implements the icon with tooltip.

src/main/webapp/app/shared/metis/posting-header/post-header/posting-header.component.ts (5)

1-31: Use of Signals and Dependency Injection aligns with Angular 16 standards

The implementation properly utilizes Angular 16 features such as input, output, and inject functions for signals and dependency injection. The input properties are correctly declared using input<T>(), and services are injected using inject(), promoting a modern and clean codebase.


70-73: Type guard isPost is correctly implemented

The isPost function accurately acts as a type guard to differentiate between Post and AnswerPost instances, ensuring type safety when accessing properties specific to Post.


74-77: Getter isPostResolved appropriately uses the type guard

The isPostResolved getter method effectively utilizes the isPost type guard to safely check the resolved property of a Post instance.


79-85: Lifecycle hook ngOnChanges correctly re-evaluates properties

The ngOnChanges method properly calls setUserProperties() and setUserAuthorityIconAndTooltip() to update relevant properties when input bindings change, ensuring the component stays in sync with its inputs.


90-91: Ensure all resources are cleaned up in ngOnDestroy

The ngOnDestroy method correctly closes the modal if it is open. After addressing the subscription unsubscription issue, the component will properly clean up its resources upon destruction.

src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (5)

1-9: Update to Angular 16 injectables and input/output signals

The component correctly adopts Angular 16 standards by using inject, input, and output functions for dependency injection and property bindings. This modernizes the component and aligns it with the latest Angular practices.


22-32: Proper declaration of input and output properties using signals

The input and output properties are appropriately defined using input<T>() and output<T>(), enhancing reactivity and interoperability within the component.


47-48: Services are correctly injected using inject function

The MetisService and ChangeDetectorRef are injected using inject(), aligning with Angular 16's improved dependency injection patterns.


83-85: Ensure createEmptyAnswerPost correctly initializes answerPost

The answerPost.post assignment uses this.posting(), which ensures the new answer post is associated with the correct post. This maintains the integrity of the data relationships.


89-102: Efficient grouping and sorting of answer posts

The groupAnswerPosts method efficiently reverses, maps, sorts, and groups the answer posts to optimize display logic. This implementation enhances performance and user experience.

src/main/webapp/app/shared/metis/metis.module.ts (2)

70-70: Component declarations updated appropriately

The PostingHeaderComponent is correctly declared and exported, replacing the AnswerPostHeaderComponent and PostHeaderComponent. This consolidation simplifies the module and reduces redundancy.


94-94: Exports updated to include PostingHeaderComponent

The module exports are updated to include PostingHeaderComponent, ensuring that it can be used across the application where needed.

src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts (1)

Line range hint 64-75: LGTM: Well-structured test with proper initialization

The test case properly initializes the component within the Angular injection context and includes clear expectations.

src/main/webapp/app/shared/metis/post/post.component.html (1)

Line range hint 12-20: LGTM: Clean component renaming and modern Angular syntax

The component has been correctly renamed from jhi-post-header to jhi-posting-header and uses proper input bindings. The template also effectively uses Angular 17's new control flow syntax.

src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts (1)

110-158: LGTM: Well-structured parameterized tests

Excellent use of parameterized tests with it.each to test different user roles and their corresponding icons and tooltips. This approach reduces code duplication and makes the test cases more maintainable.

src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts (1)

72-74: LGTM! Test setup follows best practices

The test module configuration correctly uses MockComponent for dependencies and follows the testing guidelines.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 2, 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: 1

🧹 Outside diff range and nitpick comments (6)
src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (2)

22-31: Consider adding default values for all input signals.

Some input signals like lastReadDate and posting lack default values, which could lead to undefined behavior. Consider providing default values for all inputs.

-    lastReadDate = input<dayjs.Dayjs | undefined>();
+    lastReadDate = input<dayjs.Dayjs | undefined>(undefined);
-    posting = input<Posting>();
+    posting = input<Posting | undefined>(undefined);

Line range hint 94-107: Fix type safety issues with creationDateDayjs property.

Using (post as any).creationDateDayjs is unsafe. Consider creating an interface for the extended post type.

+interface PostWithDayjs extends AnswerPost {
+    creationDateDayjs?: dayjs.Dayjs;
+}

-        const sortedAnswerPosts = this.sortedAnswerPosts()
+        const sortedAnswerPosts: PostWithDayjs[] = this.sortedAnswerPosts()
             .slice()
             .reverse()
             .map((post) => {
-                (post as any).creationDateDayjs = post.creationDate ? dayjs(post.creationDate) : undefined;
+                const postWithDayjs: PostWithDayjs = { ...post };
+                postWithDayjs.creationDateDayjs = post.creationDate ? dayjs(post.creationDate) : undefined;
-                return post;
+                return postWithDayjs;
             });

-        const sortedPosts = sortedAnswerPosts.sort((a, b) => {
-            const aDate = (a as any).creationDateDayjs;
-            const bDate = (b as any).creationDateDayjs;
+        const sortedPosts = sortedAnswerPosts.sort((a, b) => {
+            const aDate = a.creationDateDayjs;
+            const bDate = b.creationDateDayjs;
             return aDate?.valueOf() - bDate?.valueOf();
         });
src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts (1)

79-84: Add edge case tests for groupAnswerPosts.

The current test only verifies that groups are created. Consider adding tests for:

  • Empty array
  • Single post
  • Posts with same author but different timestamps
  • Posts with missing creation dates
src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts (1)

59-68: Enhance header visibility test assertions.

The test could be more specific about why the header should be visible when isConsecutive is false.

 it('should contain the posting header when isConsecutive is false', () => {
     runInInjectionContext(fixture.debugElement.injector, () => {
         component.isConsecutive = input<boolean>(false);
         component.posting = metisResolvingAnswerPostUser1;
     });

     fixture.detectChanges();
     const header = debugElement.query(By.css('jhi-posting-header'));
     expect(header).not.toBeNull();
+    // Verify header properties
+    const headerComponent = ngMocks.findInstance(header, PostingHeaderComponent);
+    expect(headerComponent.posting).toBe(metisResolvingAnswerPostUser1);
 });
src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts (2)

169-178: Enhance undefined posting test coverage

While the test handles the basic undefined case, consider adding more assertions to verify the complete component state:

 it('should handle undefined posting gracefully', () => {
     runInInjectionContext(injector, () => {
         component.posting = input<Posting>();
         component.ngOnInit();
         fixture.detectChanges();

         expect(component.isPostResolved).toBeFalse();
         expect(getElement(debugElement, '.resolved')).toBeNull();
+        expect(component.userAuthorityIcon).toBeDefined();
+        expect(component.userAuthorityTooltip).toBeDefined();
+        expect(component.isAuthorOfPosting).toBeFalse();
+        expect(getElement(debugElement, '#today-flag')).toBeNull();
+        expect(getElement(debugElement, '#header-author-date')).not.toBeNull();
     });
 });

62-70: Improve date-related test assertions

The date-related tests could be more specific in their assertions to ensure the complete date handling logic:

 it('should set date information correctly for post of today', () => {
     runInInjectionContext(injector, () => {
         component.posting = input<Posting>(metisPostLectureUser1);
         component.ngOnInit();
         fixture.detectChanges();

         expect(getElement(debugElement, '#today-flag')).toBeDefined();
+        const headerDate = getElement(debugElement, '#header-author-date');
+        expect(headerDate.textContent).toContain('artemisApp.metis.today');
     });
 });

 it('should not set today flag for posts not created today', () => {
     runInInjectionContext(injector, () => {
         const pastDatePost = {
             ...metisPostLectureUser1,
             creationDate: dayjs().subtract(1, 'day').toDate(),
         } as unknown as Post;
         component.posting = input<Posting>(pastDatePost);
         component.ngOnInit();
         fixture.detectChanges();

         expect(getElement(debugElement, '#today-flag')).toBeNull();
+        const headerDate = getElement(debugElement, '#header-author-date');
+        expect(headerDate.textContent).not.toContain('artemisApp.metis.today');
+        expect(headerDate.textContent).toContain(pastDatePost.creationDate.toLocaleDateString());
     });
 });

Also applies to: 72-84

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 063353a and ecba0b5.

📒 Files selected for processing (4)
  • src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (5 hunks)
  • src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts (3 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts (5 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

📓 Learnings (1)
src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts (1)
Learnt from: rabeatwork
PR: ls1intum/Artemis#9103
File: src/test/javascript/spec/component/shared/metis/postings-header/answer-post-header/answer-post-header.component.spec.ts:94-94
Timestamp: 2024-11-12T12:52:03.805Z
Learning: The `#today-flag` ID is only present in the test files and not in the actual component's HTML or TypeScript files.
🔇 Additional comments (3)
src/test/javascript/spec/component/shared/metis/postings-header/posting-header.component.spec.ts (3)

1-28: LGTM: Import statements are well-organized and complete

The imports cover all necessary testing utilities, mock components/services, and models required for the test suite.


35-56: Consolidate duplicate beforeEach blocks

The beforeEach blocks could be combined to improve readability and maintenance.

-    beforeEach(async () => {
-        await TestBed.configureTestingModule({
-            imports: [MockModule(FormsModule), MockModule(ReactiveFormsModule), MockDirective(NgbTooltip), MockModule(MetisModule)],
-            providers: [FormBuilder, { provide: MetisService, useClass: MockMetisService }, { provide: AccountService, useClass: MockAccountService }],
-            declarations: [
-                PostingHeaderComponent,
-                FaIconComponent,
-                MockComponent(PostCreateEditModalComponent),
-                MockPipe(ArtemisTranslatePipe),
-                MockPipe(ArtemisDatePipe),
-                MockComponent(PostingMarkdownEditorComponent),
-                MockComponent(PostingButtonComponent),
-                MockComponent(ConfirmIconComponent),
-                MockComponent(ProfilePictureComponent),
-            ],
-        }).compileComponents();
-    });
-
-    beforeEach(() => {
-        fixture = TestBed.createComponent(PostingHeaderComponent);
-        component = fixture.componentInstance;
-        debugElement = fixture.debugElement;
-        injector = fixture.debugElement.injector;
-    });
+    beforeEach(async () => {
+        await TestBed.configureTestingModule({
+            imports: [MockModule(FormsModule), MockModule(ReactiveFormsModule), MockDirective(NgbTooltip), MockModule(MetisModule)],
+            providers: [FormBuilder, { provide: MetisService, useClass: MockMetisService }, { provide: AccountService, useClass: MockAccountService }],
+            declarations: [
+                PostingHeaderComponent,
+                FaIconComponent,
+                MockComponent(PostCreateEditModalComponent),
+                MockPipe(ArtemisTranslatePipe),
+                MockPipe(ArtemisDatePipe),
+                MockComponent(PostingMarkdownEditorComponent),
+                MockComponent(PostingButtonComponent),
+                MockComponent(ConfirmIconComponent),
+                MockComponent(ProfilePictureComponent),
+            ],
+        }).compileComponents();
+
+        fixture = TestBed.createComponent(PostingHeaderComponent);
+        component = fixture.componentInstance;
+        debugElement = fixture.debugElement;
+        injector = fixture.debugElement.injector;
+    });

108-156: LGTM: Comprehensive user role testing

The test cases for user roles are well-structured using Jest's parameterized testing feature. They thoroughly verify both the visual elements and component properties for different user roles.

Copy link
Contributor

@FelberMartin FelberMartin left a comment

Choose a reason for hiding this comment

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

Tested on TS5: Writing message, reply to message, mark as resolve, add reaction, edit message. All works as before.

Copy link

@eylulnc eylulnc left a comment

Choose a reason for hiding this comment

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

Tested in TS5, works as expected

Copy link
Member

@anian03 anian03 left a comment

Choose a reason for hiding this comment

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

Tested on TS5 during testing session, I noticed this:

  • The number of replies in the thread view does not update, it is stuck at 0 replies image
  • When sending a reply in the last thread of a conversation, sometimes the sent reply does not appear until refreshing the page or sending another reply. But this is not consistently reproducible, so may just be bad connection right now

Copy link

@HawKhiem HawKhiem left a comment

Choose a reason for hiding this comment

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

Tested on TS5. Other than the problem Anian mentioned above, eveything else works fine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Pull requests that update TypeScript code. (Added Automatically!) lock:artemis-test5 ready for review tests
Projects
Status: Ready For Review
Development

Successfully merging this pull request may close these issues.

5 participants