Skip to content
Open
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 @@ -9,7 +9,11 @@ import { of, throwError } from 'rxjs';
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { CreateCollectionSubmission } from '@osf/features/collections/store/add-to-collection/add-to-collection.actions';
import { CedarMetadataAttributes, CedarRecordDataBinding } from '@osf/features/metadata/models';
import { CreateCedarMetadataRecord } from '@osf/features/metadata/store';
import { UpdateProjectPublicStatus } from '@osf/features/project/overview/store';
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
import { CollectionSubmissionPayload } from '@osf/shared/models/collections/collection-submission-payload.model';
import { ToastService } from '@osf/shared/services/toast.service';

import { provideOSFCore } from '@testing/osf.testing.provider';
Expand All @@ -19,20 +23,39 @@ import { ToastServiceMock, ToastServiceMockType } from '@testing/providers/toast

import { AddToCollectionConfirmationDialogComponent } from './add-to-collection-confirmation-dialog.component';

const MOCK_CEDAR_DATA: CedarRecordDataBinding = {
data: { '@context': {} } as CedarMetadataAttributes,
id: 'template-1',
isPublished: true,
};

describe('AddToCollectionConfirmationDialogComponent', () => {
let component: AddToCollectionConfirmationDialogComponent;
let fixture: ComponentFixture<AddToCollectionConfirmationDialogComponent>;
let store: Store;
let dialogRef: DynamicDialogRef;
let toastService: ToastServiceMockType;
let dialogConfig: { data: { payload?: unknown; project?: { id: string; isPublic: boolean } } };
let dialogConfig: {
data: {
payload?: CollectionSubmissionPayload;
project?: { id: string; isPublic: boolean };
cedarData?: CedarRecordDataBinding | null;
};
};

const MOCK_PAYLOAD: CollectionSubmissionPayload = {
collectionId: 'collection-1',
projectId: 'project-1',
userId: 'user-1',
};

beforeEach(() => {
toastService = ToastServiceMock.simple();
dialogConfig = {
data: {
payload: { title: 'Submission' },
payload: MOCK_PAYLOAD,
project: { id: 'project-1', isPublic: false },
cedarData: null,
},
};

Expand Down Expand Up @@ -69,13 +92,14 @@ describe('AddToCollectionConfirmationDialogComponent', () => {
expect(toastService.showSuccess).not.toHaveBeenCalled();
});

it('should update project public status and create submission when project is private', () => {
it('should update project public status then create submission when project is private and no Cedar data', () => {
vi.spyOn(store, 'dispatch').mockReturnValue(of(void 0));

component.handleAddToCollectionConfirm();

expect(store.dispatch).toHaveBeenCalledWith(new UpdateProjectPublicStatus([{ id: 'project-1', public: true }]));
expect(store.dispatch).toHaveBeenCalledWith(new CreateCollectionSubmission({ title: 'Submission' } as any));
expect(store.dispatch).toHaveBeenCalledWith(new CreateCollectionSubmission(MOCK_PAYLOAD));
expect(store.dispatch).not.toHaveBeenCalledWith(expect.any(CreateCedarMetadataRecord));
expect(dialogRef.close).toHaveBeenCalledWith(true);
expect(toastService.showSuccess).toHaveBeenCalledWith('collections.addToCollection.confirmationDialogToastMessage');
expect(component.isSubmitting()).toBe(false);
Expand All @@ -87,11 +111,34 @@ describe('AddToCollectionConfirmationDialogComponent', () => {

component.handleAddToCollectionConfirm();

expect(store.dispatch).toHaveBeenCalledWith(new CreateCollectionSubmission({ title: 'Submission' } as any));
expect(store.dispatch).toHaveBeenCalledWith(new CreateCollectionSubmission(MOCK_PAYLOAD));
expect(store.dispatch).not.toHaveBeenCalledWith(expect.any(UpdateProjectPublicStatus));
expect(dialogRef.close).toHaveBeenCalledWith(true);
});

it('should create Cedar record before submission when cedarData is present', () => {
dialogConfig.data.cedarData = MOCK_CEDAR_DATA;
vi.spyOn(store, 'dispatch').mockReturnValue(of(void 0));

component.handleAddToCollectionConfirm();

expect(store.dispatch).toHaveBeenCalledWith(
new CreateCedarMetadataRecord(MOCK_CEDAR_DATA, 'project-1', ResourceType.Project)
);
expect(store.dispatch).toHaveBeenCalledWith(new CreateCollectionSubmission(MOCK_PAYLOAD));
expect(dialogRef.close).toHaveBeenCalledWith(true);
});

it('should not create Cedar record when cedarData is null', () => {
dialogConfig.data.cedarData = null;
vi.spyOn(store, 'dispatch').mockReturnValue(of(void 0));

component.handleAddToCollectionConfirm();

expect(store.dispatch).not.toHaveBeenCalledWith(expect.any(CreateCedarMetadataRecord));
expect(store.dispatch).toHaveBeenCalledWith(new CreateCollectionSubmission(MOCK_PAYLOAD));
});

it('should reset submitting state on error', () => {
vi.spyOn(store, 'dispatch').mockImplementation((action) => {
if (action instanceof CreateCollectionSubmission) {
Expand All @@ -106,4 +153,20 @@ describe('AddToCollectionConfirmationDialogComponent', () => {
expect(dialogRef.close).not.toHaveBeenCalled();
expect(toastService.showSuccess).not.toHaveBeenCalled();
});

it('should reset submitting state on Cedar record creation error', () => {
dialogConfig.data.cedarData = MOCK_CEDAR_DATA;
vi.spyOn(store, 'dispatch').mockImplementation((action) => {
if (action instanceof CreateCedarMetadataRecord) {
return throwError(() => new Error('cedar fail'));
}
return of(void 0);
});

component.handleAddToCollectionConfirm();

expect(component.isSubmitting()).toBe(false);
expect(dialogRef.close).not.toHaveBeenCalled();
expect(toastService.showSuccess).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import { TranslatePipe } from '@ngx-translate/core';
import { Button } from 'primeng/button';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';

import { forkJoin, of } from 'rxjs';
import { Observable, of, switchMap } from 'rxjs';

import { ChangeDetectionStrategy, Component, DestroyRef, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

import { CreateCollectionSubmission } from '@osf/features/collections/store/add-to-collection/add-to-collection.actions';
import { CedarRecordDataBinding } from '@osf/features/metadata/models';
import { CreateCedarMetadataRecord } from '@osf/features/metadata/store';
import { UpdateProjectPublicStatus } from '@osf/features/project/overview/store';
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
import { ToastService } from '@osf/shared/services/toast.service';

@Component({
Expand All @@ -30,26 +33,33 @@ export class AddToCollectionConfirmationDialogComponent {
actions = createDispatchMap({
createCollectionSubmission: CreateCollectionSubmission,
updateProjectPublicStatus: UpdateProjectPublicStatus,
createCedarRecord: CreateCedarMetadataRecord,
});

handleAddToCollectionConfirm(): void {
const payload = this.config.data.payload;
const project = this.config.data.project;
const cedarData = this.config.data.cedarData as CedarRecordDataBinding | null | undefined;

if (!payload || !project) return;

this.isSubmitting.set(true);
const projectPayload = [{ id: project.id as string, public: true }];

const updatePublicStatus$ = project.isPublic ? of(null) : this.actions.updateProjectPublicStatus(projectPayload);
const updatePublicStatus$: Observable<unknown> = project.isPublic
? of(null)
: this.actions.updateProjectPublicStatus(projectPayload);

const createSubmission$ = this.actions.createCollectionSubmission(payload);
const createCedar$: Observable<unknown> = cedarData
? this.actions.createCedarRecord(cedarData, project.id as string, ResourceType.Project)
: of(null);

forkJoin({
publicStatusUpdate: updatePublicStatus$,
collectionSubmission: createSubmission$,
})
.pipe(takeUntilDestroyed(this.destroyRef))
updatePublicStatus$
.pipe(
switchMap(() => createCedar$),
switchMap(() => this.actions.createCollectionSubmission(payload)),
takeUntilDestroyed(this.destroyRef)
)
.subscribe({
next: () => {
this.isSubmitting.set(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ <h1 class="collections-heading flex align-items-center">{{ collectionProvider()?
[targetStepValue]="AddToCollectionSteps.CollectionMetadata"
[isDisabled]="isCollectionMetadataDisabled()"
[primaryCollectionId]="primaryCollectionId()"
[isCedarMode]="isCedarMode()"
[cedarTemplate]="requiredMetadataTemplate()"
[existingCedarRecord]="existingCedarRecord()"
(metadataSaved)="handleCollectionMetadataSaved($event)"
(cedarDataSaved)="handleCedarDataSaved($event)"
(stepChange)="handleChangeStep($event)"
/>
</p-stepper>
Expand Down
Loading
Loading