Skip to content

Commit e978968

Browse files
committed
test(unit-tests): added unit tests for skipped tests
1 parent a02d0bc commit e978968

File tree

7 files changed

+758
-59
lines changed

7 files changed

+758
-59
lines changed

src/app/features/institutions/pages/institutions-list/institutions-list.component.spec.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,30 @@
1-
import { MockComponents, MockProvider } from 'ng-mocks';
1+
import { Store } from '@ngxs/store';
22

3-
import { ComponentFixture, TestBed } from '@angular/core/testing';
3+
import { MockComponents } from 'ng-mocks';
4+
5+
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
46
import { FormControl } from '@angular/forms';
5-
import { ActivatedRoute, Router } from '@angular/router';
67

78
import { ScheduledBannerComponent } from '@core/components/osf-banners/scheduled-banner/scheduled-banner.component';
89
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
910
import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component';
1011
import { SubHeaderComponent } from '@osf/shared/components/sub-header/sub-header.component';
11-
import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
12+
import { FetchInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
1213

1314
import { InstitutionsListComponent } from './institutions-list.component';
1415

1516
import { MOCK_INSTITUTION } from '@testing/mocks/institution.mock';
1617
import { OSFTestingModule } from '@testing/osf.testing.module';
17-
import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock';
18-
import { RouterMockBuilder } from '@testing/providers/router-provider.mock';
1918
import { provideMockStore } from '@testing/providers/store-provider.mock';
2019

21-
describe.skip('Component: Institutions List', () => {
20+
describe('InstitutionsListComponent', () => {
2221
let component: InstitutionsListComponent;
2322
let fixture: ComponentFixture<InstitutionsListComponent>;
24-
let routerMock: ReturnType<RouterMockBuilder['build']>;
25-
let activatedRouteMock: ReturnType<ActivatedRouteMockBuilder['build']>;
23+
let store: Store;
2624

2725
const mockInstitutions = [MOCK_INSTITUTION];
28-
const mockTotalCount = 2;
2926

3027
beforeEach(async () => {
31-
routerMock = RouterMockBuilder.create().build();
32-
activatedRouteMock = ActivatedRouteMockBuilder.create()
33-
.withQueryParams({ page: '1', size: '10', search: '' })
34-
.build();
35-
3628
await TestBed.configureTestingModule({
3729
imports: [
3830
InstitutionsListComponent,
@@ -43,24 +35,42 @@ describe.skip('Component: Institutions List', () => {
4335
provideMockStore({
4436
signals: [
4537
{ selector: InstitutionsSelectors.getInstitutions, value: mockInstitutions },
46-
{ selector: InstitutionsSelectors.getInstitutionsTotalCount, value: mockTotalCount },
4738
{ selector: InstitutionsSelectors.isInstitutionsLoading, value: false },
4839
],
4940
}),
50-
MockProvider(Router, routerMock),
51-
MockProvider(ActivatedRoute, activatedRouteMock),
5241
],
5342
}).compileComponents();
5443

5544
fixture = TestBed.createComponent(InstitutionsListComponent);
5645
component = fixture.componentInstance;
46+
store = TestBed.inject(Store);
5747
fixture.detectChanges();
5848
});
5949

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

54+
it('should dispatch FetchInstitutions on init', () => {
55+
expect(store.dispatch).toHaveBeenCalledWith(expect.any(FetchInstitutions));
56+
const action = (store.dispatch as jest.Mock).mock.calls[0][0] as FetchInstitutions;
57+
expect(action.searchValue).toBeUndefined();
58+
});
59+
60+
it('should dispatch FetchInstitutions with search value after debounce', fakeAsync(() => {
61+
(store.dispatch as jest.Mock).mockClear();
62+
component.searchControl.setValue('test search');
63+
tick(300);
64+
expect(store.dispatch).toHaveBeenCalledWith(new FetchInstitutions('test search'));
65+
}));
66+
67+
it('should dispatch FetchInstitutions with empty string when search is null', fakeAsync(() => {
68+
(store.dispatch as jest.Mock).mockClear();
69+
component.searchControl.setValue(null);
70+
tick(300);
71+
expect(store.dispatch).toHaveBeenCalledWith(new FetchInstitutions(''));
72+
}));
73+
6474
it('should initialize with correct default values', () => {
6575
expect(component.classes).toBe('flex-1 flex flex-column w-full');
6676
expect(component.searchControl).toBeInstanceOf(FormControl);
Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,182 @@
1+
import { Store } from '@ngxs/store';
2+
13
import { MockComponent } from 'ng-mocks';
24

5+
import { of } from 'rxjs';
6+
37
import { ComponentFixture, TestBed } from '@angular/core/testing';
48

9+
import { UserSelectors } from '@core/store/user';
510
import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components/affiliated-institution-select/affiliated-institution-select.component';
11+
import { ComponentFormControls } from '@osf/shared/enums/create-component-form-controls.enum';
12+
import { ToastService } from '@osf/shared/services/toast.service';
13+
import { FetchUserInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
14+
import { FetchRegions, RegionsSelectors } from '@osf/shared/stores/regions';
15+
16+
import { CreateComponent, GetComponents, ProjectOverviewSelectors } from '../../store';
617

718
import { AddComponentDialogComponent } from './add-component-dialog.component';
819

9-
describe.skip('AddComponentComponent', () => {
20+
import { MOCK_INSTITUTION } from '@testing/mocks/institution.mock';
21+
import { MOCK_PROJECT } from '@testing/mocks/project.mock';
22+
import { OSFTestingModule } from '@testing/osf.testing.module';
23+
import { provideMockStore } from '@testing/providers/store-provider.mock';
24+
25+
describe('AddComponentDialogComponent', () => {
1026
let component: AddComponentDialogComponent;
1127
let fixture: ComponentFixture<AddComponentDialogComponent>;
28+
let store: Store;
29+
30+
const mockRegions = [{ id: 'region-1', name: 'Region 1' }];
31+
const mockUser = { id: 'user-1', defaultRegionId: 'user-region' } as any;
32+
const mockProject = { ...MOCK_PROJECT, id: 'proj-1', title: 'Project', tags: ['tag1'] };
33+
const mockInstitutions = [MOCK_INSTITUTION];
34+
const mockUserInstitutions = [MOCK_INSTITUTION, { ...MOCK_INSTITUTION, id: 'inst-2', name: 'Inst 2' }];
1235

1336
beforeEach(async () => {
1437
await TestBed.configureTestingModule({
15-
imports: [AddComponentDialogComponent, MockComponent(AffiliatedInstitutionSelectComponent)],
38+
imports: [AddComponentDialogComponent, OSFTestingModule, MockComponent(AffiliatedInstitutionSelectComponent)],
39+
providers: [
40+
provideMockStore({
41+
signals: [
42+
{ selector: RegionsSelectors.getRegions, value: mockRegions },
43+
{ selector: UserSelectors.getCurrentUser, value: mockUser },
44+
{ selector: ProjectOverviewSelectors.getProject, value: mockProject },
45+
{ selector: ProjectOverviewSelectors.getInstitutions, value: mockInstitutions },
46+
{ selector: RegionsSelectors.areRegionsLoading, value: false },
47+
{ selector: ProjectOverviewSelectors.getComponentsSubmitting, value: false },
48+
{ selector: InstitutionsSelectors.getUserInstitutions, value: mockUserInstitutions },
49+
{ selector: InstitutionsSelectors.areUserInstitutionsLoading, value: false },
50+
],
51+
}),
52+
],
1653
}).compileComponents();
1754

1855
fixture = TestBed.createComponent(AddComponentDialogComponent);
1956
component = fixture.componentInstance;
57+
store = TestBed.inject(Store);
58+
(store.dispatch as jest.Mock).mockReturnValue(of(void 0));
2059
fixture.detectChanges();
2160
});
2261

2362
it('should create', () => {
2463
expect(component).toBeTruthy();
2564
});
65+
66+
it('should initialize form with default values', () => {
67+
expect(component.componentForm.get(ComponentFormControls.Title)?.value).toBe('');
68+
expect(Array.isArray(component.componentForm.get(ComponentFormControls.Affiliations)?.value)).toBe(true);
69+
expect(component.componentForm.get(ComponentFormControls.Description)?.value).toBe('');
70+
expect(component.componentForm.get(ComponentFormControls.AddContributors)?.value).toBe(false);
71+
expect(component.componentForm.get(ComponentFormControls.AddTags)?.value).toBe(false);
72+
expect(['', 'user-region']).toContain(component.componentForm.get(ComponentFormControls.StorageLocation)?.value);
73+
});
74+
75+
it('should dispatch FetchRegions and FetchUserInstitutions on init', () => {
76+
expect(store.dispatch).toHaveBeenCalledWith(expect.any(FetchRegions));
77+
expect(store.dispatch).toHaveBeenCalledWith(expect.any(FetchUserInstitutions));
78+
});
79+
80+
it('should return store values from selectors', () => {
81+
expect(component.storageLocations()).toEqual(mockRegions);
82+
expect(component.currentUser()).toEqual(mockUser);
83+
expect(component.currentProject()).toEqual(mockProject);
84+
expect(component.institutions()).toEqual(mockInstitutions);
85+
expect(component.areRegionsLoading()).toBe(false);
86+
expect(component.isSubmitting()).toBe(false);
87+
expect(component.userInstitutions()).toEqual(mockUserInstitutions);
88+
expect(component.areUserInstitutionsLoading()).toBe(false);
89+
});
90+
91+
it('should set affiliations form control from selected institutions', () => {
92+
const institutions = [MOCK_INSTITUTION];
93+
component.setSelectedInstitutions(institutions);
94+
expect(component.componentForm.get(ComponentFormControls.Affiliations)?.value).toEqual([MOCK_INSTITUTION.id]);
95+
});
96+
97+
it('should mark form as touched and not dispatch when submitForm with invalid form', () => {
98+
(store.dispatch as jest.Mock).mockClear();
99+
component.componentForm.get(ComponentFormControls.Title)?.setValue('');
100+
component.submitForm();
101+
expect(component.componentForm.touched).toBe(true);
102+
const createCalls = (store.dispatch as jest.Mock).mock.calls.filter((c) => c[0] instanceof CreateComponent);
103+
expect(createCalls.length).toBe(0);
104+
});
105+
106+
it('should dispatch CreateComponent and on success close dialog, getComponents, showSuccess', () => {
107+
component.componentForm.get(ComponentFormControls.Title)?.setValue('New Component');
108+
component.componentForm.get(ComponentFormControls.StorageLocation)?.setValue('region-1');
109+
component.componentForm.get(ComponentFormControls.Affiliations)?.setValue([MOCK_INSTITUTION.id]);
110+
(store.dispatch as jest.Mock).mockClear();
111+
112+
component.submitForm();
113+
114+
expect(store.dispatch).toHaveBeenCalledWith(
115+
new CreateComponent(mockProject.id, 'New Component', '', [], 'region-1', [MOCK_INSTITUTION.id], false)
116+
);
117+
expect(component.dialogRef.close).toHaveBeenCalled();
118+
expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetComponents));
119+
expect(TestBed.inject(ToastService).showSuccess).toHaveBeenCalledWith(
120+
'project.overview.dialog.toast.addComponent.success'
121+
);
122+
});
123+
124+
it('should pass project tags when addTags is true', () => {
125+
component.componentForm.get(ComponentFormControls.Title)?.setValue('With Tags');
126+
component.componentForm.get(ComponentFormControls.StorageLocation)?.setValue('region-1');
127+
component.componentForm.get(ComponentFormControls.Affiliations)?.setValue([]);
128+
component.componentForm.get(ComponentFormControls.AddTags)?.setValue(true);
129+
(store.dispatch as jest.Mock).mockClear();
130+
131+
component.submitForm();
132+
133+
expect(store.dispatch).toHaveBeenCalledWith(
134+
new CreateComponent(mockProject.id, 'With Tags', '', mockProject.tags, 'region-1', [], false)
135+
);
136+
});
137+
138+
it('should set storage location to user default region when control empty and regions loaded', () => {
139+
fixture = TestBed.createComponent(AddComponentDialogComponent);
140+
component = fixture.componentInstance;
141+
component.componentForm.get(ComponentFormControls.StorageLocation)?.setValue('');
142+
fixture.detectChanges();
143+
expect(component.componentForm.get(ComponentFormControls.StorageLocation)?.value).toBe('user-region');
144+
});
145+
});
146+
147+
describe('AddComponentDialogComponent when user has no default region', () => {
148+
let component: AddComponentDialogComponent;
149+
let fixture: ComponentFixture<AddComponentDialogComponent>;
150+
151+
const mockRegions = [{ id: 'region-1', name: 'Region 1' }];
152+
const mockProject = { ...MOCK_PROJECT, id: 'proj-1', title: 'Project', tags: ['tag1'] };
153+
154+
beforeEach(async () => {
155+
await TestBed.configureTestingModule({
156+
imports: [AddComponentDialogComponent, OSFTestingModule, MockComponent(AffiliatedInstitutionSelectComponent)],
157+
providers: [
158+
provideMockStore({
159+
signals: [
160+
{ selector: RegionsSelectors.getRegions, value: mockRegions },
161+
{ selector: UserSelectors.getCurrentUser, value: null },
162+
{ selector: ProjectOverviewSelectors.getProject, value: mockProject },
163+
{ selector: ProjectOverviewSelectors.getInstitutions, value: [] },
164+
{ selector: RegionsSelectors.areRegionsLoading, value: false },
165+
{ selector: ProjectOverviewSelectors.getComponentsSubmitting, value: false },
166+
{ selector: InstitutionsSelectors.getUserInstitutions, value: [] },
167+
{ selector: InstitutionsSelectors.areUserInstitutionsLoading, value: false },
168+
],
169+
}),
170+
],
171+
}).compileComponents();
172+
173+
fixture = TestBed.createComponent(AddComponentDialogComponent);
174+
component = fixture.componentInstance;
175+
component.componentForm.get(ComponentFormControls.StorageLocation)?.setValue('');
176+
fixture.detectChanges();
177+
});
178+
179+
it('should set storage location to first region when control empty', () => {
180+
expect(component.componentForm.get(ComponentFormControls.StorageLocation)?.value).toBe('region-1');
181+
});
26182
});

0 commit comments

Comments
 (0)