From d3be42a3d2024ca5fa84dd3700650a450f3cc0a9 Mon Sep 17 00:00:00 2001 From: Sergei Zaychenko Date: Fri, 8 Sep 2023 11:36:13 -0700 Subject: [PATCH] Improved test coverage for AccountSettingsComponent --- src/app/app-routing.module.ts | 4 +- src/app/app.module.ts | 4 +- ...t.html => account-settings.component.html} | 44 ++++---- ...t.sass => account-settings.component.sass} | 0 .../account-settings.component.spec.ts | 100 ++++++++++++++++++ .../settings/account-settings.component.ts | 59 +++++++++++ .../auth/settings/settings.component.spec.ts | 44 -------- src/app/auth/settings/settings.component.ts | 41 ------- 8 files changed, 187 insertions(+), 109 deletions(-) rename src/app/auth/settings/{settings.component.html => account-settings.component.html} (92%) rename src/app/auth/settings/{settings.component.sass => account-settings.component.sass} (100%) create mode 100644 src/app/auth/settings/account-settings.component.spec.ts create mode 100644 src/app/auth/settings/account-settings.component.ts delete mode 100644 src/app/auth/settings/settings.component.spec.ts delete mode 100644 src/app/auth/settings/settings.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 832da16e8..16f409b2f 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,7 +1,7 @@ import { AddPollingSourceComponent } from "./dataset-view/additional-components/metadata-component/components/add-polling-source/add-polling-source.component"; import { MetadataBlockComponent } from "./dataset-block/metadata-block/metadata-block.component"; import { AuthenticatedGuard } from "./auth/guards/authenticated.guard"; -import { SettingsComponent } from "./auth/settings/settings.component"; +import { AccountSettingsComponent } from "./auth/settings/account-settings.component"; import { PageNotFoundComponent } from "./components/page-not-found/page-not-found.component"; import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; @@ -52,7 +52,7 @@ export const routes: Routes = [ children: [ { path: `:${ProjectLinks.URL_PARAM_CATEGORY}`, - component: SettingsComponent, + component: AccountSettingsComponent, }, ], }, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4a875da37..407e0f890 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -52,7 +52,7 @@ import { SpinnerModule } from "./components/spinner/spinner.module"; import { DatasetApi } from "./api/dataset.api"; import { ErrorHandlerService } from "./services/error-handler.service"; -import { SettingsComponent } from "./auth/settings/settings.component"; +import { AccountSettingsComponent } from "./auth/settings/account-settings.component"; import { MatButtonToggleModule } from "@angular/material/button-toggle"; import { DatasetListModule } from "./components/dataset-list-component/dataset-list.module"; import { PaginationModule } from "./components/pagination-component/pagination.module"; @@ -171,7 +171,7 @@ const MatModules = [ GithubCallbackComponent, AccountComponent, NotificationIndicatorComponent, - SettingsComponent, + AccountSettingsComponent, DatasetsTabComponent, ], imports: [ diff --git a/src/app/auth/settings/settings.component.html b/src/app/auth/settings/account-settings.component.html similarity index 92% rename from src/app/auth/settings/settings.component.html rename to src/app/auth/settings/account-settings.component.html index 3b4692bd2..4b71b4138 100644 --- a/src/app/auth/settings/settings.component.html +++ b/src/app/auth/settings/account-settings.component.html @@ -1,4 +1,4 @@ -
+
- + {{ user.displayName }} settings @@ -35,13 +39,13 @@ data-item-id="settings_user_profile" data-view-component="true" [ngClass]="{ - 'action-list-item--nav__active': activeTab === settingsTabs.PROFILE + 'action-list-item--nav__active': activeTab === SettingsTabs.PROFILE }" class="action-list-item" > @@ -80,13 +84,13 @@ data-item-id="" data-view-component="true" [ngClass]="{ - 'action-list-item--nav__active': activeTab === settingsTabs.APPEARANCE + 'action-list-item--nav__active': activeTab === SettingsTabs.APPEARANCE }" class="action-list-item" > @@ -102,13 +106,13 @@ data-item-id="" data-view-component="true" [ngClass]="{ - 'action-list-item--nav__active': activeTab === settingsTabs.ACCESSIBILITY + 'action-list-item--nav__active': activeTab === SettingsTabs.ACCESSIBILITY }" class="action-list-item" > @@ -124,13 +128,13 @@ data-item-id="" data-view-component="true" [ngClass]="{ - 'action-list-item--nav__active': activeTab === settingsTabs.NOTIFICATIONS + 'action-list-item--nav__active': activeTab === SettingsTabs.NOTIFICATIONS }" class="action-list-item" > @@ -168,12 +172,12 @@ data-view-component="true" class="action-list-item" [ngClass]="{ - 'action-list-item--nav__active': activeTab === settingsTabs.BILLING + 'action-list-item--nav__active': activeTab === SettingsTabs.BILLING }" > @@ -190,12 +194,12 @@ data-view-component="true" class="action-list-item" [ngClass]="{ - 'action-list-item--nav__active': activeTab === settingsTabs.EMAILS + 'action-list-item--nav__active': activeTab === SettingsTabs.EMAILS }" > @@ -212,12 +216,12 @@ data-view-component="true" class="action-list-item" [ngClass]="{ - 'action-list-item--nav__active': activeTab === settingsTabs.SECURITY + 'action-list-item--nav__active': activeTab === SettingsTabs.SECURITY }" > @@ -234,12 +238,12 @@ data-view-component="true" class="action-list-item" [ngClass]="{ - 'action-list-item--nav__active': activeTab === settingsTabs.ORGANIZATIONS + 'action-list-item--nav__active': activeTab === SettingsTabs.ORGANIZATIONS }" > diff --git a/src/app/auth/settings/settings.component.sass b/src/app/auth/settings/account-settings.component.sass similarity index 100% rename from src/app/auth/settings/settings.component.sass rename to src/app/auth/settings/account-settings.component.sass diff --git a/src/app/auth/settings/account-settings.component.spec.ts b/src/app/auth/settings/account-settings.component.spec.ts new file mode 100644 index 000000000..b2b9228bc --- /dev/null +++ b/src/app/auth/settings/account-settings.component.spec.ts @@ -0,0 +1,100 @@ +import { mockAccountDetails } from "../../api/mock/auth.mock"; +import { RouterTestingModule } from "@angular/router/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ActivatedRoute, NavigationEnd, Router, RouterEvent } from "@angular/router"; +import { ApolloTestingModule } from "apollo-angular/testing"; +import { AccountSettingsComponent } from "./account-settings.component"; +import { AngularSvgIconModule } from "angular-svg-icon"; +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { LoggedUserService } from "../logged-user.service"; +import { findElementByDataTestId, getElementByDataTestId } from "src/app/common/base-test.helpers.spec"; +import { AuthApi } from "src/app/api/auth.api"; +import { SettingsTabs } from "./settings.constants"; +import { Subject, of } from "rxjs"; +import ProjectLinks from "src/app/project-links"; + +describe("AccountSettingsComponent", () => { + let component: AccountSettingsComponent; + let fixture: ComponentFixture; + let loggedUserService: LoggedUserService; + let authApi: AuthApi; + let activatedRoute: ActivatedRoute; + let router: Router; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AccountSettingsComponent], + imports: [ + ApolloTestingModule, + RouterTestingModule, + AngularSvgIconModule.forRoot(), + HttpClientTestingModule, + ], + }).compileComponents(); + + activatedRoute = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + + authApi = TestBed.inject(AuthApi); + spyOn(authApi, "accountChanged").and.returnValue(of(mockAccountDetails)); + loggedUserService = TestBed.inject(LoggedUserService); + + fixture = TestBed.createComponent(AccountSettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + enum Elements { + UserNameLink = "user-name-link", + } + + it("should create", () => { + expect(component).toBeTruthy(); + }); + + it("should see logged user", () => { + const userNameLinKElement: HTMLElement = getElementByDataTestId(fixture, Elements.UserNameLink); + expect(userNameLinKElement.innerText).toEqual(mockAccountDetails.displayName); + }); + + it("should not show user data for logged off case", () => { + loggedUserService.logout(); + fixture.detectChanges(); + + expect(findElementByDataTestId(fixture, Elements.UserNameLink)).toBeFalsy(); + }); + + it("should open profile tab by default", () => { + expect(component.activeTab).toEqual(SettingsTabs.PROFILE); + }); + + [ + SettingsTabs.ACCESSIBILITY, + SettingsTabs.ACCOUNT, + SettingsTabs.APPEARANCE, + SettingsTabs.BILLING, + SettingsTabs.EMAILS, + SettingsTabs.NOTIFICATIONS, + SettingsTabs.ORGANIZATIONS, + SettingsTabs.PROFILE, + SettingsTabs.SECURITY, + ].forEach((tab: SettingsTabs) => { + it(`should activate ${tab} tab`, () => { + activatedRoute.snapshot.params = { + [ProjectLinks.URL_PARAM_CATEGORY]: tab, + }; + (router.events as Subject).next(new NavigationEnd(1, "", "")); + + expect(component.activeTab).toEqual(tab); + }); + }); + + it("should open profile tab for a wrong tab", () => { + activatedRoute.snapshot.params = { + [ProjectLinks.URL_PARAM_CATEGORY]: "wrong", + }; + (router.events as Subject).next(new NavigationEnd(1, "", "")); + + expect(component.activeTab).toEqual(SettingsTabs.PROFILE); + }); +}); diff --git a/src/app/auth/settings/account-settings.component.ts b/src/app/auth/settings/account-settings.component.ts new file mode 100644 index 000000000..9ec473f2f --- /dev/null +++ b/src/app/auth/settings/account-settings.component.ts @@ -0,0 +1,59 @@ +import ProjectLinks from "src/app/project-links"; +import { AccountDetailsFragment } from "src/app/api/kamu.graphql.interface"; +import { SettingsTabs } from "./settings.constants"; +import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; +import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; +import { filter } from "rxjs/operators"; +import { BaseComponent } from "src/app/common/base.component"; +import AppValues from "src/app/common/app.values"; +import { LoggedUserService } from "../logged-user.service"; +import { MaybeNull, MaybeUndefined } from "src/app/common/app.types"; +import { Observable } from "rxjs"; + +@Component({ + selector: "app-settings", + templateUrl: "./account-settings.component.html", + styleUrls: ["./account-settings.component.sass"], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AccountSettingsComponent extends BaseComponent implements OnInit { + public readonly DEFAULT_AVATAR_URL = AppValues.DEFAULT_AVATAR_URL; + public readonly SettingsTabs: typeof SettingsTabs = SettingsTabs; + + public activeTab: SettingsTabs = SettingsTabs.PROFILE; + public user$: Observable>; + + constructor(private router: Router, private route: ActivatedRoute, private loggedUserService: LoggedUserService) { + super(); + } + + public ngOnInit(): void { + this.trackSubscription( + this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { + this.extractActiveTabFromRoute(); + }), + ); + this.extractActiveTabFromRoute(); + this.user$ = this.loggedUserService.onLoggedInUserChanges; + } + + public getRouteLink(tab: SettingsTabs): string { + return `/${ProjectLinks.URL_SETTINGS}/${tab}`; + } + + private extractActiveTabFromRoute(): void { + const categoryParam: MaybeUndefined = this.route.snapshot.params[ + ProjectLinks.URL_PARAM_CATEGORY + ] as MaybeUndefined; + + if (categoryParam) { + const category = categoryParam as SettingsTabs; + if (Object.values(SettingsTabs).includes(category)) { + this.activeTab = category; + return; + } + } + + this.activeTab = SettingsTabs.PROFILE; + } +} diff --git a/src/app/auth/settings/settings.component.spec.ts b/src/app/auth/settings/settings.component.spec.ts deleted file mode 100644 index e7ba24238..000000000 --- a/src/app/auth/settings/settings.component.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { mockAccountDetails } from "./../../api/mock/auth.mock"; -import { RouterTestingModule } from "@angular/router/testing"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { ActivatedRoute } from "@angular/router"; -import { ApolloTestingModule } from "apollo-angular/testing"; -import { SettingsComponent } from "./settings.component"; -import { AngularSvgIconModule } from "angular-svg-icon"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; - -describe("SettingsComponent", () => { - let component: SettingsComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [SettingsComponent], - imports: [ - ApolloTestingModule, - RouterTestingModule, - AngularSvgIconModule.forRoot(), - HttpClientTestingModule, - ], - providers: [ - { - provide: ActivatedRoute, - useValue: { - snapshot: { - params: { category: "" }, - }, - }, - }, - ], - }).compileComponents(); - - fixture = TestBed.createComponent(SettingsComponent); - component = fixture.componentInstance; - component.user = mockAccountDetails; - fixture.detectChanges(); - }); - - it("should create", () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/auth/settings/settings.component.ts b/src/app/auth/settings/settings.component.ts deleted file mode 100644 index 318e6e253..000000000 --- a/src/app/auth/settings/settings.component.ts +++ /dev/null @@ -1,41 +0,0 @@ -import ProjectLinks from "src/app/project-links"; -import { AccountDetailsFragment } from "src/app/api/kamu.graphql.interface"; -import { SettingsTabs } from "./settings.constants"; -import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; -import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; -import { filter } from "rxjs/operators"; -import { BaseComponent } from "src/app/common/base.component"; -import AppValues from "src/app/common/app.values"; -import { LoggedUserService } from "../logged-user.service"; - -@Component({ - selector: "app-settings", - templateUrl: "./settings.component.html", - styleUrls: ["./settings.component.sass"], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class SettingsComponent extends BaseComponent implements OnInit { - public readonly DEFAULT_AVATAR_URL = AppValues.DEFAULT_AVATAR_URL; - - public activeTab: string = SettingsTabs.PROFILE; - public settingsTabs: typeof SettingsTabs = SettingsTabs; - public user: AccountDetailsFragment; - - constructor(private router: Router, private route: ActivatedRoute, private loggedUserService: LoggedUserService) { - super(); - } - - ngOnInit(): void { - this.trackSubscription( - this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { - this.activeTab = this.route.snapshot.params[ProjectLinks.URL_PARAM_CATEGORY] as string; - }), - ); - this.activeTab = this.route.snapshot.params[ProjectLinks.URL_PARAM_CATEGORY] as string; - if (this.loggedUserService.currentlyLoggedInUser) this.user = this.loggedUserService.currentlyLoggedInUser; - } - - public getRouteLink(tab: SettingsTabs): string { - return `/${ProjectLinks.URL_SETTINGS}/${tab}`; - } -}