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
93 changes: 85 additions & 8 deletions src/app/features/analytics/analytics.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,105 @@
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { MockComponent, MockPipe, MockProvider } from 'ng-mocks';
import { MockComponents, MockProvider } from 'ng-mocks';

import { of } from 'rxjs';

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

import { SubHeaderComponent } from '@osf/shared/components';
import { AnalyticsComponent } from '@osf/features/analytics/analytics.component';
import { AnalyticsKpiComponent } from '@osf/features/analytics/components';
import { AnalyticsSelectors } from '@osf/features/analytics/store';
import {
BarChartComponent,
LineChartComponent,
PieChartComponent,
SelectComponent,
SubHeaderComponent,
ViewOnlyLinkMessageComponent,
} from '@shared/components';
import { IS_WEB } from '@shared/helpers';
import { MOCK_ANALYTICS_METRICS, MOCK_RELATED_COUNTS, MOCK_RESOURCE_OVERVIEW } from '@shared/mocks';

import { AnalyticsComponent } from './analytics.component';
import { OSFTestingModule } from '@testing/osf.testing.module';
import { provideMockStore } from '@testing/providers/store-provider.mock';

describe.skip('AnalyticsComponent', () => {
describe('AnalyticsComponent', () => {
let component: AnalyticsComponent;
let fixture: ComponentFixture<AnalyticsComponent>;

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 () => {
jest.clearAllMocks();
await TestBed.configureTestingModule({
imports: [AnalyticsComponent, MockComponent(SubHeaderComponent), MockPipe(TranslatePipe)],
providers: [MockProvider(TranslateService)],
imports: [
AnalyticsComponent,
...MockComponents(
SubHeaderComponent,
AnalyticsKpiComponent,
LineChartComponent,
BarChartComponent,
PieChartComponent,
ViewOnlyLinkMessageComponent,
SelectComponent
),
OSFTestingModule,
],
providers: [
provideMockStore({
selectors: [
{ selector: metricsSelector, value: metrics },
{ selector: relatedCountsSelector, value: relatedCounts },
{ selector: AnalyticsSelectors.isMetricsLoading, value: false },
{ selector: AnalyticsSelectors.isRelatedCountsLoading, value: false },
{ selector: AnalyticsSelectors.isMetricsError, value: false },
],
signals: [
{ selector: metricsSelector, value: metrics },
{ selector: relatedCountsSelector, value: relatedCounts },
{ selector: AnalyticsSelectors.isMetricsLoading, value: false },
{ selector: AnalyticsSelectors.isRelatedCountsLoading, value: false },
{ selector: AnalyticsSelectors.isMetricsError, value: false },
],
}),
{ provide: IS_WEB, useValue: of(true) },
MockProvider(Router, { navigate: jest.fn(), url: '/' }),
{
provide: ActivatedRoute,
useValue: {
parent: { params: of({ id: resourceId }) },
data: of({ resourceType: undefined }),
},
},
],
}).compileComponents();

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

it('should create', () => {
fixture.detectChanges();
expect(component).toBeTruthy();
});

it('should set selectedRange via onRangeChange', () => {
fixture.detectChanges();
component.onRangeChange('month');
expect(component.selectedRange()).toBe('month');
});

it('should navigate to duplicates with correct relative route', () => {
const router = TestBed.inject(Router);
const navigateSpy = jest.spyOn(router, 'navigate');

fixture.detectChanges();
component.navigateToDuplicates();

expect(navigateSpy).toHaveBeenCalledWith(['duplicates'], { relativeTo: expect.any(Object) });
});
});
Original file line number Diff line number Diff line change
@@ -1,22 +1,85 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { AnalyticsKpiComponent } from './analytics-kpi.component';

describe.skip('AnalyticsKpiComponent', () => {
import { OSFTestingModule } from '@testing/osf.testing.module';

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

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AnalyticsKpiComponent],
imports: [AnalyticsKpiComponent, OSFTestingModule],
}).compileComponents();

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

it('should create', () => {
expect(component).toBeTruthy();
});

it('should have default input values', () => {
expect(component.isLoading()).toBe(false);
expect(component.showButton()).toBe(false);
expect(component.buttonLabel()).toBe('');
expect(component.title()).toBe('');
expect(component.value()).toBe(0);
});

it('should update inputs via setInput', () => {
fixture.componentRef.setInput('isLoading', true);
fixture.componentRef.setInput('showButton', true);
fixture.componentRef.setInput('buttonLabel', 'CLICK_ME');
fixture.componentRef.setInput('title', 'T');
fixture.componentRef.setInput('value', 7);

expect(component.isLoading()).toBe(true);
expect(component.showButton()).toBe(true);
expect(component.buttonLabel()).toBe('CLICK_ME');
expect(component.title()).toBe('T');
expect(component.value()).toBe(7);
});

it('should render title set via setInput', () => {
fixture.componentRef.setInput('title', 'SOME_TITLE');
fixture.detectChanges();

const titleEl = fixture.debugElement.query(By.css('p.title'));
expect(titleEl).toBeTruthy();
expect(titleEl.nativeElement.textContent.trim()).toBe('SOME_TITLE');
});

it('should show button with label and emit on click', () => {
const clickSpy = jest.fn();
component.buttonClick.subscribe(() => clickSpy());

fixture.componentRef.setInput('showButton', true);
fixture.componentRef.setInput('buttonLabel', 'CLICK_ME');
fixture.detectChanges();

const nativeButton = fixture.debugElement.query(By.css('button.p-button'));
expect(nativeButton).toBeTruthy();
expect(nativeButton.nativeElement.textContent.trim()).toBe('CLICK_ME');

nativeButton.nativeElement.click();
expect(clickSpy).toHaveBeenCalled();
});

it('should toggle button visibility via setInput', () => {
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('button.p-button'))).toBeNull();

fixture.componentRef.setInput('showButton', true);
fixture.componentRef.setInput('buttonLabel', 'LBL');
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('button.p-button'))).toBeTruthy();

