juice-shop
176 строк · 7.8 Кб
1/*
2* Copyright (c) 2014-2024 Bjoern Kimminich & the OWASP Juice Shop contributors.
3* SPDX-License-Identifier: MIT
4*/
5
6import { TranslateModule } from '@ngx-translate/core'
7import { ProductReviewEditComponent } from '../product-review-edit/product-review-edit.component'
8import { By } from '@angular/platform-browser'
9import { MatDividerModule } from '@angular/material/divider'
10import { UserService } from '../Services/user.service'
11import { ProductReviewService } from '../Services/product-review.service'
12import { HttpClientTestingModule } from '@angular/common/http/testing'
13import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
14import { MatButtonModule } from '@angular/material/button'
15import { MatInputModule } from '@angular/material/input'
16import { MatFormFieldModule } from '@angular/material/form-field'
17import { MAT_DIALOG_DATA, MatDialog, MatDialogModule } from '@angular/material/dialog'
18import { MatBadgeModule } from '@angular/material/badge'
19import { type ComponentFixture, fakeAsync, flush, TestBed, waitForAsync } from '@angular/core/testing'
20import { MatTooltipModule } from '@angular/material/tooltip'
21import { MatIconModule } from '@angular/material/icon'
22import { MatExpansionModule } from '@angular/material/expansion'
23
24import { ProductDetailsComponent } from './product-details.component'
25import { of, throwError } from 'rxjs'
26import { ReactiveFormsModule } from '@angular/forms'
27import { MatSnackBarModule } from '@angular/material/snack-bar'
28import { type Product } from '../Models/product.model'
29
30describe('ProductDetailsComponent', () => {
31let component: ProductDetailsComponent
32let fixture: ComponentFixture<ProductDetailsComponent>
33let userService: any
34let productReviewService: any
35let dialog: any
36let dialogRefMock
37
38beforeEach(waitForAsync(() => {
39userService = jasmine.createSpyObj('UserService', ['whoAmI'])
40userService.whoAmI.and.returnValue(of({}))
41productReviewService = jasmine.createSpyObj('ProductReviewService', ['get', 'create'])
42productReviewService.get.and.returnValue(of([]))
43productReviewService.create.and.returnValue(of({}))
44dialog = jasmine.createSpyObj('Dialog', ['open'])
45dialogRefMock = {
46afterClosed: () => of({})
47}
48dialog.open.and.returnValue(dialogRefMock)
49
50TestBed.configureTestingModule({
51imports: [
52TranslateModule.forRoot(),
53HttpClientTestingModule,
54ReactiveFormsModule,
55BrowserAnimationsModule,
56MatDialogModule,
57MatFormFieldModule,
58MatInputModule,
59MatButtonModule,
60MatDividerModule,
61MatBadgeModule,
62MatIconModule,
63MatTooltipModule,
64MatExpansionModule,
65MatSnackBarModule
66],
67declarations: [ProductDetailsComponent],
68providers: [
69{ provide: UserService, useValue: userService },
70{ provide: ProductReviewService, useValue: productReviewService },
71{ provide: MatDialog, useValue: dialog },
72{ provide: MAT_DIALOG_DATA, useValue: { productData: {} } }
73]
74})
75.compileComponents()
76}))
77
78beforeEach(() => {
79fixture = TestBed.createComponent(ProductDetailsComponent)
80component = fixture.componentInstance
81fixture.autoDetectChanges()
82})
83
84it('should create', () => {
85expect(component).toBeTruthy()
86})
87
88it('should post anonymous review if no user email is returned', () => {
89component.data = { productData: { id: 42 } as Product }
90userService.whoAmI.and.returnValue(of({}))
91component.ngOnInit()
92const textArea: HTMLTextAreaElement = fixture.debugElement.query(By.css('textarea')).nativeElement
93textArea.value = 'Great product!'
94const buttonDe = fixture.debugElement.query(By.css('#submitButton'))
95buttonDe.triggerEventHandler('click', null)
96const reviewObject = { message: 'Great product!', author: 'Anonymous' }
97expect(productReviewService.create.calls.count()).toBe(1)
98expect(productReviewService.create.calls.argsFor(0)[0]).toBe(42)
99expect(productReviewService.create.calls.argsFor(0)[1]).toEqual(reviewObject)
100})
101
102it('should post review with user email as author', () => {
103component.data = { productData: { id: 42 } as Product }
104userService.whoAmI.and.returnValue(of({ email: 'horst@juice-sh.op' }))
105component.ngOnInit()
106const textArea: HTMLTextAreaElement = fixture.debugElement.query(By.css('textarea')).nativeElement
107textArea.value = 'Great product!'
108const buttonDe = fixture.debugElement.query(By.css('#submitButton'))
109buttonDe.triggerEventHandler('click', null)
110const reviewObject = { message: 'Great product!', author: 'horst@juice-sh.op' }
111expect(productReviewService.create.calls.count()).toBe(1)
112expect(productReviewService.create.calls.argsFor(0)[0]).toBe(42)
113expect(productReviewService.create.calls.argsFor(0)[1]).toEqual(reviewObject)
114})
115
116it('should log errors when retrieving user directly to browser console', fakeAsync(() => {
117component.data = { productData: { id: 42 } as Product }
118userService.whoAmI.and.returnValue(throwError('Error'))
119console.log = jasmine.createSpy('log')
120component.ngOnInit()
121expect(console.log).toHaveBeenCalledWith('Error')
122}))
123
124it('should log errors when posting review directly to browser console', fakeAsync(() => {
125component.data = { productData: { id: 42 } as Product }
126userService.whoAmI.and.returnValue(of({}))
127productReviewService.create.and.returnValue(throwError('Error'))
128console.log = jasmine.createSpy('log')
129component.ngOnInit()
130const textArea: HTMLTextAreaElement = fixture.debugElement.query(By.css('textarea')).nativeElement
131textArea.value = 'Great product!'
132const buttonDe = fixture.debugElement.query(By.css('#submitButton'))
133buttonDe.triggerEventHandler('click', null)
134expect(console.log).toHaveBeenCalledWith('Error')
135fixture.destroy()
136flush()
137}))
138
139it('should refresh reviews after posting a review', () => {
140component.data = { productData: { id: 42 } as Product }
141productReviewService.create.and.returnValue(of({}))
142productReviewService.get.and.returnValue(of([{ id: '42', message: 'Review 1', author: 'Anonymous' }]))
143userService.whoAmI.and.returnValue(of({}))
144component.ngOnInit()
145const textArea: HTMLTextAreaElement = fixture.debugElement.query(By.css('textarea')).nativeElement
146textArea.value = 'Great product!'
147const buttonDe = fixture.debugElement.query(By.css('#submitButton'))
148buttonDe.triggerEventHandler('click', null)
149expect(productReviewService.create).toHaveBeenCalled()
150expect(productReviewService.get).toHaveBeenCalled()
151})
152
153it('should open a modal dialog with review editor', () => {
154component.data = { productData: { id: 42 } as Product }
155userService.whoAmI.and.returnValue(of({ email: 'horst@juice-sh.op' }))
156productReviewService.get.and.returnValue(of([{ id: '42', message: 'Great product!', author: 'horst@juice-sh.op' }]))
157component.ngOnInit()
158fixture.detectChanges()
159const buttonDe = fixture.debugElement.query(By.css('div.review-text'))
160buttonDe.triggerEventHandler('click', null)
161expect(dialog.open.calls.count()).toBe(1)
162expect(dialog.open.calls.argsFor(0)[0]).toBe(ProductReviewEditComponent)
163expect(dialog.open.calls.argsFor(0)[1].data).toEqual({ reviewData: { id: '42', message: 'Great product!', author: 'horst@juice-sh.op' } })
164})
165
166it('should refresh reviews of product after editing a review', () => {
167component.data = { productData: { id: 42 } as Product }
168userService.whoAmI.and.returnValue(of({ email: 'horst@juice-sh.op' }))
169productReviewService.get.and.returnValue(of([{ id: '42', message: 'Great product!', author: 'horst@juice-sh.op' }]))
170component.ngOnInit()
171fixture.detectChanges()
172const buttonDe = fixture.debugElement.query(By.css('div.review-text'))
173buttonDe.triggerEventHandler('click', null)
174expect(productReviewService.get).toHaveBeenCalledWith(42)
175})
176})
177