From e8cde0d8747b8051c29958974eb7fa352976a8a4 Mon Sep 17 00:00:00 2001 From: Masayuki Date: Sat, 19 Oct 2024 00:58:02 +0900 Subject: [PATCH 1/2] fix sonarlint issues --- src/app/app.component.ts | 39 ++++---- .../item-selector/item-selector.component.ts | 26 ++--- .../add-customer/add-customer.component.ts | 2 +- .../associate-item.component.ts | 44 +++++---- .../customer-detail.component.ts | 36 +++---- .../customer-list/customer-list.component.ts | 49 +++++----- .../order-sheet/order-sheet.component.ts | 61 ++++++------ src/app/item/add-item/add-item.component.ts | 58 ++++++----- .../item/item-detail/item-detail.component.ts | 62 ++++++------ src/app/item/item-list/item-list.component.ts | 21 ++-- .../login-by-email.component.ts | 8 +- .../login-by-phone.component.ts | 4 +- src/app/login/login.component.ts | 41 ++++---- src/app/login/login.service.ts | 2 +- .../order/new-order/new-order.component.ts | 96 ++++++++++--------- .../order-detail/order-detail.component.ts | 46 ++++----- src/app/services/firestoreBase.ts | 12 ++- src/app/services/item.service.ts | 9 +- .../add-storage/add-storage.component.ts | 59 +++++++----- .../storage-list/storage-list.component.ts | 14 +-- 20 files changed, 363 insertions(+), 326 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bb7b577..cca0a17 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -10,29 +10,32 @@ import { MatIcon } from '@angular/material/icon'; import { MatToolbar } from '@angular/material/toolbar'; @Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'], - standalone: true, - imports: [ - MatToolbar, - MatIcon, - NgIf, - MatIconButton, - MatSidenavContainer, - MatSidenav, - MatNavList, - MatListItem, - RouterLink, - MatSidenavContent, - RouterOutlet, - ], + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], + standalone: true, + imports: [ + MatToolbar, + MatIcon, + NgIf, + MatIconButton, + MatSidenavContainer, + MatSidenav, + MatNavList, + MatListItem, + RouterLink, + MatSidenavContent, + RouterOutlet, + ], }) export class AppComponent { user: User | null = null; siteName = environment.siteName; - constructor(private auth: Auth, private router: Router) { + constructor( + private readonly auth: Auth, + private readonly router: Router, + ) { onAuthStateChanged(this.auth, (user) => { this.user = user; }); diff --git a/src/app/components/item-selector/item-selector.component.ts b/src/app/components/item-selector/item-selector.component.ts index fcdba53..3d28cff 100644 --- a/src/app/components/item-selector/item-selector.component.ts +++ b/src/app/components/item-selector/item-selector.component.ts @@ -1,25 +1,17 @@ +import { AsyncPipe, NgClass, NgFor, NgIf } from '@angular/common'; import { Component, EventEmitter, Output } from '@angular/core'; +import { MatGridList, MatGridTile } from '@angular/material/grid-list'; +import { MatIcon } from '@angular/material/icon'; import { Observable } from 'rxjs'; import { ItemService } from 'src/app/services/item.service'; import { Item } from 'src/models/item.model'; -import { MatIcon } from '@angular/material/icon'; -import { MatGridList, MatGridTile } from '@angular/material/grid-list'; -import { NgIf, NgFor, NgClass, AsyncPipe } from '@angular/common'; @Component({ - selector: 'app-item-selector', - templateUrl: './item-selector.component.html', - styleUrls: ['./item-selector.component.scss'], - standalone: true, - imports: [ - NgIf, - MatGridList, - NgFor, - MatGridTile, - NgClass, - MatIcon, - AsyncPipe, - ], + selector: 'app-item-selector', + templateUrl: './item-selector.component.html', + styleUrls: ['./item-selector.component.scss'], + standalone: true, + imports: [NgIf, MatGridList, NgFor, MatGridTile, NgClass, MatIcon, AsyncPipe], }) export class ItemSelectorComponent { columns = Math.floor(window.innerWidth / 170).toString(); @@ -27,7 +19,7 @@ export class ItemSelectorComponent { @Output() choose = new EventEmitter(); - constructor(private is: ItemService) { + constructor(private readonly is: ItemService) { this.items = this.is.list(); } diff --git a/src/app/customer/add-customer/add-customer.component.ts b/src/app/customer/add-customer/add-customer.component.ts index cf67740..b5f69e0 100644 --- a/src/app/customer/add-customer/add-customer.component.ts +++ b/src/app/customer/add-customer/add-customer.component.ts @@ -35,7 +35,7 @@ import { CustomerDialogData } from 'src/models/customer.model'; export class AddCustomerComponent { constructor( @Inject(MAT_DIALOG_DATA) public data: CustomerDialogData, - private cs: CustomerService, + private readonly cs: CustomerService, ) { if (typeof data.customer.id === 'undefined') { data.customer = { diff --git a/src/app/customer/associate-item/associate-item.component.ts b/src/app/customer/associate-item/associate-item.component.ts index 1be6d49..7340aaf 100644 --- a/src/app/customer/associate-item/associate-item.component.ts +++ b/src/app/customer/associate-item/associate-item.component.ts @@ -1,29 +1,35 @@ +import { CdkScrollable } from '@angular/cdk/scrolling'; import { Component } from '@angular/core'; -import { MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog'; -import { Item } from 'src/models/item.model'; -import { MatIcon } from '@angular/material/icon'; import { MatButton } from '@angular/material/button'; +import { + MatDialogActions, + MatDialogClose, + MatDialogContent, + MatDialogRef, + MatDialogTitle, +} from '@angular/material/dialog'; +import { MatIcon } from '@angular/material/icon'; +import { Item } from 'src/models/item.model'; import { ItemSelectorComponent } from '../../components/item-selector/item-selector.component'; -import { CdkScrollable } from '@angular/cdk/scrolling'; @Component({ - selector: 'app-associate-item', - templateUrl: './associate-item.component.html', - styleUrls: ['./associate-item.component.scss'], - standalone: true, - imports: [ - MatDialogTitle, - CdkScrollable, - MatDialogContent, - ItemSelectorComponent, - MatDialogActions, - MatButton, - MatDialogClose, - MatIcon, - ], + selector: 'app-associate-item', + templateUrl: './associate-item.component.html', + styleUrls: ['./associate-item.component.scss'], + standalone: true, + imports: [ + MatDialogTitle, + CdkScrollable, + MatDialogContent, + ItemSelectorComponent, + MatDialogActions, + MatButton, + MatDialogClose, + MatIcon, + ], }) export class AssociateItemComponent { - constructor(private ref: MatDialogRef) {} + constructor(private readonly ref: MatDialogRef) {} associateItem(item: Item) { this.ref.close(item); diff --git a/src/app/customer/customer-detail/customer-detail.component.ts b/src/app/customer/customer-detail/customer-detail.component.ts index d6456fa..8fc352d 100644 --- a/src/app/customer/customer-detail/customer-detail.component.ts +++ b/src/app/customer/customer-detail/customer-detail.component.ts @@ -1,6 +1,17 @@ +import { NgFor } from '@angular/common'; import { Component, OnDestroy } from '@angular/core'; import { where } from '@angular/fire/firestore'; +import { MatAnchor, MatButton, MatIconButton } from '@angular/material/button'; +import { + MatCard, + MatCardActions, + MatCardContent, + MatCardSubtitle, + MatCardTitle, +} from '@angular/material/card'; import { MatDialog } from '@angular/material/dialog'; +import { MatIcon } from '@angular/material/icon'; +import { MatList, MatListItem } from '@angular/material/list'; import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { take } from 'rxjs'; @@ -11,17 +22,6 @@ import { Customer, CustomerDialogData } from 'src/models/customer.model'; import { Item } from 'src/models/item.model'; import { AddCustomerComponent } from '../add-customer/add-customer.component'; import { AssociateItemComponent } from '../associate-item/associate-item.component'; -import { NgFor } from '@angular/common'; -import { MatList, MatListItem } from '@angular/material/list'; -import { MatIcon } from '@angular/material/icon'; -import { MatButton, MatIconButton, MatAnchor } from '@angular/material/button'; -import { - MatCard, - MatCardTitle, - MatCardSubtitle, - MatCardActions, - MatCardContent, -} from '@angular/material/card'; @Component({ selector: 'app-customer-detail', @@ -50,13 +50,13 @@ export class CustomerDetailComponent implements OnDestroy { private isUpdated = false; constructor( - private cs: CustomerService, - private dialog: MatDialog, - private is: ItemService, - private route: ActivatedRoute, - private router: Router, - private snack: MatSnackBar, - private title: TitleService, + private readonly cs: CustomerService, + private readonly dialog: MatDialog, + private readonly is: ItemService, + private readonly route: ActivatedRoute, + private readonly router: Router, + private readonly snack: MatSnackBar, + private readonly title: TitleService, ) { this.cs .load(this.route.snapshot.paramMap.get('id') ?? '_') diff --git a/src/app/customer/customer-list/customer-list.component.ts b/src/app/customer/customer-list/customer-list.component.ts index bc587c1..b42a468 100644 --- a/src/app/customer/customer-list/customer-list.component.ts +++ b/src/app/customer/customer-list/customer-list.component.ts @@ -1,37 +1,40 @@ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component } from '@angular/core'; +import { MatFabButton } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; +import { MatIcon } from '@angular/material/icon'; +import { MatListItem, MatNavList } from '@angular/material/list'; +import { MatTooltip } from '@angular/material/tooltip'; +import { RouterLink } from '@angular/router'; import { Observable } from 'rxjs'; import { CustomerService } from 'src/app/services/customer.service'; import { Customer, CustomerDialogData } from 'src/models/customer.model'; import { AddCustomerComponent } from '../add-customer/add-customer.component'; -import { MatTooltip } from '@angular/material/tooltip'; -import { MatFabButton } from '@angular/material/button'; -import { MatIcon } from '@angular/material/icon'; -import { RouterLink } from '@angular/router'; -import { NgFor, NgIf, AsyncPipe } from '@angular/common'; -import { MatNavList, MatListItem } from '@angular/material/list'; @Component({ - selector: 'app-customer-list', - templateUrl: './customer-list.component.html', - styleUrls: ['./customer-list.component.scss'], - standalone: true, - imports: [ - MatNavList, - NgFor, - MatListItem, - RouterLink, - NgIf, - MatIcon, - MatFabButton, - MatTooltip, - AsyncPipe, - ], + selector: 'app-customer-list', + templateUrl: './customer-list.component.html', + styleUrls: ['./customer-list.component.scss'], + standalone: true, + imports: [ + MatNavList, + NgFor, + MatListItem, + RouterLink, + NgIf, + MatIcon, + MatFabButton, + MatTooltip, + AsyncPipe, + ], }) export class CustomerListComponent { customers: Observable; - constructor(private cs: CustomerService, private dialog: MatDialog) { + constructor( + private readonly cs: CustomerService, + private readonly dialog: MatDialog, + ) { this.customers = this.cs.list(); } @@ -44,7 +47,7 @@ export class CustomerListComponent { type: '追加', customer: {}, }, - } + }, ) .afterClosed() .subscribe((customer) => { diff --git a/src/app/customer/order-sheet/order-sheet.component.ts b/src/app/customer/order-sheet/order-sheet.component.ts index 8f29779..3692a0a 100644 --- a/src/app/customer/order-sheet/order-sheet.component.ts +++ b/src/app/customer/order-sheet/order-sheet.component.ts @@ -1,5 +1,15 @@ +import { CurrencyPipe, NgFor } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { where } from '@angular/fire/firestore'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatAnchor, MatButton } from '@angular/material/button'; +import { + MatCard, + MatCardActions, + MatCardContent, + MatCardTitle, +} from '@angular/material/card'; +import { MatIcon } from '@angular/material/icon'; import { ActivatedRoute, RouterLink } from '@angular/router'; import { take } from 'rxjs'; import { CustomerService } from 'src/app/services/customer.service'; @@ -7,31 +17,26 @@ import { ItemService } from 'src/app/services/item.service'; import { TitleService } from 'src/app/services/title.service'; import { Customer } from 'src/models/customer.model'; import { Item } from 'src/models/item.model'; -import { ReactiveFormsModule, FormsModule } from '@angular/forms'; -import { NgFor, CurrencyPipe } from '@angular/common'; -import { MatIcon } from '@angular/material/icon'; -import { MatAnchor, MatButton } from '@angular/material/button'; -import { MatCard, MatCardTitle, MatCardContent, MatCardActions } from '@angular/material/card'; @Component({ - selector: 'app-order-sheet', - templateUrl: './order-sheet.component.html', - styleUrls: ['./order-sheet.component.scss'], - standalone: true, - imports: [ - MatCard, - MatCardTitle, - MatCardContent, - MatCardActions, - MatAnchor, - RouterLink, - MatIcon, - MatButton, - NgFor, - ReactiveFormsModule, - FormsModule, - CurrencyPipe, - ], + selector: 'app-order-sheet', + templateUrl: './order-sheet.component.html', + styleUrls: ['./order-sheet.component.scss'], + standalone: true, + imports: [ + MatCard, + MatCardTitle, + MatCardContent, + MatCardActions, + MatAnchor, + RouterLink, + MatIcon, + MatButton, + NgFor, + ReactiveFormsModule, + FormsModule, + CurrencyPipe, + ], }) export class OrderSheetComponent implements OnInit { customer: Customer | undefined; @@ -43,13 +48,13 @@ export class OrderSheetComponent implements OnInit { isNotReady = true; constructor( - private route: ActivatedRoute, - private cs: CustomerService, - private is: ItemService, - private title: TitleService + private readonly route: ActivatedRoute, + private readonly cs: CustomerService, + private readonly is: ItemService, + private readonly title: TitleService, ) { this.cs - .load(this.route.snapshot.paramMap.get('id') || '_') + .load(this.route.snapshot.paramMap.get('id') ?? '_') .pipe(take(1)) .subscribe((customer) => { if (!customer) return; diff --git a/src/app/item/add-item/add-item.component.ts b/src/app/item/add-item/add-item.component.ts index 08ba9e7..94943ac 100644 --- a/src/app/item/add-item/add-item.component.ts +++ b/src/app/item/add-item/add-item.component.ts @@ -1,40 +1,46 @@ +import { CdkScrollable } from '@angular/cdk/scrolling'; import { Component, Inject } from '@angular/core'; -import { UntypedFormBuilder, ReactiveFormsModule } from '@angular/forms'; -import { MAT_DIALOG_DATA, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog'; -import { ItemService } from 'src/app/services/item.service'; -import { ItemDialogData } from 'src/models/item.model'; -import { MatIcon } from '@angular/material/icon'; +import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms'; import { MatButton } from '@angular/material/button'; -import { MatInput } from '@angular/material/input'; +import { + MAT_DIALOG_DATA, + MatDialogActions, + MatDialogClose, + MatDialogContent, + MatDialogTitle, +} from '@angular/material/dialog'; import { MatFormField, MatLabel } from '@angular/material/form-field'; -import { CdkScrollable } from '@angular/cdk/scrolling'; +import { MatIcon } from '@angular/material/icon'; +import { MatInput } from '@angular/material/input'; +import { ItemService } from 'src/app/services/item.service'; +import { ItemDialogData } from 'src/models/item.model'; @Component({ - selector: 'app-add-item', - templateUrl: './add-item.component.html', - styleUrls: ['./add-item.component.scss'], - standalone: true, - imports: [ - MatDialogTitle, - ReactiveFormsModule, - CdkScrollable, - MatDialogContent, - MatFormField, - MatLabel, - MatInput, - MatDialogActions, - MatButton, - MatDialogClose, - MatIcon, - ], + selector: 'app-add-item', + templateUrl: './add-item.component.html', + styleUrls: ['./add-item.component.scss'], + standalone: true, + imports: [ + MatDialogTitle, + ReactiveFormsModule, + CdkScrollable, + MatDialogContent, + MatFormField, + MatLabel, + MatInput, + MatDialogActions, + MatButton, + MatDialogClose, + MatIcon, + ], }) export class AddItemComponent { item; constructor( @Inject(MAT_DIALOG_DATA) public data: ItemDialogData, - private is: ItemService, - private fb: UntypedFormBuilder + private readonly is: ItemService, + private readonly fb: UntypedFormBuilder, ) { this.item = this.fb.group({ id: [this.is.id], diff --git a/src/app/item/item-detail/item-detail.component.ts b/src/app/item/item-detail/item-detail.component.ts index 060c2a5..81abd40 100644 --- a/src/app/item/item-detail/item-detail.component.ts +++ b/src/app/item/item-detail/item-detail.component.ts @@ -23,26 +23,26 @@ import { MatButton, MatAnchor } from '@angular/material/button'; import { MatCard, MatCardTitle, MatCardContent, MatCardActions, MatCardSubtitle } from '@angular/material/card'; @Component({ - selector: 'app-item-detail', - templateUrl: './item-detail.component.html', - styleUrls: ['./item-detail.component.scss'], - standalone: true, - imports: [ - MatCard, - MatCardTitle, - MatCardContent, - MatCardActions, - MatButton, - MatIcon, - MatCardSubtitle, - ReactiveFormsModule, - NgFor, - MatFormField, - MatLabel, - MatInput, - MatAnchor, - RouterLink, - ], + selector: 'app-item-detail', + templateUrl: './item-detail.component.html', + styleUrls: ['./item-detail.component.scss'], + standalone: true, + imports: [ + MatCard, + MatCardTitle, + MatCardContent, + MatCardActions, + MatButton, + MatIcon, + MatCardSubtitle, + ReactiveFormsModule, + NgFor, + MatFormField, + MatLabel, + MatInput, + MatAnchor, + RouterLink, + ], }) export class ItemDetailComponent implements OnDestroy { item: ItemWithoutTimestamp = { @@ -59,14 +59,14 @@ export class ItemDetailComponent implements OnDestroy { storages: Storage[] = []; constructor( - private dialog: MatDialog, - private fb: UntypedFormBuilder, - private is: ItemService, - private route: ActivatedRoute, - private router: Router, - private sb: MatSnackBar, - private ss: StorageService, - private title: TitleService + private readonly dialog: MatDialog, + private readonly fb: UntypedFormBuilder, + private readonly is: ItemService, + private readonly route: ActivatedRoute, + private readonly router: Router, + private readonly sb: MatSnackBar, + private readonly ss: StorageService, + private readonly title: TitleService, ) { const id = this.route.snapshot.paramMap.get('id') ?? '_'; this.is @@ -85,8 +85,8 @@ export class ItemDetailComponent implements OnDestroy { this.storages = storages; this.storedCount = this.fb.group( Object.fromEntries( - storages.map((s) => [s.id, [item.storedCount[s.id] || 0]]) - ) + storages.map((s) => [s.id, [item.storedCount[s.id] || 0]]), + ), ); }); }); @@ -112,7 +112,7 @@ export class ItemDetailComponent implements OnDestroy { this.item.storedCount = this.storedCount.value; this.item.total = Object.values(this.item.storedCount).reduce( (acc, cur) => acc + cur, - 0 + 0, ); } diff --git a/src/app/item/item-list/item-list.component.ts b/src/app/item/item-list/item-list.component.ts index 583c2ac..226be0f 100644 --- a/src/app/item/item-list/item-list.component.ts +++ b/src/app/item/item-list/item-list.component.ts @@ -10,22 +10,17 @@ import { MatFabButton } from '@angular/material/button'; import { ItemSelectorComponent } from '../../components/item-selector/item-selector.component'; @Component({ - selector: 'app-item-list', - templateUrl: './item-list.component.html', - styleUrls: ['./item-list.component.scss'], - standalone: true, - imports: [ - ItemSelectorComponent, - MatFabButton, - MatTooltip, - MatIcon, - ], + selector: 'app-item-list', + templateUrl: './item-list.component.html', + styleUrls: ['./item-list.component.scss'], + standalone: true, + imports: [ItemSelectorComponent, MatFabButton, MatTooltip, MatIcon], }) export class ItemListComponent { constructor( - private dialog: MatDialog, - private router: Router, - private is: ItemService + private readonly dialog: MatDialog, + private readonly router: Router, + private readonly is: ItemService, ) {} addItem() { diff --git a/src/app/login/login-by-email/login-by-email.component.ts b/src/app/login/login-by-email/login-by-email.component.ts index 552fa09..2c8d1f8 100644 --- a/src/app/login/login-by-email/login-by-email.component.ts +++ b/src/app/login/login-by-email/login-by-email.component.ts @@ -30,10 +30,10 @@ export class LoginByEmailComponent { credentials; constructor( - private fb: UntypedFormBuilder, - private snack: MatSnackBar, - private router: Router, - private loginSv: LoginService, + private readonly fb: UntypedFormBuilder, + private readonly snack: MatSnackBar, + private readonly router: Router, + private readonly loginSv: LoginService, ) { this.credentials = this.fb.group({ email: [''], diff --git a/src/app/login/login-by-phone/login-by-phone.component.ts b/src/app/login/login-by-phone/login-by-phone.component.ts index 77dec99..069a27e 100644 --- a/src/app/login/login-by-phone/login-by-phone.component.ts +++ b/src/app/login/login-by-phone/login-by-phone.component.ts @@ -34,8 +34,8 @@ export class LoginByPhoneComponent { private authResult?: ConfirmationResult; constructor( - private login: LoginService, - private sb: MatSnackBar, + private readonly login: LoginService, + private readonly sb: MatSnackBar, ) {} async sendConfirmation() { diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index fda2013..394b448 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -1,37 +1,42 @@ import { AfterViewInit, Component, ViewChild } from '@angular/core'; -import { MatCard, MatCardContent, MatCardHeader, MatCardTitle } from '@angular/material/card'; +import { + MatCard, + MatCardContent, + MatCardHeader, + MatCardTitle, +} from '@angular/material/card'; import { MatTab, MatTabGroup } from '@angular/material/tabs'; import { LocalStorageService } from 'ngx-webstorage'; import { LoginByEmailComponent } from './login-by-email/login-by-email.component'; import { LoginByPhoneComponent } from './login-by-phone/login-by-phone.component'; @Component({ - selector: 'app-login', - templateUrl: './login.component.html', - styleUrls: ['./login.component.scss'], - standalone: true, - imports: [ - MatCard, - MatCardTitle, - MatCardHeader, - MatCardContent, - MatTabGroup, - MatTab, - LoginByPhoneComponent, - LoginByEmailComponent, - ], + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'], + standalone: true, + imports: [ + MatCard, + MatCardTitle, + MatCardHeader, + MatCardContent, + MatTabGroup, + MatTab, + LoginByPhoneComponent, + LoginByEmailComponent, + ], }) export class LoginComponent implements AfterViewInit { - private lastLoginMethodIndexKey = 'login.lastLoginMethodIndex'; + private readonly lastLoginMethodIndexKey = 'login.lastLoginMethodIndex'; @ViewChild('tabGroup') tabGroup?: MatTabGroup; - constructor(private storage: LocalStorageService) {} + constructor(private readonly storage: LocalStorageService) {} ngAfterViewInit(): void { if (!this.tabGroup) return; const index = Number( - this.storage.retrieve(this.lastLoginMethodIndexKey) ?? 0 + this.storage.retrieve(this.lastLoginMethodIndexKey) ?? 0, ); this.tabGroup.selectedIndex = index; } diff --git a/src/app/login/login.service.ts b/src/app/login/login.service.ts index 1aada2c..db27ddd 100644 --- a/src/app/login/login.service.ts +++ b/src/app/login/login.service.ts @@ -16,7 +16,7 @@ import { providedIn: 'root', }) export class LoginService { - constructor(private auth: Auth) {} + constructor(private readonly auth: Auth) {} async createAccountByEmail( email: string, diff --git a/src/app/order/new-order/new-order.component.ts b/src/app/order/new-order/new-order.component.ts index 1a3706e..b115d1a 100644 --- a/src/app/order/new-order/new-order.component.ts +++ b/src/app/order/new-order/new-order.component.ts @@ -1,10 +1,26 @@ +import { NgFor, NgIf } from '@angular/common'; import { Component } from '@angular/core'; import { serverTimestamp, where, WithFieldValue, } from '@angular/fire/firestore'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { + MatCard, + MatCardActions, + MatCardContent, + MatCardSubtitle, + MatCardTitle, +} from '@angular/material/card'; +import { MatDivider } from '@angular/material/divider'; +import { MatFormField, MatLabel } from '@angular/material/form-field'; +import { MatIcon } from '@angular/material/icon'; +import { MatInput } from '@angular/material/input'; +import { MatList, MatListItem } from '@angular/material/list'; import { MatSnackBar } from '@angular/material/snack-bar'; +import { MatTooltip } from '@angular/material/tooltip'; import { ActivatedRoute } from '@angular/router'; import { CustomerService } from 'src/app/services/customer.service'; import { ItemService } from 'src/app/services/item.service'; @@ -12,42 +28,32 @@ import { OrderService } from 'src/app/services/order.service'; import { Customer } from 'src/models/customer.model'; import { Item } from 'src/models/item.model'; import { Order } from 'src/models/order.model'; -import { MatIcon } from '@angular/material/icon'; -import { MatButton } from '@angular/material/button'; -import { MatDivider } from '@angular/material/divider'; -import { MatTooltip } from '@angular/material/tooltip'; -import { ReactiveFormsModule, FormsModule } from '@angular/forms'; -import { MatInput } from '@angular/material/input'; -import { MatFormField, MatLabel } from '@angular/material/form-field'; -import { NgFor, NgIf } from '@angular/common'; -import { MatList, MatListItem } from '@angular/material/list'; -import { MatCard, MatCardTitle, MatCardSubtitle, MatCardContent, MatCardActions } from '@angular/material/card'; @Component({ - selector: 'app-new-order', - templateUrl: './new-order.component.html', - styleUrls: ['./new-order.component.scss'], - standalone: true, - imports: [ - MatCard, - MatCardTitle, - MatCardSubtitle, - MatCardContent, - MatList, - NgFor, - MatListItem, - NgIf, - MatFormField, - MatLabel, - MatInput, - ReactiveFormsModule, - FormsModule, - MatTooltip, - MatDivider, - MatButton, - MatIcon, - MatCardActions, - ], + selector: 'app-new-order', + templateUrl: './new-order.component.html', + styleUrls: ['./new-order.component.scss'], + standalone: true, + imports: [ + MatCard, + MatCardTitle, + MatCardSubtitle, + MatCardContent, + MatList, + NgFor, + MatListItem, + NgIf, + MatFormField, + MatLabel, + MatInput, + ReactiveFormsModule, + FormsModule, + MatTooltip, + MatDivider, + MatButton, + MatIcon, + MatCardActions, + ], }) export class NewOrderComponent { customer: Customer | undefined; @@ -63,14 +69,14 @@ export class NewOrderComponent { sending = false; constructor( - private route: ActivatedRoute, - private cs: CustomerService, - private is: ItemService, - private os: OrderService, - private sb: MatSnackBar + private readonly route: ActivatedRoute, + private readonly cs: CustomerService, + private readonly is: ItemService, + private readonly os: OrderService, + private readonly sb: MatSnackBar, ) { this.cs - .load(this.route.snapshot.paramMap.get('id') || '_') + .load(this.route.snapshot.paramMap.get('id') ?? '_') .subscribe((customer) => { this.customer = customer; @@ -98,18 +104,18 @@ export class NewOrderComponent { this.order.orderedAt = serverTimestamp(); this.order.items = this.items .map((item) => ({ - id: item.id || '', - name: item.name || '', - orderedCount: item.orderedCount || 0, + id: item.id ?? '', + name: item.name ?? '', + orderedCount: item.orderedCount ?? 0, })) .filter((item) => item.orderedCount); - this.os.store(Object.assign({ id: this.os.id }, this.order)).then( + this.os.store({ id: this.os.id, ...this.order }).then( () => { this.sb.open('注文を送信しました'); }, () => { this.sending = false; - } + }, ); } } diff --git a/src/app/order/order-detail/order-detail.component.ts b/src/app/order/order-detail/order-detail.component.ts index aed0b1f..fd28056 100644 --- a/src/app/order/order-detail/order-detail.component.ts +++ b/src/app/order/order-detail/order-detail.component.ts @@ -13,35 +13,35 @@ import { MatNavList, MatListItem } from '@angular/material/list'; import { MatCard, MatCardTitle, MatCardSubtitle, MatCardContent, MatCardActions } from '@angular/material/card'; @Component({ - selector: 'app-order-detail', - templateUrl: './order-detail.component.html', - styleUrls: ['./order-detail.component.scss'], - standalone: true, - imports: [ - MatCard, - MatCardTitle, - MatCardSubtitle, - MatCardContent, - MatNavList, - NgFor, - MatListItem, - RouterLink, - NgIf, - MatCardActions, - MatButton, - MatIcon, - DatePipe, - ], + selector: 'app-order-detail', + templateUrl: './order-detail.component.html', + styleUrls: ['./order-detail.component.scss'], + standalone: true, + imports: [ + MatCard, + MatCardTitle, + MatCardSubtitle, + MatCardContent, + MatNavList, + NgFor, + MatListItem, + RouterLink, + NgIf, + MatCardActions, + MatButton, + MatIcon, + DatePipe, + ], }) export class OrderDetailComponent { order: Order | undefined; itemList: { [key: string]: Item | undefined } = {}; constructor( - private os: OrderService, - private is: ItemService, - private route: ActivatedRoute, - private router: Router + private readonly os: OrderService, + private readonly is: ItemService, + private readonly route: ActivatedRoute, + private readonly router: Router, ) { this.os .load(this.route.snapshot.paramMap.get('id') ?? '_') diff --git a/src/app/services/firestoreBase.ts b/src/app/services/firestoreBase.ts index f587021..6930dc9 100644 --- a/src/app/services/firestoreBase.ts +++ b/src/app/services/firestoreBase.ts @@ -18,13 +18,19 @@ import { import { Observable } from 'rxjs'; export class FirestoreBase { - constructor(protected db: Firestore, protected path: string) {} + constructor( + protected db: Firestore, + protected path: string, + ) {} - private col = collection(this.db, this.path) as CollectionReference; + private readonly col = collection( + this.db, + this.path, + ) as CollectionReference; list(where?: QueryConstraint): Observable { return collectionData( - where ? query(this.col, where) : this.col + where ? query(this.col, where) : this.col, ); } diff --git a/src/app/services/item.service.ts b/src/app/services/item.service.ts index 5748c6b..ad52852 100644 --- a/src/app/services/item.service.ts +++ b/src/app/services/item.service.ts @@ -17,17 +17,20 @@ import { FirestoreBase } from './firestoreBase'; providedIn: 'root', }) export class ItemService extends FirestoreBase { - constructor(db: Firestore, private auth: Auth) { + constructor( + db: Firestore, + private readonly auth: Auth, + ) { super(db, 'items'); } async store(item: WithFieldValue & { id: Item['id'] }) { const history = collection( this.db, - 'histories' + 'histories', ) as CollectionReference; await addDoc(history, { - uid: this.auth.currentUser?.uid || '', + uid: this.auth.currentUser?.uid ?? '', date: serverTimestamp(), itemId: item.id, item: { ...item, updatedAt: serverTimestamp() }, diff --git a/src/app/storage/add-storage/add-storage.component.ts b/src/app/storage/add-storage/add-storage.component.ts index 1323aa9..fb32aa2 100644 --- a/src/app/storage/add-storage/add-storage.component.ts +++ b/src/app/storage/add-storage/add-storage.component.ts @@ -1,39 +1,46 @@ +import { CdkScrollable } from '@angular/cdk/scrolling'; import { Component, Inject } from '@angular/core'; -import { UntypedFormBuilder, ReactiveFormsModule } from '@angular/forms'; -import { MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog'; -import { Storage } from 'src/models/storage.model'; -import { MatIcon } from '@angular/material/icon'; +import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms'; import { MatButton } from '@angular/material/button'; -import { MatInput } from '@angular/material/input'; +import { + MAT_DIALOG_DATA, + MatDialogActions, + MatDialogClose, + MatDialogContent, + MatDialogRef, + MatDialogTitle, +} from '@angular/material/dialog'; import { MatFormField, MatLabel } from '@angular/material/form-field'; -import { CdkScrollable } from '@angular/cdk/scrolling'; +import { MatIcon } from '@angular/material/icon'; +import { MatInput } from '@angular/material/input'; +import { Storage } from 'src/models/storage.model'; @Component({ - selector: 'app-add-storage', - templateUrl: './add-storage.component.html', - styleUrls: ['./add-storage.component.scss'], - standalone: true, - imports: [ - MatDialogTitle, - ReactiveFormsModule, - CdkScrollable, - MatDialogContent, - MatFormField, - MatLabel, - MatInput, - MatDialogActions, - MatButton, - MatIcon, - MatDialogClose, - ], + selector: 'app-add-storage', + templateUrl: './add-storage.component.html', + styleUrls: ['./add-storage.component.scss'], + standalone: true, + imports: [ + MatDialogTitle, + ReactiveFormsModule, + CdkScrollable, + MatDialogContent, + MatFormField, + MatLabel, + MatInput, + MatDialogActions, + MatButton, + MatIcon, + MatDialogClose, + ], }) export class AddStorageComponent { storage; constructor( - private fb: UntypedFormBuilder, - private ref: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data?: Storage + private readonly fb: UntypedFormBuilder, + private readonly ref: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data?: Storage, ) { this.storage = this.fb.group({ id: [data?.id], diff --git a/src/app/storage/storage-list/storage-list.component.ts b/src/app/storage/storage-list/storage-list.component.ts index 52eafb3..ff16918 100644 --- a/src/app/storage/storage-list/storage-list.component.ts +++ b/src/app/storage/storage-list/storage-list.component.ts @@ -1,13 +1,13 @@ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component } from '@angular/core'; +import { MatFabButton, MatIconButton } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; +import { MatIcon } from '@angular/material/icon'; +import { MatList, MatListItem } from '@angular/material/list'; +import { MatTooltip } from '@angular/material/tooltip'; import { StorageService } from 'src/app/services/storage.service'; import { Storage } from 'src/models/storage.model'; import { AddStorageComponent } from '../add-storage/add-storage.component'; -import { MatTooltip } from '@angular/material/tooltip'; -import { MatIconButton, MatFabButton } from '@angular/material/button'; -import { MatIcon } from '@angular/material/icon'; -import { MatList, MatListItem } from '@angular/material/list'; -import { NgIf, NgFor, AsyncPipe } from '@angular/common'; @Component({ selector: 'app-storage-list', @@ -30,8 +30,8 @@ export class StorageListComponent { storages; constructor( - private ss: StorageService, - private dialog: MatDialog, + private readonly ss: StorageService, + private readonly dialog: MatDialog, ) { this.storages = this.ss.list(); } From 42b00da6d693c7844f74932938229416d0b378fd Mon Sep 17 00:00:00 2001 From: Masayuki Date: Sat, 19 Oct 2024 02:21:41 +0900 Subject: [PATCH 2/2] add tests --- .vscode/launch.json | 15 ++ .vscode/settings.json | 3 +- package-lock.json | 14 ++ package.json | 1 + .../customer-detail.component.spec.ts | 139 +++++++++++++++++- .../item-detail/item-detail.component.spec.ts | 16 +- .../new-order/new-order.component.spec.ts | 21 ++- 7 files changed, 200 insertions(+), 9 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2542005 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:4200", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 01a9727..6eac4d2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,6 @@ "sonarlint.connectedMode.project": { "connectionId": "plageoj", "projectKey": "plageoj_fukagawa-coffee" - } + }, + "simplecov-vscode.enabled": false } diff --git a/package-lock.json b/package-lock.json index 1f5db12..1050f73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "eslint-config-prettier": "^9.1.0", "inquirer-autocomplete-prompt": "^3.0.1", "jasmine-core": "~5.4.0", + "jasmine-marbles": "^0.9.2", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", @@ -13025,6 +13026,19 @@ "dev": true, "license": "MIT" }, + "node_modules/jasmine-marbles": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/jasmine-marbles/-/jasmine-marbles-0.9.2.tgz", + "integrity": "sha512-T7RjG4fRsdiGGzbQZ6Kj39qYt6O1/KIcR4FkUNsD3DUGkd/AzpwzN+xtk0DXlLWEz5BaVdK1SzMgQDVw879c4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.20" + }, + "peerDependencies": { + "rxjs": "^7.0.0" + } + }, "node_modules/jasmine-spec-reporter": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz", diff --git a/package.json b/package.json index 93296b8..3540f69 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "eslint-config-prettier": "^9.1.0", "inquirer-autocomplete-prompt": "^3.0.1", "jasmine-core": "~5.4.0", + "jasmine-marbles": "^0.9.2", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", diff --git a/src/app/customer/customer-detail/customer-detail.component.spec.ts b/src/app/customer/customer-detail/customer-detail.component.spec.ts index d8954bf..4fd5d60 100644 --- a/src/app/customer/customer-detail/customer-detail.component.spec.ts +++ b/src/app/customer/customer-detail/customer-detail.component.spec.ts @@ -1,32 +1,163 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { CustomerDetailComponent } from './customer-detail.component'; -import { FirebaseTestingModule } from 'src/app/firebase-testing.module'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { MatButtonHarness } from '@angular/material/button/testing'; +import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { cold } from 'jasmine-marbles'; +import { FirebaseTestingModule } from 'src/app/firebase-testing.module'; +import { CustomerService } from 'src/app/services/customer.service'; +import { ItemService } from 'src/app/services/item.service'; +import { Customer } from 'src/models/customer.model'; +import { Item } from 'src/models/item.model'; +import { CustomerDetailComponent } from './customer-detail.component'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { of } from 'rxjs'; +import { MatSnackBar } from '@angular/material/snack-bar'; describe('CustomerDetailComponent', () => { let component: CustomerDetailComponent; let fixture: ComponentFixture; + let loader: HarnessLoader; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [FirebaseTestingModule, CustomerDetailComponent], + imports: [ + FirebaseTestingModule, + CustomerDetailComponent, + NoopAnimationsModule, + ], providers: [ { provide: ActivatedRoute, - useValue: { snapshot: { paramMap: convertToParamMap({}) } }, + useValue: { + snapshot: { + paramMap: convertToParamMap({ + id: 'test-id', + }), + }, + }, }, ], }).compileComponents(); + + const customerService = TestBed.inject(CustomerService); + spyOn(customerService, 'load').and.returnValue( + cold('c|', { + c: { + id: 'test-id', + name: 'Test Customer', + address: '123 Main Street', + items: { + test: true, + }, + } as Customer, + }), + ); + + const itemService = TestBed.inject(ItemService); + spyOn(itemService, 'list').and.returnValue( + cold('i|', { + i: [ + { + id: 'test', + name: 'Test Item', + } as Item, + ], + }), + ); }); beforeEach(() => { fixture = TestBed.createComponent(CustomerDetailComponent); component = fixture.componentInstance; + loader = TestbedHarnessEnvironment.loader(fixture); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('associate item', async () => { + const dialogSpy = ( + spyOn(TestBed.inject(MatDialog), 'open') as jasmine.Spy + ).and.returnValue({ + afterClosed: () => + of({ + id: 'test', + name: 'Test Item', + }), + }); + const button = await loader.getHarness( + MatButtonHarness.with({ text: /品目を追加する/ }), + ); + await button.click(); + + expect(dialogSpy).toHaveBeenCalled(); + }); + + it('edit customer', async () => { + const dialogSpy = spyOn( + TestBed.inject(MatDialog), + 'open', + ).and.callThrough(); + const button = await loader.getHarness( + MatButtonHarness.with({ text: /編集/ }), + ); + await button.click(); + + expect(dialogSpy).not.toHaveBeenCalled(); + + component.customer = { + id: 'test-id', + name: 'Test Customer', + address: '123 Main Street', + items: { + test: true, + }, + }; + + fixture.detectChanges(); + await button.click(); + expect(dialogSpy).toHaveBeenCalled(); + }); + + it('delete customer', async () => { + const dialogSpy = ( + spyOn(TestBed.inject(MatSnackBar), 'open') as jasmine.Spy + ).and.returnValue({ + afterDismissed: () => of({ dismissedByAction: false }), + }); + const button = await loader.getHarness( + MatButtonHarness.with({ text: /削除/ }), + ); + await button.click(); + expect(dialogSpy).not.toHaveBeenCalled(); + component.customer = { + id: 'test-id', + name: 'Test Customer', + address: '123 Main Street', + items: { + test: true, + }, + }; + fixture.detectChanges(); + await button.click(); + expect(dialogSpy).toHaveBeenCalled(); + }); + + it('delete item', () => { + component.customer = { + id: 'test-id', + name: 'Test Customer', + address: '123 Main Street', + items: { + test: true, + }, + }; + component.deleteItem('test'); + expect(component.customer?.items).toEqual({}); + }); }); diff --git a/src/app/item/item-detail/item-detail.component.spec.ts b/src/app/item/item-detail/item-detail.component.spec.ts index 96efd59..1b03bbc 100644 --- a/src/app/item/item-detail/item-detail.component.spec.ts +++ b/src/app/item/item-detail/item-detail.component.spec.ts @@ -1,8 +1,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ItemDetailComponent } from './item-detail.component'; +import { + ActivatedRoute, + convertToParamMap, + provideRouter, +} from '@angular/router'; import { FirebaseTestingModule } from 'src/app/firebase-testing.module'; -import { provideRouter } from '@angular/router'; +import { ItemDetailComponent } from './item-detail.component'; describe('ItemDetailComponent', () => { let component: ItemDetailComponent; @@ -11,7 +15,13 @@ describe('ItemDetailComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [FirebaseTestingModule, ItemDetailComponent], - providers: [provideRouter([])], + providers: [ + provideRouter([]), + { + provides: ActivatedRoute, + useValue: convertToParamMap({ id: 'test' }), + }, + ], }).compileComponents(); }); diff --git a/src/app/order/new-order/new-order.component.spec.ts b/src/app/order/new-order/new-order.component.spec.ts index f87d11f..0395cd5 100644 --- a/src/app/order/new-order/new-order.component.spec.ts +++ b/src/app/order/new-order/new-order.component.spec.ts @@ -1,9 +1,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { provideRouter } from '@angular/router'; import { FirebaseTestingModule } from 'src/app/firebase-testing.module'; +import { OrderService } from 'src/app/services/order.service'; import { NewOrderComponent } from './new-order.component'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { CustomerService } from 'src/app/services/customer.service'; +import { cold } from 'jasmine-marbles'; describe('NewOrderComponent', () => { let component: NewOrderComponent; @@ -14,6 +17,10 @@ describe('NewOrderComponent', () => { imports: [NewOrderComponent, FirebaseTestingModule, NoopAnimationsModule], providers: [provideRouter([])], }).compileComponents(); + + spyOn(TestBed.inject(CustomerService), 'load').and.returnValue( + cold('c|', { c: { items: { test: true } } }), + ); }); beforeEach(() => { @@ -25,4 +32,16 @@ describe('NewOrderComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('add item', () => { + component.addItem(); + expect(component.items.length).toBe(1); + }); + + it('send order', async () => { + component.items = [{ name: 'test' }]; + const store = spyOn(TestBed.inject(OrderService), 'store').and.resolveTo(); + component.sendOrder(); + expect(store).toHaveBeenCalled(); + }); });