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
18 changes: 4 additions & 14 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ module.exports = {
extensionsToTreatAsEsm: ['.ts'],
coverageThreshold: {
global: {
branches: 28.0,
functions: 32.0,
lines: 60.28,
statements: 60.77,
branches: 31.0,
functions: 35.0,
lines: 64.0,
statements: 64.0,
},
},
watchPathIgnorePatterns: [
Expand All @@ -70,19 +70,9 @@ module.exports = {
],
testPathIgnorePatterns: [
'<rootDir>/src/environments',
'<rootDir>/src/app/app.config.ts',
'<rootDir>/src/app/features/files/pages/file-detail',
'<rootDir>/src/app/features/project/addons/',
'<rootDir>/src/app/features/project/registrations',
'<rootDir>/src/app/features/project/wiki',
'<rootDir>/src/app/features/registry/components',
'<rootDir>/src/app/features/registry/pages/registry-wiki/registry-wiki',
'<rootDir>/src/app/features/settings/addons/',
'<rootDir>/src/app/features/settings/tokens/store/',
'<rootDir>/src/app/shared/components/file-menu/',
'<rootDir>/src/app/shared/components/line-chart/',
'<rootDir>/src/app/shared/components/pie-chart/',
'<rootDir>/src/app/shared/components/reusable-filter/',
'<rootDir>/src/app/shared/components/wiki/edit-section/',
],
};
8 changes: 8 additions & 0 deletions setup-jest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ Object.defineProperty(window, 'ResizeObserver', {
value: ResizeObserver,
});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(global as any).ace = {
define: jest.fn(),
require: jest.fn().mockReturnValue({
snippetCompleter: {},
}),
};

jest.mock('@newrelic/browser-agent/loaders/browser-agent', () => ({
BrowserAgent: jest.fn().mockImplementation(() => ({
start: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
Signal,
signal,
} from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';

import { UserSelectors } from '@core/store/user';
Expand Down Expand Up @@ -75,7 +75,6 @@ export class ViewDuplicatesComponent {
isAuthenticated = select(UserSelectors.isAuthenticated);

readonly pageSize = 10;
readonly UserPermissions = UserPermissions;

currentPage = signal<number>(1);
firstIndex = computed(() => (this.currentPage() - 1) * this.pageSize);
Expand Down Expand Up @@ -180,7 +179,8 @@ export class ViewDuplicatesComponent {
resourceType: this.resourceType(),
},
})
.onClose.subscribe((result) => {
.onClose.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((result) => {
if (result?.success) {
this.actions.getDuplicates(currentResource.id, currentResource.type, this.currentPage(), this.pageSize);
}
Expand Down Expand Up @@ -224,7 +224,8 @@ export class ViewDuplicatesComponent {
pageSize: this.pageSize,
},
})
.onClose.subscribe((result) => {
.onClose.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((result) => {
if (result?.success) {
const resource = this.currentResource();
if (resource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,11 @@ import { CanDeactivateComponent } from '@shared/models/can-deactivate.interface'
import { CollectionsSelectors, GetCollectionProvider } from '@shared/stores/collections';
import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors';

import {
AddToCollectionConfirmationDialogComponent,
CollectionMetadataStepComponent,
ProjectContributorsStepComponent,
ProjectMetadataStepComponent,
SelectProjectStepComponent,
} from './index';
import { AddToCollectionConfirmationDialogComponent } from './add-to-collection-confirmation-dialog/add-to-collection-confirmation-dialog.component';
import { CollectionMetadataStepComponent } from './collection-metadata-step/collection-metadata-step.component';
import { ProjectContributorsStepComponent } from './project-contributors-step/project-contributors-step.component';
import { ProjectMetadataStepComponent } from './project-metadata-step/project-metadata-step.component';
import { SelectProjectStepComponent } from './select-project-step/select-project-step.component';

@Component({
selector: 'osf-add-to-collection-form',
Expand Down
120 changes: 112 additions & 8 deletions src/app/features/project/registrations/registrations.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,145 @@
import { TranslatePipe } from '@ngx-translate/core';
import { MockComponents } from 'ng-mocks';
import { MockComponents, MockProvider } from 'ng-mocks';

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

import { ENVIRONMENT } from '@core/provider/environment.provider';
import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component';
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
import { RegistrationCardComponent } from '@osf/shared/components/registration-card/registration-card.component';
import { SubHeaderComponent } from '@osf/shared/components/sub-header/sub-header.component';
import { CurrentResourceSelectors } from '@shared/stores/current-resource';

import { RegistrationsComponent } from './registrations.component';
import { GetRegistrations, RegistrationsSelectors } from './store';

import { MOCK_REGISTRATION } from '@testing/mocks/registration.mock';
import { OSFTestingModule } from '@testing/osf.testing.module';
import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock';
import { RouterMockBuilder } from '@testing/providers/router-provider.mock';
import { provideMockStore } from '@testing/providers/store-provider.mock';

describe('RegistrationsComponent', () => {
let component: RegistrationsComponent;
let fixture: ComponentFixture<RegistrationsComponent>;
let routerMock: ReturnType<RouterMockBuilder['build']>;
let activatedRouteMock: ReturnType<ActivatedRouteMockBuilder['build']>;
let storeDispatchSpy: jest.SpyInstance;

const mockProjectId = 'project-123';
const mockRegistrations = [MOCK_REGISTRATION];
const mockEnvironment = {
defaultProvider: 'test-provider',
};

beforeEach(async () => {
routerMock = RouterMockBuilder.create().build();
activatedRouteMock = ActivatedRouteMockBuilder.create().withParams({ id: mockProjectId }).build();

const mockStore = provideMockStore({
signals: [
{ selector: CurrentResourceSelectors.hasAdminAccess, value: signal(true) },
{ selector: RegistrationsSelectors.getRegistrations, value: signal(mockRegistrations) },
{ selector: RegistrationsSelectors.getRegistrationsTotalCount, value: signal(10) },
{ selector: RegistrationsSelectors.isRegistrationsLoading, value: signal(false) },
],
});

storeDispatchSpy = jest.spyOn(mockStore.useValue, 'dispatch');

await TestBed.configureTestingModule({
imports: [
RegistrationsComponent,
OSFTestingModule,
...MockComponents(
RegistrationCardComponent,
SubHeaderComponent,
FormsModule,
TranslatePipe,
LoadingSpinnerComponent,
CustomPaginatorComponent
),
],
providers: [
MockProvider(Router, routerMock),
MockProvider(ActivatedRoute, activatedRouteMock),
MockProvider(ENVIRONMENT, mockEnvironment),
mockStore,
],
}).compileComponents();

fixture = TestBed.createComponent(RegistrationsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
it('should have default values', () => {
expect(component.itemsPerPage).toBe(10);
expect(component.first).toBe(0);
});

it('should initialize projectId from route params', () => {
expect(component.projectId()).toBe(mockProjectId);
});

it('should dispatch getRegistrations action on ngOnInit', () => {
component.ngOnInit();

expect(storeDispatchSpy).toHaveBeenCalledWith(
expect.objectContaining({
projectId: mockProjectId,
page: 1,
pageSize: 10,
})
);
expect(storeDispatchSpy).toHaveBeenCalledWith(expect.any(GetRegistrations));
});

it('should navigate to registries route when addRegistration is called', () => {
const navigateSpy = jest.spyOn(routerMock, 'navigate');

component.addRegistration();

expect(navigateSpy).toHaveBeenCalledWith([`registries/${mockEnvironment.defaultProvider}/new`], {
queryParams: { projectId: mockProjectId },
});
});

it('should dispatch getRegistrations and update first on page change', () => {
const mockPaginatorState = {
page: 2,
first: 20,
rows: 10,
pageCount: 5,
} as any;

component.onPageChange(mockPaginatorState);

expect(storeDispatchSpy).toHaveBeenCalledWith(
expect.objectContaining({
projectId: mockProjectId,
page: 3,
pageSize: 10,
})
);
expect(component.first).toBe(20);
});

it('should handle page change with page 0', () => {
const mockPaginatorState = {
page: 0,
first: 0,
rows: 10,
pageCount: 5,
} as any;

component.onPageChange(mockPaginatorState);

expect(storeDispatchSpy).toHaveBeenCalledWith(
expect.objectContaining({
projectId: mockProjectId,
page: 1,
pageSize: 10,
})
);
expect(component.first).toBe(0);
});
});
Loading