Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ <h4 class="mb-2">
@if (isDraft()) {
<p>{{ file.file_name }}</p>
} @else {
<a class="font-bold cursor-pointer" target="_blank" rel="noopener" [href]="file?.file_urls?.html">
<a
class="font-bold cursor-pointer"
target="_blank"
rel="noopener"
[href]="getFileUrl(file?.file_urls?.html)"
>
{{ file.file_name }}
</a>
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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<RegistrationBlocksDataComponent>;
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();
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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']);
Expand All @@ -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']);
Expand All @@ -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');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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<Question[]>();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reviewData = input<Record<string, any>>({});
Expand All @@ -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();
}
}
Loading