diff --git a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.html b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.html
index 0f3e2636a..b318cbc51 100644
--- a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.html
+++ b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.html
@@ -25,7 +25,12 @@
@if (isDraft()) {
{{ file.file_name }}
} @else {
-
+
{{ file.file_name }}
}
diff --git a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.spec.ts b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.spec.ts
index bfc7847a0..b5c846f18 100644
--- a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.spec.ts
+++ b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.spec.ts
@@ -1,10 +1,15 @@
-import { TestBed } from '@angular/core/testing';
+import { MockProvider } from 'ng-mocks';
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FieldType } from '@osf/shared/enums/field-type.enum';
import { Question } from '@osf/shared/models/registration/page-schema.model';
+import { ViewOnlyLinkHelperService } from '@osf/shared/services/view-only-link-helper.service';
import { MOCK_REVIEW } from '@testing/mocks/review.mock';
import { provideOSFCore } from '@testing/osf.testing.provider';
+import { provideRouterMock, RouterMockBuilder, RouterMockType } from '@testing/providers/router-provider.mock';
+import { ViewOnlyLinkHelperMock, ViewOnlyLinkHelperMockType } from '@testing/providers/view-only-link-helper.mock';
import { RegistrationBlocksDataComponent } from './registration-blocks-data.component';
@@ -13,39 +18,61 @@ const MOCK_QUESTIONS: Question[] = [
{ id: '2', displayText: 'Q2', required: false, responseKey: 'question2', fieldType: FieldType.Checkbox },
];
+interface SetupOverrides {
+ routerUrl?: string;
+ viewOnlyParam?: string | null;
+}
+
describe('RegistrationBlocksDataComponent', () => {
- beforeEach(() => {
+ let component: RegistrationBlocksDataComponent;
+ let fixture: ComponentFixture;
+ let routerMock: RouterMockType;
+ let viewOnlyHelper: ViewOnlyLinkHelperMockType;
+
+ function setup(overrides: SetupOverrides = {}) {
+ routerMock = RouterMockBuilder.create()
+ .withUrl(overrides.routerUrl ?? '/')
+ .build();
+ viewOnlyHelper = ViewOnlyLinkHelperMock.simple();
+ viewOnlyHelper.getViewOnlyParamFromUrl.mockReturnValue(
+ overrides.viewOnlyParam !== undefined ? overrides.viewOnlyParam : null
+ );
+
TestBed.configureTestingModule({
imports: [RegistrationBlocksDataComponent],
- providers: [provideOSFCore()],
+ providers: [
+ provideOSFCore(),
+ provideRouterMock(routerMock),
+ MockProvider(ViewOnlyLinkHelperService, viewOnlyHelper),
+ ],
});
- });
- it('should create', () => {
- const fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
+ fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
+ component = fixture.componentInstance;
fixture.detectChanges();
+ }
+
+ beforeEach(() => setup());
- expect(fixture.componentInstance).toBeTruthy();
+ it('should create', () => {
+ expect(component).toBeTruthy();
});
it('should compute updatedKeysMap from updatedFields', () => {
- const fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
fixture.componentRef.setInput('updatedFields', ['question1', 'question3']);
fixture.detectChanges();
- expect(fixture.componentInstance.updatedKeysMap()).toEqual({ question1: true, question3: true });
+ expect(component.updatedKeysMap()).toEqual({ question1: true, question3: true });
});
it('should return empty updatedKeysMap when updatedFields is empty', () => {
- const fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
fixture.componentRef.setInput('updatedFields', []);
fixture.detectChanges();
- expect(fixture.componentInstance.updatedKeysMap()).toEqual({});
+ expect(component.updatedKeysMap()).toEqual({});
});
it('should render questions with review data', () => {
- const fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
fixture.componentRef.setInput('questions', MOCK_QUESTIONS);
fixture.componentRef.setInput('reviewData', MOCK_REVIEW);
fixture.detectChanges();
@@ -56,7 +83,6 @@ describe('RegistrationBlocksDataComponent', () => {
});
it('should show required error when question is required and no data on non-overview page', () => {
- const fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
fixture.componentRef.setInput('questions', MOCK_QUESTIONS);
fixture.componentRef.setInput('reviewData', {});
fixture.componentRef.setInput('isOverviewPage', false);
@@ -67,7 +93,6 @@ describe('RegistrationBlocksDataComponent', () => {
});
it('should not show required error on overview page', () => {
- const fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
fixture.componentRef.setInput('questions', MOCK_QUESTIONS);
fixture.componentRef.setInput('reviewData', {});
fixture.componentRef.setInput('isOverviewPage', true);
@@ -78,7 +103,6 @@ describe('RegistrationBlocksDataComponent', () => {
});
it('should show updated tag when field is updated and not original revision', () => {
- const fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
fixture.componentRef.setInput('questions', MOCK_QUESTIONS);
fixture.componentRef.setInput('reviewData', MOCK_REVIEW);
fixture.componentRef.setInput('updatedFields', ['question1']);
@@ -90,7 +114,6 @@ describe('RegistrationBlocksDataComponent', () => {
});
it('should not show updated tag on original revision', () => {
- const fixture = TestBed.createComponent(RegistrationBlocksDataComponent);
fixture.componentRef.setInput('questions', MOCK_QUESTIONS);
fixture.componentRef.setInput('reviewData', MOCK_REVIEW);
fixture.componentRef.setInput('updatedFields', ['question1']);
@@ -100,4 +123,24 @@ describe('RegistrationBlocksDataComponent', () => {
const tags = fixture.nativeElement.querySelectorAll('p-tag');
expect(tags.length).toBe(0);
});
+
+ it('should return empty string when file url is missing', () => {
+ expect(component.getFileUrl()).toBe('');
+ });
+
+ it('should return original file url when view_only param is absent', () => {
+ const htmlUrl = 'https://osf.io/abc12/';
+
+ expect(component.getFileUrl(htmlUrl)).toBe(htmlUrl);
+ });
+
+ it('should append view_only query param to file url', () => {
+ viewOnlyHelper.getViewOnlyParamFromUrl.mockReturnValue('token');
+
+ const htmlUrl = 'https://osf.io/abc12/';
+ const result = component.getFileUrl(htmlUrl);
+
+ expect(viewOnlyHelper.getViewOnlyParamFromUrl).toHaveBeenCalledWith('/');
+ expect(result).toBe('https://osf.io/abc12/?view_only=token');
+ });
});
diff --git a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.ts b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.ts
index d1c49eb2f..908471dbc 100644
--- a/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.ts
+++ b/src/app/shared/components/registration-blocks-data/registration-blocks-data.component.ts
@@ -3,12 +3,14 @@ import { TranslatePipe } from '@ngx-translate/core';
import { Message } from 'primeng/message';
import { Tag } from 'primeng/tag';
-import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
+import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';
+import { Router } from '@angular/router';
import { INPUT_VALIDATION_MESSAGES } from '@osf/shared/constants/input-validation-messages.const';
import { FieldType } from '@osf/shared/enums/field-type.enum';
import { Question } from '@osf/shared/models/registration/page-schema.model';
import { FixSpecialCharPipe } from '@osf/shared/pipes/fix-special-char.pipe';
+import { ViewOnlyLinkHelperService } from '@osf/shared/services/view-only-link-helper.service';
@Component({
selector: 'osf-registration-blocks-data',
@@ -18,6 +20,9 @@ import { FixSpecialCharPipe } from '@osf/shared/pipes/fix-special-char.pipe';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RegistrationBlocksDataComponent {
+ private readonly router = inject(Router);
+ private readonly viewOnlyService = inject(ViewOnlyLinkHelperService);
+
questions = input();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reviewData = input>({});
@@ -34,4 +39,20 @@ export class RegistrationBlocksDataComponent {
readonly FieldType = FieldType;
readonly INPUT_VALIDATION_MESSAGES = INPUT_VALIDATION_MESSAGES;
+
+ getFileUrl(htmlUrl?: string): string {
+ if (!htmlUrl) {
+ return '';
+ }
+
+ const viewOnly = this.viewOnlyService.getViewOnlyParamFromUrl(this.router.url);
+
+ if (!viewOnly) {
+ return htmlUrl;
+ }
+
+ const url = new URL(htmlUrl);
+ url.searchParams.set('view_only', viewOnly);
+ return url.toString();
+ }
}