fixture.componentRef.setInput('showButton', false);
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('button.p-button'))).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -1,22 +1,132 @@
import { MockComponents, MockProvider } from 'ng-mocks';

import { DialogService } from 'primeng/dynamicdialog';
import { PaginatorState } from 'primeng/paginator';

import { of } from 'rxjs';

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

import { ProjectOverviewSelectors } from '@osf/features/project/overview/store';
import { RegistryOverviewSelectors } from '@osf/features/registry/store/registry-overview';
import { ResourceType } from '@osf/shared/enums';
import { IS_SMALL } from '@osf/shared/helpers';
import { DuplicatesSelectors } from '@osf/shared/stores';
import {
CustomPaginatorComponent,
IconComponent,
LoadingSpinnerComponent,
SubHeaderComponent,
TruncatedTextComponent,
} from '@shared/components';
import { MOCK_PROJECT_OVERVIEW } from '@shared/mocks';

import { ViewDuplicatesComponent } from './view-duplicates.component';

describe.skip('ViewForksComponent', () => {
import { OSFTestingModule } from '@testing/osf.testing.module';
import { provideMockStore } from '@testing/providers/store-provider.mock';

describe('ViewDuplicatesComponent', () => {
let component: ViewDuplicatesComponent;
let fixture: ComponentFixture<ViewDuplicatesComponent>;
let dialogService: DialogService;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ViewDuplicatesComponent],
imports: [
ViewDuplicatesComponent,
OSFTestingModule,
...MockComponents(
SubHeaderComponent,
TruncatedTextComponent,
LoadingSpinnerComponent,
CustomPaginatorComponent,
IconComponent
),
],
providers: [
provideMockStore({
signals: [
{ selector: DuplicatesSelectors.getDuplicates, value: [] },
{ selector: DuplicatesSelectors.getDuplicatesLoading, value: false },
{ selector: DuplicatesSelectors.getDuplicatesTotalCount, value: 0 },
{ selector: ProjectOverviewSelectors.getProject, value: MOCK_PROJECT_OVERVIEW },
{ selector: ProjectOverviewSelectors.isProjectAnonymous, value: false },
{ selector: RegistryOverviewSelectors.getRegistry, value: undefined },
{ selector: RegistryOverviewSelectors.isRegistryAnonymous, value: false },
],
}),
MockProvider(Router, { navigate: jest.fn() }),
{ provide: IS_SMALL, useValue: of(false) },
{
provide: ActivatedRoute,
useValue: {
parent: { params: of({ id: 'rid' }) },
data: of({ resourceType: ResourceType.Project }),
},
},
],
}).compileComponents();

fixture = TestBed.createComponent(ViewDuplicatesComponent);
component = fixture.componentInstance;

dialogService = fixture.debugElement.injector.get(DialogService);
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should open ForkDialog with width 95vw and refresh on success', () => {
const openSpy = jest.spyOn(dialogService, 'open').mockReturnValue({ onClose: of({ success: true }) } as any);
(component as any).actions = { ...component.actions, getDuplicates: jest.fn() };

component.handleForkResource();

expect(openSpy).toHaveBeenCalledWith(
expect.any(Function),
expect.objectContaining({
width: '95vw',
focusOnShow: false,
header: 'project.overview.dialog.fork.headerProject',
closeOnEscape: true,
modal: true,
closable: true,
data: expect.objectContaining({
resource: expect.any(Object),
resourceType: ResourceType.Project,
}),
})
);

expect((component as any).actions.getDuplicates).toHaveBeenCalled();
});

