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
19 changes: 17 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,23 @@ module.exports = {
'<rootDir>/src/app/features/project/settings',
'<rootDir>/src/app/features/project/wiki',
'<rootDir>/src/app/features/project/project.component.ts',
'<rootDir>/src/app/features/registries/',
'<rootDir>/src/app/features/registries/pages',
'<rootDir>/src/app/features/registries/registries.component.spec.ts',
'<rootDir>/src/app/features/registries/components/metadata/contributors',
'<rootDir>/src/app/features/registries/components/metadata/registries-license',
'<rootDir>/src/app/features/registries/components/metadata/registries-subjects',
'<rootDir>/src/app/features/registries/components/confirm-continue-editing-dialog',
'<rootDir>/src/app/features/registries/components/confirm-registration-dialog',
'<rootDir>/src/app/features/registries/components/custom-step',
'<rootDir>/src/app/features/registries/components/drafts',
'<rootDir>/src/app/features/registries/components/files-control',
'<rootDir>/src/app/features/registries/components/justification-review',
'<rootDir>/src/app/features/registries/components/justification-step',
'<rootDir>/src/app/features/registries/components/new-registration',
'<rootDir>/src/app/features/registries/components/registry-provider-hero',
'<rootDir>/src/app/features/registries/components/registry-services',
'<rootDir>/src/app/features/registries/components/review',
'<rootDir>/src/app/features/registries/components/select-components-dialog',
'<rootDir>/src/app/features/settings/addons/',
'<rootDir>/src/app/features/settings/tokens/mappers/',
'<rootDir>/src/app/features/settings/tokens/store/',
Expand All @@ -90,7 +106,6 @@ module.exports = {
'<rootDir>/src/app/shared/components/shared-metadata/dialogs/affiliated-institutions-dialog/',
'<rootDir>/src/app/shared/components/shared-metadata/dialogs/contributors-dialog/',
'<rootDir>/src/app/shared/components/shared-metadata/shared-metadata',
'<rootDir>/src/app/shared/components/subjects/',
'<rootDir>/src/app/shared/components/wiki/edit-section/',
'<rootDir>/src/app/shared/components/wiki/wiki-list/',
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
<section class="flex flex-column bg-white flex-1 h-full p-5 gap-4 w-full">
<div>
<h2 class="mb-2">{{ 'registries.metadata.title' | translate }}</h2>
<p class="mb-4">{{ 'registries.metadata.description' | translate }}</p>
</div>
<form [formGroup]="metadataForm" (ngSubmit)="submitMetadata()" class="flex flex-column gap-4">
<section>
<h2 class="mb-2">{{ 'registries.metadata.title' | translate }}</h2>
<p class="mb-4">{{ 'registries.metadata.description' | translate }}</p>
<section data-test-registration-title-section>
<p-card class="w-full">
<label for="registration-title-id">
<h2 class="mb-2">{{ 'common.labels.title' | translate }}</h2>
</label>
<p class="mb-1">{{ 'shared.title.description' | translate }}</p>
<osf-text-input
label="common.labels.title"
id="registration-title-id"
[control]="metadataForm.controls['title']"
[maxLength]="inputLimits.fullName.maxLength"
>
</osf-text-input>
</p-card>
</section>
<section data-test-registration-description-section>
<p-card>
<div class="mt-2">
<label for="project-description-id">{{ 'common.labels.description' | translate }}</label>
<label for="registration-description-id" class="mb-2">
<h2>{{ 'common.labels.description' | translate }}</h2>
</label>
<p class="mb-1">{{ 'shared.description.message' | translate }}</p>
<textarea
id="project-description-id"
id="registration-description-id"
formControlName="description"
class="w-full"
rows="5"
Expand All @@ -32,10 +45,19 @@ <h2 class="mb-2">{{ 'registries.metadata.title' | translate }}</h2>
<div class="flex justify-content-end"></div>
</p-card>
</section>
<osf-contributors [control]="metadataForm.controls['contributors']"></osf-contributors>
<osf-registries-license [control]="metadataForm.controls['license']"></osf-registries-license>
<osf-registries-subjects [control]="metadataForm.controls['subjects']"></osf-registries-subjects>
<osf-registries-tags></osf-registries-tags>
<osf-contributors
data-test-registration-contributors-section
[control]="metadataForm.controls['contributors']"
></osf-contributors>
<osf-registries-license
data-test-registration-license-section
[control]="metadataForm.controls['license']"
></osf-registries-license>
<osf-registries-subjects
data-test-registration-subjects-section
[control]="metadataForm.controls['subjects']"
></osf-registries-subjects>
<osf-registries-tags data-test-registration-tags-section></osf-registries-tags>
<div class="flex justify-content-end">
<p-button [label]="'registries.deleteDraft' | translate" severity="danger" (click)="deleteDraft()" class="mr-2">
</p-button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,71 @@
import { Store } from '@ngxs/store';

import { MockComponent, MockProvider } from 'ng-mocks';

import { ConfirmationService, MessageService } from 'primeng/api';

import { of } from 'rxjs';

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';

import { TextInputComponent } from '@osf/shared/components';
import { MOCK_STORE } from '@osf/shared/mocks';
import { ContributorsSelectors, SubjectsSelectors } from '@osf/shared/stores';

import { RegistriesSelectors } from '../../store';

import { ContributorsComponent } from './contributors/contributors.component';
import { RegistriesLicenseComponent } from './registries-license/registries-license.component';
import { RegistriesSubjectsComponent } from './registries-subjects/registries-subjects.component';
import { RegistriesTagsComponent } from './registries-tags/registries-tags.component';
import { MetadataComponent } from './metadata.component';

import { MOCK_DRAFT_REGISTRATION } from '@testing/mocks/draft-registration.mock';
import { OSFTestingModule } from '@testing/osf.testing.module';

describe('MetadataComponent', () => {
let component: MetadataComponent;
let fixture: ComponentFixture<MetadataComponent>;
const mockRoute = {
snapshot: {
params: of({ id: 'someId' }),
},
params: of(''),
};

beforeEach(async () => {
MOCK_STORE.selectSignal.mockImplementation((selector) => {
switch (selector) {
case RegistriesSelectors.getDraftRegistration:
return () => MOCK_DRAFT_REGISTRATION;
case RegistriesSelectors.getStepsValidation:
return () => [];
case ContributorsSelectors.getContributors:
return () => [];
case SubjectsSelectors.getSelectedSubjects:
return () => [];
}
return null;
});
await TestBed.configureTestingModule({
imports: [MetadataComponent],
imports: [
OSFTestingModule,
ReactiveFormsModule,
MetadataComponent,
MockComponent(ContributorsComponent),
MockComponent(RegistriesLicenseComponent),
MockComponent(RegistriesSubjectsComponent),
MockComponent(RegistriesTagsComponent),
MockComponent(TextInputComponent),
],
providers: [
{ provide: ActivatedRoute, useValue: mockRoute },
MockProvider(ConfirmationService),
MockProvider(MessageService),
MockProvider(Store, MOCK_STORE),
],
}).compileComponents();

fixture = TestBed.createComponent(MetadataComponent);
Expand All @@ -19,4 +76,25 @@ describe('MetadataComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

it('should render with submission form', () => {
const titleSection = fixture.nativeElement.querySelector('[data-test-registration-title-section]');
const descriptionSection = fixture.nativeElement.querySelector('[data-test-registration-description-section]');
const contributorsSection = fixture.nativeElement.querySelector('[data-test-registration-contributors-section]');
const subjectsSection = fixture.nativeElement.querySelector('[data-test-registration-subjects-section]');
const tagsSection = fixture.nativeElement.querySelector('[data-test-registration-tags-section]');
expect(titleSection).toBeDefined();
expect(descriptionSection).toBeDefined();
expect(contributorsSection).toBeDefined();
expect(subjectsSection).toBeDefined();
expect(tagsSection).toBeDefined();
const titleFieldLabel = titleSection.querySelector('h2');
expect(titleFieldLabel.textContent).toEqual('common.labels.title');
const titleFieldHelpText = titleSection.querySelector('p');
expect(titleFieldHelpText.textContent).toEqual('shared.title.description');
const descriptionFieldLabel = descriptionSection.querySelector('h2');
expect(descriptionFieldLabel.textContent).toEqual('common.labels.description');
const descriptionFieldHelpText = descriptionSection.querySelector('p');
expect(descriptionFieldHelpText.textContent).toEqual('shared.description.message');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export class MetadataComponent implements OnDestroy {
constructor() {
effect(() => {
const draft = this.draftRegistration();
// TODO: This shouldn't be an effect()
if (draft && !this.isFormUpdated) {
this.updateFormValue(draft);
this.isFormUpdated = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
<p-card>
<div class="flex flex-column gap-3">
<h2>{{ 'project.overview.metadata.tags' | translate }} ({{ 'common.labels.optional' | translate }})</h2>

<osf-tags-input (tagsChanged)="onTagsChanged($event)" [tags]="selectedTags()"></osf-tags-input>
<label for="registration-tag-id">
<h2>{{ 'project.overview.metadata.tags' | translate }} ({{ 'common.labels.optional' | translate }})</h2>
</label>
<p class="mb-1">{{ 'shared.tags.description' | translate }}</p>
<osf-tags-input
id="registration-tag-id"
(tagsChanged)="onTagsChanged($event)"
[tags]="selectedTags()"
></osf-tags-input>
</div>
</p-card>
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
import { Store } from '@ngxs/store';

import { MockProvider } from 'ng-mocks';

import { of } from 'rxjs';

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';

import { RegistriesSelectors } from '@osf/features/registries/store';
import { MOCK_STORE } from '@osf/shared/mocks';

import { RegistriesTagsComponent } from './registries-tags.component';

import { OSFTestingStoreModule } from '@testing/osf.testing.module';

describe('TagsComponent', () => {
let component: RegistriesTagsComponent;
let fixture: ComponentFixture<RegistriesTagsComponent>;
const mockRoute = {
snapshot: {
params: of({ id: 'someId' }),
},
params: of(''),
};

MOCK_STORE.selectSignal.mockImplementation((selector) => {
switch (selector) {
case RegistriesSelectors.getSelectedTags:
return () => [];
}
return null;
});
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RegistriesTagsComponent],
imports: [OSFTestingStoreModule, RegistriesTagsComponent],
providers: [{ provide: ActivatedRoute, useValue: mockRoute }, MockProvider(Store, MOCK_STORE)],
}).compileComponents();

fixture = TestBed.createComponent(RegistriesTagsComponent);
Expand All @@ -19,4 +45,9 @@ describe('TagsComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

it('should render with label', () => {
const labelElement = fixture.nativeElement.querySelector('label');
expect(labelElement.textContent).toEqual('project.overview.metadata.tags (common.labels.optional)');
});
});
2 changes: 1 addition & 1 deletion src/app/shared/components/subjects/subjects.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<h2 class="mb-2">{{ 'shared.subjects.title' | translate }}</h2>

<p class="mb-1">{{ 'shared.subjects.description' | translate }}</p>
<p-card class="block text-base mb-3">
@if (!selected().length) {
<p class="text-500">{{ 'shared.subjects.noSelected' | translate }}</p>
Expand Down
32 changes: 31 additions & 1 deletion src/app/shared/components/subjects/subjects.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import { Store } from '@ngxs/store';

import { MockProvider } from 'ng-mocks';

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { MOCK_STORE } from '@osf/shared/mocks';
import { SubjectsSelectors } from '@osf/shared/stores';

import { SubjectsComponent } from './subjects.component';

import { OSFTestingStoreModule } from '@testing/osf.testing.module';

describe('SubjectsComponent', () => {
let component: SubjectsComponent;
let fixture: ComponentFixture<SubjectsComponent>;

beforeEach(async () => {
MOCK_STORE.selectSignal.mockImplementation((selector) => {
switch (selector) {
case SubjectsSelectors.getSubjects:
return () => [];
case SubjectsSelectors.getSubjectsLoading:
return () => false;
case SubjectsSelectors.getSearchedSubjects:
return () => [];
case SubjectsSelectors.getSearchedSubjectsLoading:
return () => false;
}
return null;
});
await TestBed.configureTestingModule({
imports: [SubjectsComponent],
imports: [SubjectsComponent, OSFTestingStoreModule],
providers: [MockProvider(Store, MOCK_STORE)],
}).compileComponents();

fixture = TestBed.createComponent(SubjectsComponent);
Expand All @@ -19,4 +42,11 @@ describe('SubjectsComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

it('should render with label and description', () => {
const headerElement = fixture.nativeElement.querySelector('h2');
expect(headerElement.textContent).toEqual('shared.subjects.title');
const descriptionElement = fixture.nativeElement.querySelector('p');
expect(descriptionElement.textContent).toEqual('shared.subjects.description');
});
});
15 changes: 13 additions & 2 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2447,6 +2447,15 @@
"copyright": "Copyright © 2011-2025"
},
"shared": {
"title": {
"description": "Provide a concise and descriptive title for your registration that accurately reflects the main focus of your research. A clear and specific title will help others quickly understand the essence of your study."
},
"description": {
"message": "Write a detailed description of your research project, including its purpose and any expected outcomes. This should give readers a comprehensive overview of your work."
},
"affiliatedInstitutions": {
"description": "This is a service provided by the OSF and is automatically applied to your registration. If you are not sure if your institution has signed up for this service, you can look for their name in this list."
},
"resources": {
"title": "Open resources",
"data": "Data",
Expand All @@ -2466,10 +2475,12 @@
"title": "Subjects",
"noSelected": "No subjects selected",
"searchSubjects": "Search subjects",
"noSubject": "No subjects found"
"noSubject": "No subjects found",
"description": "Identify the main subject areas that your research pertains to. This helps categorize your work and makes it easier for others in your field to find and reference it."
},
"tags": {
"title": "Tags"
"title": "Tags",
"description": "Enter specific keywords that describe the key elements and concepts of your research. These keywords will improve the discoverability of your registration in search results and databases."
},
"files": {
"limitText": "You may attach up to 5 file(s) to this question. Files cannot total over 5GB in size.",
Expand Down
26 changes: 26 additions & 0 deletions src/testing/mocks/draft-registration.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { DraftRegistrationModel } from '@shared/models';

export const MOCK_DRAFT_REGISTRATION: DraftRegistrationModel = {
branchedFrom: {
filesLink: 'someFilesLink',
id: 'projectId',
title: 'Project Title',
type: 'nodes',
},
components: [],
description: 'This is a description',
hasProject: true,
id: 'thisissupposedtobeauniqueid',
license: {
id: 'someLicenseId',
options: null,
},
providerId: 'osf',
registrationSchemaId: '6797c0dedee44d144a2943fc',
stepsData: {
summary: '',
uploader: [],
},
tags: [],
title: 'This is a title',
};