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
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ module.exports = tseslint.config(
['^@ngxs'],

// NGX packages (ngx-... or @ngx/...)
['^ngx-', '^@ngx'],
['^ngx-', '^@ngx', '^ng-'],

// Third-party packages (primeng)
['^primeng'],
Expand Down
40 changes: 26 additions & 14 deletions src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
import { TestBed } from '@angular/core/testing';
import { provideStore, Store } from '@ngxs/store';

import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { GetCurrentUser, UserState } from '@core/store/user';

import { AppComponent } from './app.component';

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

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent],
providers: [provideStore([UserState]), provideHttpClient(), provideHttpClientTesting()],
}).compileComponents();

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

it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
it('should create', () => {
expect(component).toBeTruthy();
});

it(`should have the 'osf' title`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('osf');
it('should dispatch GetCurrentUser action on initialization', () => {
const store = TestBed.inject(Store);
const dispatchSpy = jest.spyOn(store, 'dispatch');
store.dispatch(GetCurrentUser);
expect(dispatchSpy).toHaveBeenCalledWith(GetCurrentUser);
});

it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, osf');
it('should render router outlet', () => {
const routerOutlet = fixture.debugElement.query(By.css('router-outlet'));
expect(routerOutlet).toBeTruthy();
});
});
38 changes: 37 additions & 1 deletion src/app/core/components/breadcrumb/breadcrumb.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,58 @@
import { MockProvider } from 'ng-mocks';

import { of } from 'rxjs';

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

import { BreadcrumbComponent } from './breadcrumb.component';

describe('BreadcrumbComponent', () => {
let component: BreadcrumbComponent;
let fixture: ComponentFixture<BreadcrumbComponent>;
let router: Router;

const mockRouter = {
url: '/test/path',
events: of(new NavigationEnd(1, '/test/path', '/test/path')),
};

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [BreadcrumbComponent],
providers: [MockProvider(Router, mockRouter)],
}).compileComponents();

router = TestBed.inject(Router);
fixture = TestBed.createComponent(BreadcrumbComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
it('should create and parse URL correctly', () => {
expect(component).toBeTruthy();
expect(component['url']()).toBe('/test/path');
expect(component['parsedUrl']()).toEqual(['test', 'path']);
});

it('should not show breadcrumb for home page', () => {
Object.defineProperty(router, 'url', { value: '/home' });
component['url'].set('/home');
fixture.detectChanges();

const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.breadcrumbs')).toBeNull();
});

it('should show breadcrumb for valid path', () => {
Object.defineProperty(router, 'url', { value: '/settings/profile' });
component['url'].set('/settings/profile');
fixture.detectChanges();

const compiled = fixture.nativeElement as HTMLElement;
const breadcrumbs = compiled.querySelector('.breadcrumbs');
expect(breadcrumbs).toBeTruthy();
expect(breadcrumbs?.textContent).toContain('settings');
expect(breadcrumbs?.textContent).toContain('profile');
});
});
7 changes: 6 additions & 1 deletion src/app/core/components/footer/footer.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { MockPipe, MockProvider } from 'ng-mocks';

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

import { FooterComponent } from './footer.component';

Expand All @@ -8,7 +12,8 @@ describe('FooterComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FooterComponent],
imports: [FooterComponent, MockPipe(TranslatePipe)],
providers: [MockProvider(TranslateService), MockProvider(ActivatedRoute)],
}).compileComponents();

fixture = TestBed.createComponent(FooterComponent);
Expand Down
1 change: 0 additions & 1 deletion src/app/core/components/footer/footer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { SocialIcon } from '@osf/shared/entities/social-icon.interface';
import { IS_PORTRAIT, IS_XSMALL } from '@shared/utils/breakpoints.tokens';