it('should open ForkDialog with width 450px when small and not refresh on failure', () => {
(component as any).isSmall = () => true;
(component as any).actions = { ...component.actions, getDuplicates: jest.fn() };

const openSpy = jest.spyOn(dialogService, 'open').mockReturnValue({ onClose: of({ success: false }) } as any);

component.handleForkResource();

expect(openSpy).toHaveBeenCalledWith(expect.any(Function), expect.objectContaining({ width: '450px' }));
expect((component as any).actions.getDuplicates).not.toHaveBeenCalled();
});

it('should update currentPage when page is defined', () => {
const event: PaginatorState = { page: 1 } as PaginatorState;
component.onPageChange(event);
expect(component.currentPage()).toBe('2');
});

it('should not update currentPage when page is undefined', () => {
component.currentPage.set('5');
const event: PaginatorState = { page: undefined } as PaginatorState;
component.onPageChange(event);
expect(component.currentPage()).toBe('5');
});
});
2 changes: 2 additions & 0 deletions src/app/features/files/pages/files/files.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ describe('Component: Files', () => {
'viewOnlyDownloadable',
'resourceId',
'provider',
'storage',
'totalCount',
]),
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class TokensService {
const request = TokenMapper.toRequest(name, scopes);

return this.jsonApiService
.post<JsonApiResponse<TokenGetResponseJsonApi, null>>(environment.apiDomainUrl + '/tokens/', request)
.post<JsonApiResponse<TokenGetResponseJsonApi, null>>(`${this.apiUrl}/tokens/`, request)
.pipe(map((response) => TokenMapper.fromGetResponse(response.data)));
}

Expand Down
30 changes: 30 additions & 0 deletions src/app/shared/mocks/analytics.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { AnalyticsMetricsModel, RelatedCountsModel } from '@osf/features/analytics/models';

export const MOCK_ANALYTICS_METRICS: AnalyticsMetricsModel = {
id: 'rid',
type: 'analytics',
uniqueVisits: [
{ date: '2023-01-01', count: 1 },
{ date: '2023-01-02', count: 2 },
],
timeOfDay: [
{ hour: 0, count: 5 },
{ hour: 1, count: 3 },
],
refererDomain: [
{ refererDomain: 'example.com', count: 4 },
{ refererDomain: 'osf.io', count: 6 },
],
popularPages: [
{ path: '/', route: '/', title: 'Home', count: 7 },
{ path: '/about', route: '/about', title: 'About', count: 2 },
],
};

export const MOCK_RELATED_COUNTS: RelatedCountsModel = {
id: 'rid',
isPublic: true,
forksCount: 1,
linksToCount: 2,
templateCount: 3,
};
1 change: 1 addition & 0 deletions src/app/shared/mocks/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { MOCK_ADDON } from './addon.mock';
export * from './analytics.mock';
export { CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK } from './cedar-metadata-data-template-json-api.mock';
export * from './contributors.mock';
export * from './custom-сonfirmation.service.mock';
Expand Down
Loading