juice-shop
266 строк · 11.8 Кб
1/*
2* Copyright (c) 2014-2024 Bjoern Kimminich & the OWASP Juice Shop contributors.
3* SPDX-License-Identifier: MIT
4*/
5
6import { TranslateModule, TranslateService } from '@ngx-translate/core'
7import { MatInputModule } from '@angular/material/input'
8import { BasketService } from '../Services/basket.service'
9import { type ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'
10import { MatCardModule } from '@angular/material/card'
11import { MatTableModule } from '@angular/material/table'
12import { MatButtonModule } from '@angular/material/button'
13import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
14import { HttpClientTestingModule } from '@angular/common/http/testing'
15import { ReactiveFormsModule } from '@angular/forms'
16import { of } from 'rxjs'
17import { throwError } from 'rxjs/internal/observable/throwError'
18import { MatButtonToggleModule } from '@angular/material/button-toggle'
19import { PurchaseBasketComponent } from '../purchase-basket/purchase-basket.component'
20import { UserService } from '../Services/user.service'
21import { DeluxeGuard } from '../app.guard'
22import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'
23import { EventEmitter } from '@angular/core'
24
25describe('PurchaseBasketComponent', () => {
26let component: PurchaseBasketComponent
27let fixture: ComponentFixture<PurchaseBasketComponent>
28let basketService
29let userService
30let translateService: any
31let deluxeGuard
32let snackBar: any
33
34beforeEach(waitForAsync(() => {
35basketService = jasmine.createSpyObj('BasketService', ['find', 'del', 'get', 'put', 'updateNumberOfCartItems'])
36basketService.find.and.returnValue(of({ Products: [] }))
37basketService.del.and.returnValue(of({}))
38basketService.get.and.returnValue(of({}))
39basketService.put.and.returnValue(of({}))
40basketService.updateNumberOfCartItems.and.returnValue(of({}))
41userService = jasmine.createSpyObj('UserService', ['whoAmI'])
42userService.whoAmI.and.returnValue(of({}))
43translateService = jasmine.createSpyObj('TranslateService', ['get'])
44translateService.get.and.returnValue(of({}))
45translateService.onLangChange = new EventEmitter()
46translateService.onTranslationChange = new EventEmitter()
47translateService.onDefaultLangChange = new EventEmitter()
48deluxeGuard = jasmine.createSpyObj('', ['isDeluxe'])
49deluxeGuard.isDeluxe.and.returnValue(false)
50snackBar = jasmine.createSpyObj('MatSnackBar', ['open'])
51
52TestBed.configureTestingModule({
53declarations: [PurchaseBasketComponent],
54imports: [
55HttpClientTestingModule,
56TranslateModule.forRoot(),
57BrowserAnimationsModule,
58ReactiveFormsModule,
59MatInputModule,
60MatCardModule,
61MatTableModule,
62MatButtonModule,
63MatButtonToggleModule,
64MatSnackBarModule
65],
66providers: [
67{ provide: TranslateService, useValue: translateService },
68{ provide: BasketService, useValue: basketService },
69{ provide: MatSnackBar, useValue: snackBar },
70{ provide: UserService, useValue: userService },
71{ provide: DeluxeGuard, useValue: deluxeGuard }
72]
73})
74.compileComponents()
75}))
76
77beforeEach(() => {
78fixture = TestBed.createComponent(PurchaseBasketComponent)
79component = fixture.componentInstance
80fixture.detectChanges()
81})
82
83it('should create', () => {
84expect(component).toBeTruthy()
85})
86
87it('should load user email when being created', () => {
88userService.whoAmI.and.returnValue(of({ email: 'a@a' }))
89component.ngOnInit()
90expect(component.userEmail).toBe('(a@a)')
91})
92
93it('should log an error if userService fails to fetch the user', fakeAsync(() => {
94userService.whoAmI.and.returnValue(throwError('Error'))
95console.log = jasmine.createSpy('log')
96component.ngOnInit()
97expect(console.log).toHaveBeenCalledWith('Error')
98}))
99
100it('should hold products returned by backend API', () => {
101basketService.find.and.returnValue(of({ Products: [{ name: 'Product1', price: 1, deluxePrice: 1, BasketItem: { quantity: 1 } }, { name: 'Product2', price: 2, deluxePrice: 2, BasketItem: { quantity: 2 } }] }))
102component.load()
103expect(component.dataSource.length).toBe(2)
104expect(component.dataSource[0].name).toBe('Product1')
105expect(component.dataSource[0].price).toBe(1)
106expect(component.dataSource[0].BasketItem.quantity).toBe(1)
107expect(component.dataSource[1].name).toBe('Product2')
108expect(component.dataSource[1].price).toBe(2)
109expect(component.dataSource[1].BasketItem.quantity).toBe(2)
110})
111
112it('should have price equal to deluxePrice for deluxe users', () => {
113deluxeGuard.isDeluxe.and.returnValue(true)
114basketService.find.and.returnValue(of({ Products: [{ name: 'Product1', price: 2, deluxePrice: 1, BasketItem: { quantity: 1 } }] }))
115component.load()
116expect(component.dataSource.length).toBe(1)
117expect(component.dataSource[0].name).toBe('Product1')
118expect(component.dataSource[0].price).toBe(1)
119})
120
121it('should have price different from deluxePrice for non-deluxe users', () => {
122deluxeGuard.isDeluxe.and.returnValue(false)
123basketService.find.and.returnValue(of({ Products: [{ name: 'Product1', price: 2, deluxePrice: 1, BasketItem: { quantity: 1 } }] }))
124component.load()
125expect(component.dataSource.length).toBe(1)
126expect(component.dataSource[0].name).toBe('Product1')
127expect(component.dataSource[0].price).toBe(2)
128})
129
130it('should hold no products on error in backend API', fakeAsync(() => {
131basketService.find.and.returnValue(throwError('Error'))
132component.load()
133expect(component.dataSource.length).toBe(0)
134}))
135
136it('should hold no products when none are returned by backend API', () => {
137basketService.find.and.returnValue(of({ Products: [] }))
138component.load()
139expect(component.dataSource).toEqual([])
140})
141
142it('should log error while getting Products from backend API directly to browser console', fakeAsync(() => {
143basketService.find.and.returnValue(throwError('Error'))
144console.log = jasmine.createSpy('log')
145component.load()
146expect(console.log).toHaveBeenCalledWith('Error')
147}))
148
149it('should pass delete request for basket item via BasketService', () => {
150component.delete(1)
151expect(basketService.del).toHaveBeenCalledWith(1)
152})
153
154it('should load again after deleting a basket item', () => {
155basketService.find.and.returnValue(of({ Products: [{ name: 'Product1', price: 1, deluxePrice: 1, BasketItem: { quantity: 1 } }, { name: 'Product2', price: 2, deluxePrice: 2, BasketItem: { quantity: 2 } }] }))
156component.delete(1)
157expect(component.dataSource.length).toBe(2)
158expect(component.dataSource[0].name).toBe('Product1')
159expect(component.dataSource[0].price).toBe(1)
160expect(component.dataSource[0].BasketItem.quantity).toBe(1)
161expect(component.dataSource[1].name).toBe('Product2')
162expect(component.dataSource[1].price).toBe(2)
163expect(component.dataSource[1].BasketItem.quantity).toBe(2)
164})
165
166it('should log error while deleting basket item directly to browser console', fakeAsync(() => {
167basketService.del.and.returnValue(throwError('Error'))
168console.log = jasmine.createSpy('log')
169component.delete(1)
170expect(console.log).toHaveBeenCalledWith('Error')
171}))
172
173it('should update basket item with increased quantity after adding another item of same type', () => {
174basketService.find.and.returnValue(of({ Products: [{ name: 'Product1', price: 1, deluxePrice: 1, BasketItem: { id: 1, quantity: 1 } }] }))
175basketService.get.and.returnValue(of({ id: 1, quantity: 1 }))
176component.inc(1)
177expect(basketService.get).toHaveBeenCalledWith(1)
178expect(basketService.put).toHaveBeenCalledWith(1, { quantity: 2 })
179})
180
181it('should not increase quantity on error retrieving basket item and log the error', fakeAsync(() => {
182basketService.get.and.returnValue(throwError('Error'))
183console.log = jasmine.createSpy('log')
184component.inc(1)
185expect(console.log).toHaveBeenCalledWith('Error')
186expect(basketService.put).not.toHaveBeenCalled()
187}))
188
189it('should not increase quantity on error updating basket item and log the error', fakeAsync(() => {
190basketService.find.and.returnValue(of({ Products: [{ BasketItem: { id: 1, quantity: 1 } }] }))
191basketService.get.and.returnValue(of({ id: 1, quantity: 1 }))
192basketService.put.and.returnValue(throwError('Error'))
193console.log = jasmine.createSpy('log')
194component.inc(1)
195expect(console.log).toHaveBeenCalledWith('Error')
196}))
197
198it('should load again after increasing product quantity', () => {
199basketService.find.and.returnValue(of({ Products: [{ BasketItem: { id: 1, quantity: 2 } }] }))
200basketService.get.and.returnValue(of({ id: 1, quantity: 2 }))
201basketService.put.and.returnValue(of({ id: 1, quantity: 3 }))
202component.inc(1)
203expect(basketService.find).toHaveBeenCalled()
204})
205
206it('should update basket item with decreased quantity after removing an item', () => {
207basketService.find.and.returnValue(of({ Products: [{ BasketItem: { id: 1, quantity: 2 } }] }))
208basketService.get.and.returnValue(of({ id: 1, quantity: 2 }))
209basketService.put.and.returnValue(of({ id: 1, quantity: 1 }))
210component.dec(1)
211expect(basketService.get).toHaveBeenCalledWith(1)
212expect(basketService.put).toHaveBeenCalledWith(1, { quantity: 1 })
213})
214
215it('should always keep one item of any product in the basket when reducing quantity via UI', () => {
216basketService.find.and.returnValue(of({ Products: [{ BasketItem: { id: 1, quantity: 1 } }] }))
217basketService.get.and.returnValue(of({ id: 1, quantity: 1 }))
218basketService.put.and.returnValue(of({ id: 1, quantity: 1 }))
219component.dec(1)
220expect(basketService.get).toHaveBeenCalledWith(1)
221expect(basketService.put).toHaveBeenCalledWith(1, { quantity: 1 })
222})
223
224it('should not decrease quantity on error retrieving basket item and log the error', fakeAsync(() => {
225basketService.get.and.returnValue(throwError('Error'))
226console.log = jasmine.createSpy('log')
227component.dec(1)
228expect(console.log).toHaveBeenCalledWith('Error')
229expect(basketService.put).not.toHaveBeenCalled()
230}))
231
232it('should not decrease quantity on error updating basket item and log the error', fakeAsync(() => {
233basketService.find.and.returnValue(of({ Products: [{ BasketItem: { id: 1, quantity: 1 } }] }))
234basketService.get.and.returnValue(of({ id: 1, quantity: 1 }))
235basketService.put.and.returnValue(throwError('Error'))
236console.log = jasmine.createSpy('log')
237component.dec(1)
238expect(console.log).toHaveBeenCalledWith('Error')
239}))
240
241it('should load again after decreasing product quantity', () => {
242basketService.find.and.returnValue(of({ Products: [{ BasketItem: { id: 1, quantity: 2 } }] }))
243basketService.get.and.returnValue(of({ id: 1, quantity: 2 }))
244basketService.put.and.returnValue(of({ id: 1, quantity: 1 }))
245component.dec(1)
246expect(basketService.find).toHaveBeenCalled()
247})
248
249it('should reset quantity to 1 when decreasing for quantity tampered to be negative', () => {
250basketService.find.and.returnValue(of({ Products: [{ BasketItem: { id: 1, quantity: -100 } }] }))
251basketService.get.and.returnValue(of({ id: 1, quantity: -100 }))
252basketService.put.and.returnValue(of({ id: 1, quantity: 1 }))
253component.dec(1)
254expect(basketService.get).toHaveBeenCalledWith(1)
255expect(basketService.put).toHaveBeenCalledWith(1, { quantity: 1 })
256})
257
258it('should reset quantity to 1 when increasing for quantity tampered to be negative', () => {
259basketService.find.and.returnValue(of({ Products: [{ BasketItem: { id: 1, quantity: -100 } }] }))
260basketService.get.and.returnValue(of({ id: 1, quantity: -100 }))
261basketService.put.and.returnValue(of({ id: 1, quantity: 1 }))
262component.inc(1)
263expect(basketService.get).toHaveBeenCalledWith(1)
264expect(basketService.put).toHaveBeenCalledWith(1, { quantity: 1 })
265})
266})
267