@Component({
standalone: true,
selector: 'osf-footer',
imports: [RouterLink, NgOptimizedImage, TranslateModule],
templateUrl: './footer.component.html',
Expand Down
14 changes: 9 additions & 5 deletions src/app/core/components/header/header.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { NgxsModule } from '@ngxs/store';
import { provideStore } from '@ngxs/store';

import { provideHttpClient, withFetch } from '@angular/common/http';
import { MockComponent } from 'ng-mocks';

import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideRouter } from '@angular/router';

import { UserState } from '@osf/core/store/user';

import { BreadcrumbComponent } from '../breadcrumb/breadcrumb.component';

import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
Expand All @@ -14,8 +18,8 @@ describe('HeaderComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HeaderComponent, NgxsModule.forRoot([UserState])],
providers: [provideRouter([]), provideHttpClient(withFetch())],
imports: [HeaderComponent, MockComponent(BreadcrumbComponent)],
providers: [provideStore([UserState]), provideHttpClient(), provideHttpClientTesting()],
}).compileComponents();

fixture = TestBed.createComponent(HeaderComponent);
Expand Down
1 change: 0 additions & 1 deletion src/app/core/components/header/header.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { BreadcrumbComponent } from '@core/components/breadcrumb/breadcrumb.comp
import { UserSelectors } from '@core/store/user/user.selectors';

