Skip to content

Commit 0447ae9

Browse files
authored
[ENG-10002] VOL: Wiki page shows edit button for home page and newly added page (#842)
- Ticket: [ENG-10002] - Feature flag: n/a ## Summary of Changes 1. Fixed permission for edit wiki. 2. Fixed view and compare section.
1 parent 7043b73 commit 0447ae9

File tree

13 files changed

+260
-133
lines changed

13 files changed

+260
-133
lines changed

src/app/shared/components/wiki/compare-section/compare-section.component.html

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@
88
<ng-template #header>
99
<h2 class="mr-2">{{ 'project.wiki.compare' | translate }}</h2>
1010
</ng-template>
11-
<div class="flex flex-wrap align-items-center mb-2 lg:flex-nowrap">
12-
<span class="mr-2">Live preview to</span>
11+
12+
<div class="flex flex-wrap align-items-center mb-4 lg:flex-nowrap">
13+
<span class="min-w-max mr-2">{{ 'project.wiki.livePreviewTo' | translate }}:</span>
14+
1315
<p-select
16+
class="w-full"
17+
styleClass="select-version"
1418
[options]="mappedVersions()"
1519
[ngModel]="selectedVersion"
1620
(onChange)="onVersionChange($event.value)"
17-
placeholder="Version"
18-
class="w-full"
19-
styleClass="select-version"
21+
[placeholder]="'project.wiki.version.title' | translate"
2022
/>
2123
</div>
24+
2225
<p-panel styleClass="compare-view" showHeader="false">
2326
<div lass="mt-3" [innerHTML]="content()"></div>
2427
</p-panel>

src/app/shared/components/wiki/compare-section/compare-section.component.spec.ts

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import { ComponentFixture, TestBed } from '@angular/core/testing';
2-
import { provideNoopAnimations } from '@angular/platform-browser/animations';
32

43
import { WikiVersion } from '@shared/models/wiki/wiki.model';
54

65
import { CompareSectionComponent } from './compare-section.component';
76

87
import { TranslateServiceMock } from '@testing/mocks/translate.service.mock';
8+
import { OSFTestingModule } from '@testing/osf.testing.module';
99

1010
describe('CompareSectionComponent', () => {
1111
let component: CompareSectionComponent;
1212
let fixture: ComponentFixture<CompareSectionComponent>;
13+
let translateServiceMock: any;
1314

1415
const mockVersions: WikiVersion[] = [
1516
{
@@ -22,32 +23,35 @@ describe('CompareSectionComponent', () => {
2223
createdAt: '2024-01-02T10:00:00Z',
2324
createdBy: 'Jane Smith',
2425
},
26+
{
27+
id: 'version-3',
28+
createdAt: '2024-01-03T10:00:00Z',
29+
createdBy: 'Bob Johnson',
30+
},
2531
];
2632

2733
const mockVersionContent = 'Original content';
2834
const mockPreviewContent = 'Updated content with changes';
2935

3036
beforeEach(async () => {
3137
await TestBed.configureTestingModule({
32-
imports: [CompareSectionComponent],
33-
providers: [TranslateServiceMock, provideNoopAnimations()],
38+
imports: [CompareSectionComponent, OSFTestingModule],
39+
providers: [TranslateServiceMock],
3440
}).compileComponents();
3541

42+
translateServiceMock = TestBed.inject(TranslateServiceMock.provide);
43+
translateServiceMock.instant.mockReturnValue('Current');
44+
3645
fixture = TestBed.createComponent(CompareSectionComponent);
3746
component = fixture.componentInstance;
3847

3948
fixture.componentRef.setInput('versions', mockVersions);
4049
fixture.componentRef.setInput('versionContent', mockVersionContent);
4150
fixture.componentRef.setInput('previewContent', mockPreviewContent);
4251
fixture.componentRef.setInput('isLoading', false);
43-
4452
fixture.detectChanges();
4553
});
4654

47-
it('should create', () => {
48-
expect(component).toBeTruthy();
49-
});
50-
5155
it('should set versions input', () => {
5256
expect(component.versions()).toEqual(mockVersions);
5357
});
@@ -64,14 +68,60 @@ describe('CompareSectionComponent', () => {
6468
expect(component.isLoading()).toBe(false);
6569
});
6670

67-
it('should emit selectVersion when version changes', () => {
71+
it('should handle empty versions array', () => {
72+
fixture.componentRef.setInput('versions', []);
73+
fixture.detectChanges();
74+
75+
expect(component.versions()).toEqual([]);
76+
expect(component.selectedVersion).toBeUndefined();
77+
});
78+
79+
it('should initialize with first version selected and emit selectVersion', () => {
80+
expect(component.selectedVersion).toBe(mockVersions[0].id);
81+
});
82+
83+
it('should not emit when no versions available', () => {
6884
const emitSpy = jest.spyOn(component.selectVersion, 'emit');
69-
const versionId = 'version-2';
7085

71-
component.onVersionChange(versionId);
86+
fixture.componentRef.setInput('versions', []);
87+
fixture.detectChanges();
7288

73-
expect(component.selectedVersion).toBe(versionId);
74-
expect(emitSpy).toHaveBeenCalledWith(versionId);
89+
expect(component.selectedVersion).toBeUndefined();
90+
expect(emitSpy).not.toHaveBeenCalled();
91+
});
92+
93+
it('should map versions correctly', () => {
94+
const mappedVersions = component.mappedVersions();
95+
96+
expect(mappedVersions).toHaveLength(3);
97+
expect(mappedVersions[0].value).toBe('version-1');
98+
expect(mappedVersions[0].label).toContain('(Current)');
99+
expect(mappedVersions[0].label).toContain('John Doe');
100+
expect(mappedVersions[1].value).toBe('version-2');
101+
expect(mappedVersions[1].label).toContain('(2)');
102+
expect(mappedVersions[1].label).toContain('Jane Smith');
103+
expect(mappedVersions[2].value).toBe('version-3');
104+
expect(mappedVersions[2].label).toContain('(1)');
105+
expect(mappedVersions[2].label).toContain('Bob Johnson');
106+
});
107+
108+
it('should handle version with undefined createdBy', () => {
109+
const versionsWithUndefinedCreator: WikiVersion[] = [
110+
{
111+
id: 'version-1',
112+
createdAt: '2024-01-01T10:00:00Z',
113+
createdBy: undefined,
114+
},
115+
];
116+
117+
fixture.componentRef.setInput('versions', versionsWithUndefinedCreator);
118+
fixture.detectChanges();
119+
120+
const mappedVersions = component.mappedVersions();
121+
expect(mappedVersions).toHaveLength(1);
122+
expect(mappedVersions[0].value).toBe('version-1');
123+
expect(mappedVersions[0].label).toContain('(Current)');
124+
expect(mappedVersions[0].label).toContain('1/1/2024');
75125
});
76126

77127
it('should handle single version', () => {
@@ -84,7 +134,61 @@ describe('CompareSectionComponent', () => {
84134
expect(mappedVersions[0].label).toContain('(Current)');
85135
});
86136

87-
it('should initialize with first version selected', () => {
88-
expect(component.selectedVersion).toBe(mockVersions[0].id);
137+
it('should compute content diff correctly', () => {
138+
const content = component.content();
139+
140+
expect(content).toContain('<span class="removed">Original</span>');
141+
expect(content).toContain('<span class="added">Updated</span>');
142+
expect(content).toContain('content');
143+
expect(content).toContain('<span class="added">with changes</span>');
144+
});
145+
146+
it('should handle identical content', () => {
147+
fixture.componentRef.setInput('previewContent', mockVersionContent);
148+
fixture.detectChanges();
149+
150+
const content = component.content();
151+
expect(content).toBe(mockVersionContent);
152+
});
153+
154+
it('should handle empty version content', () => {
155+
fixture.componentRef.setInput('versionContent', '');
156+
fixture.detectChanges();
157+
158+
const content = component.content();
159+
expect(content).toContain('<span class="added">Updated content with changes</span>');
160+
});
161+
162+
it('should handle empty preview content', () => {
163+
fixture.componentRef.setInput('previewContent', '');
164+
fixture.detectChanges();
165+
166+
const content = component.content();
167+
expect(content).toContain('<span class="removed">Original content</span>');
168+
});
169+
170+
it('should update selectedVersion and emit selectVersion', () => {
171+
const emitSpy = jest.spyOn(component.selectVersion, 'emit');
172+
const versionId = 'version-2';
173+
174+
component.onVersionChange(versionId);
175+
176+
expect(component.selectedVersion).toBe(versionId);
177+
expect(emitSpy).toHaveBeenCalledWith(versionId);
178+
expect(emitSpy).toHaveBeenCalledTimes(1);
179+
});
180+
181+
it('should emit correct version id when called multiple times', () => {
182+
const emitSpy = jest.spyOn(component.selectVersion, 'emit');
183+
184+
component.onVersionChange('version-2');
185+
component.onVersionChange('version-3');
186+
component.onVersionChange('version-1');
187+
188+
expect(component.selectedVersion).toBe('version-1');
189+
expect(emitSpy).toHaveBeenCalledTimes(3);
190+
expect(emitSpy).toHaveBeenNthCalledWith(1, 'version-2');
191+
expect(emitSpy).toHaveBeenNthCalledWith(2, 'version-3');
192+
expect(emitSpy).toHaveBeenNthCalledWith(3, 'version-1');
89193
});
90194
});

src/app/shared/components/wiki/compare-section/compare-section.component.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { TranslatePipe } from '@ngx-translate/core';
1+
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
22

33
import { Panel } from 'primeng/panel';
44
import { Select } from 'primeng/select';
55
import { Skeleton } from 'primeng/skeleton';
66

7-
import { ChangeDetectionStrategy, Component, computed, effect, input, output } from '@angular/core';
7+
import { ChangeDetectionStrategy, Component, computed, effect, inject, input, output } from '@angular/core';
88
import { FormsModule } from '@angular/forms';
99

1010
import { WikiVersion } from '@osf/shared/models/wiki/wiki.model';
@@ -25,20 +25,23 @@ export class CompareSectionComponent {
2525
isLoading = input.required<boolean>();
2626
selectVersion = output<string>();
2727

28+
translateService = inject(TranslateService);
29+
2830
selectedVersion: string | null = null;
2931

32+
private readonly currentLabel = this.translateService.instant('project.wiki.version.current');
33+
private readonly unknownAuthorLabel = this.translateService.instant('project.wiki.version.unknownAuthor');
34+
3035
mappedVersions = computed(() => [
31-
...this.versions().map((version, index) => {
32-
const labelPrefix = index === 0 ? '(Current)' : `(${this.versions().length - index})`;
33-
return {
34-
label: `${labelPrefix} ${version.createdBy}: (${new Date(version.createdAt).toLocaleString()})`,
35-
value: version.id,
36-
};
37-
}),
36+
...this.versions().map((version, index) => ({
37+
label: this.formatVersionLabel(version, index),
38+
value: version.id,
39+
})),
3840
]);
3941

4042
content = computed(() => {
4143
const changes = Diff.diffWords(this.versionContent(), this.previewContent());
44+
4245
return changes
4346
.map((change) => {
4447
if (change.added) {
@@ -54,13 +57,21 @@ export class CompareSectionComponent {
5457
constructor() {
5558
effect(() => {
5659
this.selectedVersion = this.versions()[0]?.id;
60+
5761
if (this.selectedVersion) {
5862
this.selectVersion.emit(this.selectedVersion);
5963
}
6064
});
6165
}
66+
6267
onVersionChange(versionId: string): void {
6368
this.selectedVersion = versionId;
6469
this.selectVersion.emit(versionId);
6570
}
71+
72+
private formatVersionLabel(version: WikiVersion, index: number): string {
73+
const prefix = index === 0 ? `(${this.currentLabel})` : `(${this.versions().length - index})`;
74+
const creator = version.createdBy || this.unknownAuthorLabel;
75+
return `${prefix} ${creator}: (${new Date(version.createdAt).toLocaleString()})`;
76+
}
6677
}

src/app/shared/components/wiki/view-section/view-section.component.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@
88
<ng-template #header>
99
<h2 class="mr-2">{{ 'project.wiki.view' | translate }}</h2>
1010
</ng-template>
11+
1112
<div class="flex flex-wrap align-items-center mb-4 lg:flex-nowrap">
1213
<span class="mr-2">{{ 'project.wiki.version.title' | translate }}:</span>
14+
1315
<p-select
16+
class="w-full"
17+
styleClass="select-version"
1418
[options]="mappedVersions()"
1519
[ngModel]="selectedVersion()"
1620
(onChange)="onVersionChange($event.value)"
1721
[placeholder]="'project.wiki.version.select' | translate"
18-
class="w-full"
19-
styleClass="select-version"
2022
/>
2123
</div>
24+
2225
<p-panel class="view-panel" showHeader="false">
2326
<div class="editor-view-mode mt-3">
2427
@if (content()) {

0 commit comments

Comments
 (0)