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
22 changes: 13 additions & 9 deletions src/app/features/analytics/analytics.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,31 @@ import { IS_WEB } from '@shared/helpers';
import { MOCK_ANALYTICS_METRICS, MOCK_RELATED_COUNTS, MOCK_RESOURCE_OVERVIEW } from '@shared/mocks';

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('AnalyticsComponent', () => {
let component: AnalyticsComponent;
let fixture: ComponentFixture<AnalyticsComponent>;
let routerMock: ReturnType<RouterMockBuilder['build']>;
let activatedRouteMock: ReturnType<ActivatedRouteMockBuilder['build']>;

const resourceId = MOCK_RESOURCE_OVERVIEW.id;
const metrics = { ...MOCK_ANALYTICS_METRICS, id: resourceId };
const relatedCounts = { ...MOCK_RELATED_COUNTS, id: resourceId };

const metricsSelector = AnalyticsSelectors.getMetrics(resourceId);
const relatedCountsSelector = AnalyticsSelectors.getRelatedCounts(resourceId);

beforeEach(async () => {
routerMock = RouterMockBuilder.create().build();
activatedRouteMock = ActivatedRouteMockBuilder.create()
.withParams({ id: resourceId })
.withData({ resourceType: undefined })
.build();

jest.clearAllMocks();

await TestBed.configureTestingModule({
imports: [
AnalyticsComponent,
Expand Down Expand Up @@ -67,14 +77,8 @@ describe('AnalyticsComponent', () => {
],
}),
{ provide: IS_WEB, useValue: of(true) },
MockProvider(Router, { navigate: jest.fn(), url: '/' }),
{
provide: ActivatedRoute,
useValue: {
parent: { params: of({ id: resourceId }) },
data: of({ resourceType: undefined }),
},
},
MockProvider(Router, routerMock),
MockProvider(ActivatedRoute, activatedRouteMock),
],
}).compileComponents();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,24 @@ import { MOCK_PROJECT_OVERVIEW } from '@shared/mocks';
import { ViewDuplicatesComponent } from './view-duplicates.component';

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('ViewDuplicatesComponent', () => {
let component: ViewDuplicatesComponent;
let fixture: ComponentFixture<ViewDuplicatesComponent>;
let dialogService: DialogService;
let routerMock: ReturnType<RouterMockBuilder['build']>;
let activatedRouteMock: ReturnType<ActivatedRouteMockBuilder['build']>;

beforeEach(async () => {
routerMock = RouterMockBuilder.create().build();
activatedRouteMock = ActivatedRouteMockBuilder.create()
.withParams({ id: 'rid' })
.withData({ resourceType: ResourceType.Project })
.build();

await TestBed.configureTestingModule({
imports: [
ViewDuplicatesComponent,
Expand All @@ -57,15 +67,9 @@ describe('ViewDuplicatesComponent', () => {
{ selector: RegistryOverviewSelectors.isRegistryAnonymous, value: false },
],
}),
MockProvider(Router, { navigate: jest.fn() }),
MockProvider(Router, routerMock),
MockProvider(ActivatedRoute, activatedRouteMock),
{ provide: IS_SMALL, useValue: of(false) },
{
provide: ActivatedRoute,
useValue: {
parent: { params: of({ id: 'rid' }) },
data: of({ resourceType: ResourceType.Project }),
},
},
],
}).compileComponents();

Expand Down
48 changes: 46 additions & 2 deletions src/app/features/home/home.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import { MockComponents, MockProvider } from 'ng-mocks';

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

import { IconComponent, SearchInputComponent } from '@shared/components';

import { HomeComponent } from './home.component';

describe.skip('HomeComponent', () => {
import { OSFTestingModule } from '@testing/osf.testing.module';
import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock';
import { RouterMockBuilder } from '@testing/providers/router-provider.mock';

describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
let routerMock: ReturnType<RouterMockBuilder['build']>;
let activatedRouteMock: ReturnType<ActivatedRouteMockBuilder['build']>;

beforeEach(async () => {
routerMock = RouterMockBuilder.create().build();
activatedRouteMock = ActivatedRouteMockBuilder.create().build();

await TestBed.configureTestingModule({
imports: [HomeComponent],
imports: [HomeComponent, OSFTestingModule, ...MockComponents(SearchInputComponent, IconComponent)],
providers: [MockProvider(Router, routerMock), MockProvider(ActivatedRoute, activatedRouteMock)],
}).compileComponents();

fixture = TestBed.createComponent(HomeComponent);
Expand All @@ -19,4 +34,33 @@ describe.skip('HomeComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

it('should navigate to search page with empty string when redirectToSearchPageWithValue is called with no value', () => {
component.redirectToSearchPageWithValue();

expect(routerMock.navigate).toHaveBeenCalledWith(['/search'], {
queryParams: { search: '' },
});
});

it('should navigate to search page with search value when searchControl has a value', () => {
const searchValue = 'test search query';
component.searchControl.setValue(searchValue);

component.redirectToSearchPageWithValue();

expect(routerMock.navigate).toHaveBeenCalledWith(['/search'], {
queryParams: { search: searchValue },
});
});

it('should navigate to search page with null when searchControl is set to null', () => {
component.searchControl.setValue(null);

component.redirectToSearchPageWithValue();

expect(routerMock.navigate).toHaveBeenCalledWith(['/search'], {
queryParams: { search: null },
});
});
});
2 changes: 1 addition & 1 deletion src/app/features/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { INTEGRATION_ICONS, SLIDES } from './constants';
export class HomeComponent {
private readonly router = inject(Router);

protected searchControl = new FormControl<string>('');
searchControl = new FormControl<string>('');

readonly icons = INTEGRATION_ICONS;
readonly slides = SLIDES;
Expand Down
156 changes: 154 additions & 2 deletions src/app/features/home/pages/dashboard/dashboard.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,50 @@
import { MockComponents, MockProvider } from 'ng-mocks';

import { TablePageEvent } from 'primeng/table';

import { of } from 'rxjs';

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

import { MyResourcesSelectors } from '@osf/shared/stores';
import { IconComponent, MyProjectsTableComponent, SubHeaderComponent } from '@shared/components';
import { IS_MEDIUM } from '@shared/helpers';
import { MyResourcesItem } from '@shared/models';

import { DashboardComponent } from './dashboard.component';

describe.skip('DashboardComponent', () => {
import { OSFTestingModule } from '@testing/osf.testing.module';
import { ActivatedRouteMock } from '@testing/providers/route-provider.mock';
import { RouterMock, RouterMockType } from '@testing/providers/router-provider.mock';
import { provideMockStore } from '@testing/providers/store-provider.mock';

describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
let routerMock: RouterMockType;

beforeEach(async () => {
routerMock = RouterMock.create().build();

await TestBed.configureTestingModule({
imports: [DashboardComponent],
imports: [
DashboardComponent,
...MockComponents(SubHeaderComponent, MyProjectsTableComponent, IconComponent),
OSFTestingModule,
],
providers: [
provideMockStore({
signals: [
{ selector: MyResourcesSelectors.getProjects, value: [] },
{ selector: MyResourcesSelectors.getTotalProjects, value: 0 },
{ selector: MyResourcesSelectors.getProjectsLoading, value: false },
],
}),
MockProvider(Router, routerMock),
MockProvider(IS_MEDIUM, of(false)),
MockProvider(ActivatedRoute, ActivatedRouteMock.withQueryParams({}).build()),
],
}).compileComponents();

fixture = TestBed.createComponent(DashboardComponent);
Expand All @@ -19,4 +55,120 @@ describe.skip('DashboardComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

it('should navigate to project on navigateToProject', () => {
const navigateSpy = routerMock.navigate as jest.Mock;
component.navigateToProject({ id: 'p1', title: 'T' } as MyResourcesItem);
expect(navigateSpy).toHaveBeenCalledWith(['p1']);
});

it('should open create project dialog with width 95vw when not medium', () => {
const dialogOpenSpy = jest.spyOn((component as any).dialogService, 'open');
component.createProject();
expect(dialogOpenSpy).toHaveBeenCalledWith(expect.any(Function), expect.objectContaining({ width: '95vw' }));
});

it('should update query params on page change', () => {
const navigateSpy = routerMock.navigate as jest.Mock;
component.onPageChange({ first: 20, rows: 10 } as TablePageEvent);
expect(navigateSpy).toHaveBeenCalledWith([], expect.objectContaining({ queryParamsHandling: 'merge' }));
});

it('should update query params on sort', () => {
const navigateSpy = routerMock.navigate as jest.Mock;
component.onSort({ field: 'title', order: -1 });
expect(navigateSpy).toHaveBeenCalled();
});

it('updateQueryParams should send expected query params (isSearch=false)', () => {
const navigateSpy = routerMock.navigate as jest.Mock;

component.tableParams.update((c) => ({ ...c, rows: 10, firstRowIndex: 20 }));
component.sortColumn.set('title');
component.sortOrder.set(-1);
component.searchControl.setValue('hello');

component.updateQueryParams(false);

expect(navigateSpy).toHaveBeenCalledWith(
[],
expect.objectContaining({
queryParamsHandling: 'merge',
queryParams: expect.objectContaining({
page: 3,
rows: 10,
search: 'hello',
sortField: 'title',
sortOrder: -1,
}),
})
);
});

it('updateQueryParams should reset page to 1 when isSearch=true', () => {
const navigateSpy = routerMock.navigate as jest.Mock;

component.tableParams.update((c) => ({ ...c, rows: 25, firstRowIndex: 50 }));
component.updateQueryParams(true);

expect(navigateSpy).toHaveBeenCalledWith(
[],
expect.objectContaining({ queryParams: expect.objectContaining({ page: 1, rows: 25 }) })
);
});

it('createFilters should map control and sort signals', () => {
component.searchControl.setValue('query');
component.sortColumn.set('title');
component.sortOrder.set(-1);

const filters = component.createFilters();
expect(filters).toEqual({
searchValue: 'query',
searchFields: ['title'],
sortColumn: 'title',
sortOrder: -1,
});
});

it('fetchProjects should dispatch getMyProjects with computed page and filters', () => {
(component as any).actions = { ...component['actions'], getMyProjects: jest.fn() };

component.tableParams.update((c) => ({ ...c, rows: 15, firstRowIndex: 30 }));

const mockFilters = { searchValue: '', searchFields: [], sortColumn: undefined, sortOrder: 1 };
const filtersSpy = jest.spyOn(component, 'createFilters').mockReturnValue(mockFilters);

component.fetchProjects();

expect(filtersSpy).toHaveBeenCalled();
expect((component as any).actions.getMyProjects).toHaveBeenCalledWith(3, 15, mockFilters);
});

it('setupTotalRecordsEffect should update totalRecords from selector value', () => {
expect(component.tableParams().totalRecords).toBe(0);
});

it('setupQueryParamsSubscription should parse params and call fetchProjects', () => {
const fetchSpy = jest.spyOn(component, 'fetchProjects');

const routeMock = ActivatedRouteMock.withQueryParams({
page: 2,
rows: 5,
sortField: 'title',
sortOrder: -1,
search: 'abc',
}).build();

(component as any).route = routeMock as any;

component.setupQueryParamsSubscription();

expect(component.tableParams().firstRowIndex).toBe(5);
expect(component.tableParams().rows).toBe(5);
expect(component.sortColumn()).toBe('title');
expect(component.sortOrder()).toBe(-1);
expect(component.searchControl.value).toBe('abc');
expect(fetchSpy).toHaveBeenCalled();
});
});
Loading
Loading