@Component({
standalone: true,
selector: 'osf-header',
imports: [BreadcrumbComponent, MenuModule, ButtonModule, TranslatePipe],
templateUrl: './header.component.html',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { MainContentComponent } from './main-content.component';

Expand All @@ -19,4 +20,9 @@ describe('MainContentComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

it('should render router outlet', () => {
const routerOutlet = fixture.debugElement.query(By.css('router-outlet'));
expect(routerOutlet).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
standalone: true,
selector: 'osf-main-content',
imports: [RouterOutlet],
templateUrl: './main-content.component.html',
Expand Down
8 changes: 7 additions & 1 deletion src/app/core/components/nav-menu/nav-menu.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { MockPipe, MockProvider } from 'ng-mocks';

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideNoopAnimations } from '@angular/platform-browser/animations';
import { ActivatedRoute } from '@angular/router';

import { NavMenuComponent } from './nav-menu.component';

Expand All @@ -8,7 +13,8 @@ describe('NavMenuComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [NavMenuComponent],
imports: [NavMenuComponent, MockPipe(TranslatePipe)],
providers: [MockProvider(ActivatedRoute), MockProvider(TranslateService), provideNoopAnimations()],
}).compileComponents();

fixture = TestBed.createComponent(NavMenuComponent);
Expand Down
83 changes: 82 additions & 1 deletion src/app/core/components/root/root.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,50 @@
import { MockComponents, MockProvider } from 'ng-mocks';

import { ConfirmationService } from 'primeng/api';
import { ConfirmDialog } from 'primeng/confirmdialog';

import { BehaviorSubject } from 'rxjs';

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

import { BreadcrumbComponent } from '@core/components/breadcrumb/breadcrumb.component';
import { FooterComponent } from '@core/components/footer/footer.component';
import { HeaderComponent } from '@core/components/header/header.component';
import { MainContentComponent } from '@core/components/main-content/main-content.component';
import { SidenavComponent } from '@core/components/sidenav/sidenav.component';
import { TopnavComponent } from '@core/components/topnav/topnav.component';
import { IS_WEB, IS_XSMALL } from '@osf/shared/utils/breakpoints.tokens';

import { RootComponent } from './root.component';

describe('RootComponent', () => {
let component: RootComponent;
let fixture: ComponentFixture<RootComponent>;
let isWebSubject: BehaviorSubject<boolean>;
let isMobileSubject: BehaviorSubject<boolean>;

beforeEach(async () => {
isWebSubject = new BehaviorSubject<boolean>(true);
isMobileSubject = new BehaviorSubject<boolean>(false);

await TestBed.configureTestingModule({
imports: [RootComponent],
imports: [
RootComponent,
...MockComponents(
SidenavComponent,
HeaderComponent,
MainContentComponent,
FooterComponent,
TopnavComponent,
ConfirmDialog,
BreadcrumbComponent
),
],
providers: [
MockProvider(IS_WEB, isWebSubject),
MockProvider(IS_XSMALL, isMobileSubject),
MockProvider(ConfirmationService),
],
}).compileComponents();

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

it('should show desktop layout when isWeb is true', () => {
isWebSubject.next(true);
fixture.detectChanges();

const desktopLayout = fixture.nativeElement.querySelector('.layout-desktop');
const tabletLayout = fixture.nativeElement.querySelector('.layout-tablet');

expect(desktopLayout).toBeTruthy();
expect(tabletLayout).toBeFalsy();
});

it('should show tablet layout when isWeb is false', () => {
isWebSubject.next(false);
fixture.detectChanges();

const desktopLayout = fixture.nativeElement.querySelector('.layout-desktop');
const tabletLayout = fixture.nativeElement.querySelector('.layout-tablet');

expect(desktopLayout).toBeFalsy();
expect(tabletLayout).toBeTruthy();
});

it('should show breadcrumb in tablet layout when not mobile', () => {
isWebSubject.next(false);
isMobileSubject.next(false);
fixture.detectChanges();

const breadcrumb = fixture.nativeElement.querySelector('osf-breadcrumb');
expect(breadcrumb).toBeTruthy();
});

it('should hide breadcrumb in tablet layout when mobile', () => {
isWebSubject.next(false);
isMobileSubject.next(true);
fixture.detectChanges();

const breadcrumb = fixture.nativeElement.querySelector('osf-breadcrumb');
expect(breadcrumb).toBeFalsy();
});

it('should contain confirm dialog component', () => {
const confirmDialog = fixture.nativeElement.querySelector('p-confirm-dialog');
expect(confirmDialog).toBeTruthy();
});
});
1 change: 0 additions & 1 deletion src/app/core/components/root/root.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { IS_WEB, IS_XSMALL } from '@shared/utils/breakpoints.tokens';

@Component({
selector: 'osf-root',
standalone: true,
imports: [
CommonModule,
SidenavComponent,
Expand Down
6 changes: 5 additions & 1 deletion src/app/core/components/sidenav/sidenav.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { MockComponent } from 'ng-mocks';

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

import { NavMenuComponent } from '../nav-menu/nav-menu.component';

import { SidenavComponent } from './sidenav.component';

describe('SidenavDComponent', () => {
Expand All @@ -8,7 +12,7 @@ describe('SidenavDComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SidenavComponent],
imports: [SidenavComponent, MockComponent(NavMenuComponent)],
}).compileComponents();

fixture = TestBed.createComponent(SidenavComponent);
Expand Down
1 change: 0 additions & 1 deletion src/app/core/components/sidenav/sidenav.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
import { NavMenuComponent } from '@core/components/nav-menu/nav-menu.component';

@Component({
standalone: true,
selector: 'osf-sidenav',
imports: [NgOptimizedImage, NavMenuComponent],
templateUrl: './sidenav.component.html',
Expand Down
6 changes: 5 additions & 1 deletion src/app/core/components/topnav/topnav.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { MockComponent } from 'ng-mocks';

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

import { NavMenuComponent } from '../nav-menu/nav-menu.component';

import { TopnavComponent } from './topnav.component';

describe('TopnavComponent', () => {
Expand All @@ -8,7 +12,7 @@ describe('TopnavComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TopnavComponent],
imports: [TopnavComponent, MockComponent(NavMenuComponent)],
}).compileComponents();

fixture = TestBed.createComponent(TopnavComponent);
Expand Down
1 change: 0 additions & 1 deletion src/app/core/components/topnav/topnav.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { NavMenuComponent } from '@core/components/nav-menu/nav-menu.component';

@Component({
standalone: true,
selector: 'osf-topnav',
imports: [Button, Drawer, NgOptimizedImage, NavMenuComponent],
templateUrl: './topnav.component.html',
Expand Down
Loading