+
-
+
}
-
+
@if (!isDeleted) {
-
+
+
@if (posting.isSaved) {
}
+ @if (isConsecutive()) {
+
+ {{ posting.creationDate | artemisDate: 'time' }}
+
+ }
@if (!isConsecutive()) {
implements
timeDiff = currentDate.diff(lastDate, 'minute');
}
- if (currentPost.author?.id === currentGroup.author?.id && timeDiff < 1 && timeDiff >= 0) {
+ if (currentPost.author?.id === currentGroup.author?.id && timeDiff < 5 && timeDiff >= 0) {
currentGroup.posts.push({ ...currentPost, isConsecutive: true }); // consecutive post
} else {
groups.push(currentGroup);
diff --git a/src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html b/src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html
index a03fb20cced3..f7d42ac92f61 100644
--- a/src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html
+++ b/src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html
@@ -31,7 +31,11 @@
,
}
- {{ postingIsOfToday ? (posting.creationDate | artemisDate: 'time') : (posting.creationDate | artemisDate: 'short-date') }}
+ {{
+ postingIsOfToday
+ ? (posting.creationDate | artemisDate: 'time')
+ : (posting.creationDate | artemisDate: 'short-date') + ' - ' + (posting.creationDate | artemisDate: 'time')
+ }}
@if (!!isCommunicationPage && (!lastReadDate || (lastReadDate && posting.creationDate && posting.creationDate.isAfter(lastReadDate))) && !isAuthorOfPosting) {
diff --git a/src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html b/src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html
index 1bce282adaed..deadcb377cdd 100644
--- a/src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html
+++ b/src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html
@@ -31,7 +31,11 @@
,
}
- {{ postingIsOfToday ? (posting.creationDate | artemisDate: 'time') : (posting.creationDate | artemisDate: 'short-date') }}
+ {{
+ postingIsOfToday
+ ? (posting.creationDate | artemisDate: 'time')
+ : (posting.creationDate | artemisDate: 'short-date') + ' - ' + (posting.creationDate | artemisDate: 'time')
+ }}
@if (posting.resolved) {
diff --git a/src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts b/src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts
index 3a84c4f12e87..45badc3c912d 100644
--- a/src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts
+++ b/src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts
@@ -1,13 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AnswerPostComponent } from 'app/shared/metis/answer-post/answer-post.component';
import { DebugElement, input, runInInjectionContext } from '@angular/core';
-import { MockComponent, MockModule, MockPipe, ngMocks } from 'ng-mocks';
+import { MockComponent, MockDirective, MockModule, MockPipe, ngMocks } from 'ng-mocks';
import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe';
import { By } from '@angular/platform-browser';
import { AnswerPostHeaderComponent } from 'app/shared/metis/posting-header/answer-post-header/answer-post-header.component';
import { AnswerPostReactionsBarComponent } from 'app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component';
import { PostingContentComponent } from 'app/shared/metis/posting-content/posting-content.components';
-import { metisResolvingAnswerPostUser1 } from '../../../../helpers/sample/metis-sample-data';
+import { metisPostExerciseUser1, metisResolvingAnswerPostUser1 } from '../../../../helpers/sample/metis-sample-data';
import { OverlayModule } from '@angular/cdk/overlay';
import { AnswerPostCreateEditModalComponent } from 'app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component';
import { DOCUMENT } from '@angular/common';
@@ -17,6 +17,13 @@ import { MetisService } from 'app/shared/metis/metis.service';
import { MockMetisService } from '../../../../helpers/mocks/service/mock-metis-service.service';
import { Posting, PostingType } from 'app/entities/metis/posting.model';
import { AnswerPost } from 'app/entities/metis/answer-post.model';
+import dayjs from 'dayjs/esm';
+import { ArtemisDatePipe } from '../../../../../../../main/webapp/app/shared/pipes/artemis-date.pipe';
+import { ArtemisTranslatePipe } from '../../../../../../../main/webapp/app/shared/pipes/artemis-translate.pipe';
+import { TranslateDirective } from '../../../../../../../main/webapp/app/shared/language/translate.directive';
+import { MockTranslateService } from '../../../../helpers/mocks/service/mock-translate.service';
+import { TranslateService } from '@ngx-translate/core';
+import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
describe('AnswerPostComponent', () => {
let component: AnswerPostComponent;
@@ -30,7 +37,7 @@ describe('AnswerPostComponent', () => {
document.body.appendChild(mainContainer);
return TestBed.configureTestingModule({
- imports: [OverlayModule, MockModule(BrowserAnimationsModule)],
+ imports: [OverlayModule, MockModule(BrowserAnimationsModule), MockDirective(NgbTooltip)],
declarations: [
AnswerPostComponent,
MockPipe(HtmlForMarkdownPipe),
@@ -38,10 +45,14 @@ describe('AnswerPostComponent', () => {
MockComponent(PostingContentComponent),
MockComponent(AnswerPostCreateEditModalComponent),
MockComponent(AnswerPostReactionsBarComponent),
+ ArtemisDatePipe,
+ ArtemisTranslatePipe,
+ MockDirective(TranslateDirective),
],
providers: [
{ provide: DOCUMENT, useValue: document },
{ provide: MetisService, useClass: MockMetisService },
+ { provide: TranslateService, useClass: MockTranslateService },
],
})
.compileComponents()
@@ -237,4 +248,31 @@ describe('AnswerPostComponent', () => {
expect(component.posting).toBeInstanceOf(AnswerPost);
expect(spy).toHaveBeenCalled();
});
+
+ it('should display post-time span when isConsecutive() returns true', () => {
+ const fixedDate = dayjs('2024-12-06T23:39:27.080Z');
+ component.posting = { ...metisPostExerciseUser1, creationDate: fixedDate };
+
+ jest.spyOn(component, 'isConsecutive').mockReturnValue(true);
+ fixture.detectChanges();
+
+ const postTimeDebugElement = debugElement.query(By.css('span.post-time'));
+ const postTimeElement = postTimeDebugElement.nativeElement as HTMLElement;
+
+ expect(postTimeDebugElement).toBeTruthy();
+
+ const expectedTime = dayjs(fixedDate).format('HH:mm');
+ expect(postTimeElement.textContent?.trim()).toBe(expectedTime);
+ });
+
+ it('should not display post-time span when isConsecutive() returns false', () => {
+ const fixedDate = dayjs('2024-12-06T23:39:27.080Z');
+ component.posting = { ...metisPostExerciseUser1, creationDate: fixedDate };
+
+ jest.spyOn(component, 'isConsecutive').mockReturnValue(false);
+ fixture.detectChanges();
+
+ const postTimeElement = debugElement.query(By.css('span.post-time'));
+ expect(postTimeElement).toBeFalsy();
+ });
});
diff --git a/src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts b/src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts
index 6c0859326aaa..3f37a29a0d23 100644
--- a/src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts
+++ b/src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts
@@ -11,7 +11,7 @@ import { MockMetisService } from '../../../../helpers/mocks/service/mock-metis-s
import { MetisService } from 'app/shared/metis/metis.service';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { DisplayPriority, PageType } from 'app/shared/metis/metis.util';
-import { TranslatePipeMock } from '../../../../helpers/mocks/service/mock-translate.service';
+import { MockTranslateService, TranslatePipeMock } from '../../../../helpers/mocks/service/mock-translate.service';
import { OverlayModule } from '@angular/cdk/overlay';
import {
metisChannel,
@@ -38,6 +38,12 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { DOCUMENT } from '@angular/common';
import { Posting, PostingType } from 'app/entities/metis/posting.model';
import { Post } from 'app/entities/metis/post.model';
+import { ArtemisTranslatePipe } from '../../../../../../../main/webapp/app/shared/pipes/artemis-translate.pipe';
+import { ArtemisDatePipe } from '../../../../../../../main/webapp/app/shared/pipes/artemis-date.pipe';
+import { TranslateDirective } from '../../../../../../../main/webapp/app/shared/language/translate.directive';
+import { TranslateService } from '@ngx-translate/core';
+import { By } from '@angular/platform-browser';
+import dayjs from 'dayjs/esm';
describe('PostComponent', () => {
let component: PostComponent;
@@ -64,6 +70,7 @@ describe('PostComponent', () => {
{ provide: DOCUMENT, useValue: document },
MockProvider(MetisConversationService),
MockProvider(OneToOneChatService),
+ { provide: TranslateService, useClass: MockTranslateService },
],
declarations: [
PostComponent,
@@ -77,6 +84,9 @@ describe('PostComponent', () => {
MockRouterLinkDirective,
MockQueryParamsDirective,
TranslatePipeMock,
+ ArtemisDatePipe,
+ ArtemisTranslatePipe,
+ MockDirective(TranslateDirective),
],
})
.compileComponents()
@@ -380,4 +390,31 @@ describe('PostComponent', () => {
expect(component.posting).toBeInstanceOf(Post);
expect(spy).toHaveBeenCalled();
});
+
+ it('should display post-time span when isConsecutive() returns true', () => {
+ const fixedDate = dayjs('2024-12-06T23:39:27.080Z');
+ component.posting = { ...metisPostExerciseUser1, creationDate: fixedDate };
+
+ jest.spyOn(component, 'isConsecutive').mockReturnValue(true);
+ fixture.detectChanges();
+
+ const postTimeDebugElement = debugElement.query(By.css('span.post-time'));
+ const postTimeElement = postTimeDebugElement.nativeElement as HTMLElement;
+
+ expect(postTimeDebugElement).toBeTruthy();
+
+ const expectedTime = dayjs(fixedDate).format('HH:mm');
+ expect(postTimeElement.textContent?.trim()).toBe(expectedTime);
+ });
+
+ it('should not display post-time span when isConsecutive() returns false', () => {
+ const fixedDate = dayjs('2024-12-06T23:39:27.080Z');
+ component.posting = { ...metisPostExerciseUser1, creationDate: fixedDate };
+
+ jest.spyOn(component, 'isConsecutive').mockReturnValue(false);
+ fixture.detectChanges();
+
+ const postTimeElement = debugElement.query(By.css('span.post-time'));
+ expect(postTimeElement).toBeFalsy();
+ });
});
diff --git a/src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts b/src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts
index 8071b5a84a95..c7975f26849d 100644
--- a/src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts
+++ b/src/test/javascript/spec/component/shared/metis/postings-footer/post-footer/post-footer.component.spec.ts
@@ -17,6 +17,7 @@ import { MockMetisService } from '../../../../../helpers/mocks/service/mock-meti
import { metisPostExerciseUser1, post, unsortedAnswerArray } from '../../../../../helpers/sample/metis-sample-data';
import { AnswerPost } from 'app/entities/metis/answer-post.model';
import { User } from 'app/core/user/user.model';
+import dayjs from 'dayjs/esm';
interface PostGroup {
author: User | undefined;
@@ -72,8 +73,8 @@ describe('PostFooterComponent', () => {
it('should group answer posts correctly', () => {
component.sortedAnswerPosts = unsortedAnswerArray;
component.groupAnswerPosts();
- expect(component.groupedAnswerPosts.length).toBeGreaterThan(0); // Ensure groups are created
- expect(component.groupedAnswerPosts[0].posts.length).toBeGreaterThan(0); // Ensure posts exist in groups
+ expect(component.groupedAnswerPosts.length).toBeGreaterThan(0);
+ expect(component.groupedAnswerPosts[0].posts.length).toBeGreaterThan(0);
});
it('should group answer posts and detect changes on changes to sortedAnswerPosts input', () => {
@@ -160,4 +161,39 @@ describe('PostFooterComponent', () => {
component.closeCreateAnswerPostModal();
expect(createAnswerPostModalClose).toHaveBeenCalledOnce();
});
+
+ it('should group answer posts correctly based on author and time difference', () => {
+ const authorA: User = { id: 1, login: 'authorA' } as User;
+ const authorB: User = { id: 2, login: 'authorB' } as User;
+
+ const baseTime = dayjs();
+
+ const post1: AnswerPost = { id: 1, author: authorA, creationDate: baseTime.toDate() } as unknown as AnswerPost;
+ const post2: AnswerPost = { id: 2, author: authorA, creationDate: baseTime.add(3, 'minute').toDate() } as unknown as AnswerPost;
+ const post3: AnswerPost = { id: 3, author: authorA, creationDate: baseTime.add(10, 'minute').toDate() } as unknown as AnswerPost;
+ const post4: AnswerPost = { id: 4, author: authorB, creationDate: baseTime.add(12, 'minute').toDate() } as unknown as AnswerPost;
+ const post5: AnswerPost = { id: 5, author: authorB, creationDate: baseTime.add(14, 'minute').toDate() } as unknown as AnswerPost;
+
+ component.sortedAnswerPosts = [post3, post1, post5, post2, post4];
+
+ component.groupAnswerPosts();
+ expect(component.groupedAnswerPosts).toHaveLength(3);
+
+ const group1 = component.groupedAnswerPosts[0];
+ expect(group1.author).toEqual(authorA);
+ expect(group1.posts).toHaveLength(2);
+ expect(group1.posts).toContainEqual(expect.objectContaining({ id: post1.id }));
+ expect(group1.posts).toContainEqual(expect.objectContaining({ id: post2.id }));
+
+ const group2 = component.groupedAnswerPosts[1];
+ expect(group2.author).toEqual(authorA);
+ expect(group2.posts).toHaveLength(1);
+ expect(group2.posts).toContainEqual(expect.objectContaining({ id: post3.id }));
+
+ const group3 = component.groupedAnswerPosts[2];
+ expect(group3.author).toEqual(authorB);
+ expect(group3.posts).toHaveLength(2);
+ expect(group3.posts).toContainEqual(expect.objectContaining({ id: post4.id }));
+ expect(group3.posts).toContainEqual(expect.objectContaining({ id: post5.id }));
+ });
});
@@ -7,6 +10,11 @@