diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedback.service.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedback.service.ts index b8a363c5..de118fb2 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedback.service.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedback.service.ts @@ -86,7 +86,7 @@ export class ProductFeedbackService { ); } - private findProductFeedbacksByCriteria( + findProductFeedbacksByCriteria( productId: string = this.productDetailService.productId(), page: number = this.page(), sort: string = this.sort(), @@ -175,11 +175,12 @@ export class ProductFeedbackService { this.cookieService.delete(TOKEN_KEY); } - handleFeedbackApiResponse(response: FeedbackApiResponse) { + handleFeedbackApiResponse(response: FeedbackApiResponse): void { this.totalPages.set(response.page.totalPages); this.totalElements.set(response.page.totalElements); } - getInitFeedbacksObservable() { + + getInitFeedbacksObservable(): Observable { this.page.set(0); return this.findProductFeedbacksByCriteria(); } diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.spec.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.spec.ts index 8ca0ec60..f7f156a7 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.spec.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.spec.ts @@ -10,15 +10,12 @@ import { CookieService } from 'ngx-cookie-service'; import { ActivatedRoute, provideRouter, Router } from '@angular/router'; import { CommonUtils } from '../../../../shared/utils/common.utils'; import { ROUTER } from '../../../../shared/constants/router.constant'; -import { MatomoConfiguration, MatomoModule, MatomoRouterModule } from 'ngx-matomo-client'; import { MatomoTestingModule } from 'ngx-matomo-client/testing'; import { ProductDetailActionType } from '../../../../shared/enums/product-detail-action-type'; import { MATOMO_TRACKING_ENVIRONMENT } from '../../../../shared/constants/matomo.constant'; class MockElementRef implements ElementRef { - nativeElement = { - contains: jasmine.createSpy('contains') - }; + nativeElement = { contains: jasmine.createSpy('contains') }; } describe('ProductDetailVersionActionComponent', () => { @@ -31,10 +28,11 @@ describe('ProductDetailVersionActionComponent', () => { beforeEach(() => { productServiceMock = jasmine.createSpyObj('ProductService', [ - 'sendRequestToProductDetailVersionAPI', 'sendRequestToUpdateInstallationCount', 'sendRequestToGetProductVersionsForDesigner' + 'sendRequestToProductDetailVersionAPI', + 'sendRequestToUpdateInstallationCount', + 'sendRequestToGetProductVersionsForDesigner' ]); - const commonUtilsSpy = jasmine.createSpyObj('CommonUtils', ['getCookieValue']); - // const cookieServiceSpy = jasmine.createSpyObj('CookieService', ['get', 'set']); + const commonUtilsSpy = jasmine.createSpyObj('CommonUtils', [ 'getCookieValue' ]); const activatedRouteSpy = jasmine.createSpyObj('ActivatedRoute', [], { snapshot: { queryParams: {} @@ -43,7 +41,7 @@ describe('ProductDetailVersionActionComponent', () => { TestBed.configureTestingModule({ imports: [ - ProductDetailVersionActionComponent, + ProductDetailVersionActionComponent, TranslateModule.forRoot(), MatomoTestingModule.forRoot() ], @@ -67,9 +65,7 @@ describe('ProductDetailVersionActionComponent', () => { fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { expect(component).toBeTruthy(); }); it('first artifact should be chosen when select corresponding version', () => { const selectedVersion = 'Version 10.0.2'; @@ -91,11 +87,13 @@ describe('ProductDetailVersionActionComponent', () => { it('should update selectedVersion, artifacts, selectedArtifactName, and selectedArtifact, and call addVersionParamToRoute', () => { const version = '1.0'; - const artifacts = [{ - name: 'Example Artifact', - downloadUrl: 'https://example.com/download', - isProductArtifact: true - } as ItemDropdown]; + const artifacts = [ + { + name: 'Example Artifact', + downloadUrl: 'https://example.com/download', + isProductArtifact: true + } as ItemDropdown + ]; const versionMap = new Map(); versionMap.set(version, artifacts); @@ -172,7 +170,6 @@ describe('ProductDetailVersionActionComponent', () => { }); }); - it('all of state should be reset before call rest api', () => { const selectedVersion = 'Version 10.0.2'; const artifact = { @@ -235,6 +232,7 @@ describe('ProductDetailVersionActionComponent', () => { }); it('should send Api to get DevVersion', () => { + component.isDevVersionsDisplayed.set(false); spyOn(component.isDevVersionsDisplayed, 'set'); expect(component.isDevVersionsDisplayed()).toBeFalse(); mockApiWithExpectedResponse(); @@ -249,7 +247,8 @@ describe('ProductDetailVersionActionComponent', () => { const mockArtifact1 = { name: 'Example Artifact1', downloadUrl: 'https://example.com/download', - isProductArtifact: true, label: 'Example Artifact1' + isProductArtifact: true, + label: 'Example Artifact1' } as ItemDropdown; const mockArtifact2 = { name: 'Example Artifact2', @@ -291,42 +290,61 @@ describe('ProductDetailVersionActionComponent', () => { component.versions.set(['1.0', '1.1']); fixture.detectChanges(); component.getVersionInDesigner(); - expect(productServiceMock.sendRequestToGetProductVersionsForDesigner).not.toHaveBeenCalled(); + expect( + productServiceMock.sendRequestToGetProductVersionsForDesigner + ).not.toHaveBeenCalled(); }); it('should call productService and update versions if versions are empty', () => { const productId = '123'; component.versions.set([]); const mockVersions = [{ version: '1.0' }, { version: '2.0' }]; - productServiceMock.sendRequestToGetProductVersionsForDesigner.and.returnValue(of(mockVersions)); + productServiceMock.sendRequestToGetProductVersionsForDesigner.and.returnValue( + of(mockVersions) + ); // Act component.getVersionInDesigner(); // Assert - expect(productServiceMock.sendRequestToGetProductVersionsForDesigner).toHaveBeenCalledWith(productId); + expect( + productServiceMock.sendRequestToGetProductVersionsForDesigner + ).toHaveBeenCalledWith(productId); expect(component.versions()).toEqual(['Version 1.0', 'Version 2.0']); }); it('should handle empty response from productService', () => { component.versions.set([]); - productServiceMock.sendRequestToGetProductVersionsForDesigner.and.returnValue(of([])); + productServiceMock.sendRequestToGetProductVersionsForDesigner.and.returnValue( + of([]) + ); // Act component.getVersionInDesigner(); // Assert - expect(productServiceMock.sendRequestToGetProductVersionsForDesigner).toHaveBeenCalledWith(productId); + expect( + productServiceMock.sendRequestToGetProductVersionsForDesigner + ).toHaveBeenCalledWith(productId); expect(component.versions()).toEqual([]); }); it('should return the correct tracking environment based on the action type', () => { const testCases = [ - { actionType: ProductDetailActionType.STANDARD, expected: MATOMO_TRACKING_ENVIRONMENT.standard }, - { actionType: ProductDetailActionType.DESIGNER_ENV, expected: MATOMO_TRACKING_ENVIRONMENT.designerEnv }, - { actionType: ProductDetailActionType.CUSTOM_SOLUTION, expected: MATOMO_TRACKING_ENVIRONMENT.customSolution }, + { + actionType: ProductDetailActionType.STANDARD, + expected: MATOMO_TRACKING_ENVIRONMENT.standard + }, + { + actionType: ProductDetailActionType.DESIGNER_ENV, + expected: MATOMO_TRACKING_ENVIRONMENT.designerEnv + }, + { + actionType: ProductDetailActionType.CUSTOM_SOLUTION, + expected: MATOMO_TRACKING_ENVIRONMENT.customSolution + } ]; - + testCases.forEach(({ actionType, expected }) => { component.actionType = actionType; @@ -335,9 +353,8 @@ describe('ProductDetailVersionActionComponent', () => { }); }); - it('should return empty environment when action type is default', () => { + it('should return empty environment when action type is default', () => { const result = component.getTrackingEnvironmentBasedOnActionType(); - expect(result).toBe(''); }); }); diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.spec.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.spec.ts index 969ed839..74920923 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.spec.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.spec.ts @@ -20,7 +20,8 @@ import { MOCK_PRODUCT_DETAIL, MOCK_CRON_JOB_PRODUCT_DETAIL, MOCK_PRODUCT_MODULE_CONTENT, - MOCK_PRODUCTS + MOCK_PRODUCTS, + MOCK_FEEDBACK_API_RESPONSE } from '../../../shared/mocks/mock-data'; import { ProductService } from '../product.service'; import { ProductDetailComponent } from './product-detail.component'; @@ -31,6 +32,13 @@ import { ProductDetailActionType } from '../../../shared/enums/product-detail-ac import { LanguageService } from '../../../core/services/language/language.service'; import { Language } from '../../../shared/enums/language.enum'; import { MatomoTestingModule } from 'ngx-matomo-client/testing'; +import { AuthService } from '../../../auth/auth.service'; +import { AppModalService } from '../../../shared/services/app-modal.service'; +import { ProductFeedbackService } from './product-detail-feedback/product-feedbacks-panel/product-feedback.service'; +import { ProductStarRatingService } from './product-detail-feedback/product-star-rating-panel/product-star-rating.service'; +import { FeedbackApiResponse } from '../../../shared/models/apis/feedback-response.model'; +import { StarRatingCounting } from '../../../shared/models/star-rating-counting.model'; +import { Feedback } from '../../../shared/models/feedback.model'; const products = MOCK_PRODUCTS._embedded.products; declare const viewport: Viewport; @@ -41,6 +49,10 @@ describe('ProductDetailComponent', () => { let routingQueryParamService: jasmine.SpyObj; let languageService: jasmine.SpyObj; let titleService: Title; + let mockProductFeedbackService: jasmine.SpyObj; + let mockProductStarRatingService: jasmine.SpyObj; + let mockAuthService: jasmine.SpyObj; + let mockAppModalService: jasmine.SpyObj; beforeEach(async () => { const routingQueryParamServiceSpy = jasmine.createSpyObj( @@ -48,9 +60,27 @@ describe('ProductDetailComponent', () => { ['getDesignerVersionFromSessionStorage', 'isDesignerEnv'] ); - const languageServiceSpy = jasmine.createSpyObj( - 'LanguageService', - ['selectedLanguage'] + const languageServiceSpy = jasmine.createSpyObj('LanguageService', [ + 'selectedLanguage' + ]); + + mockProductFeedbackService = jasmine.createSpyObj( + 'ProductFeedbackService', + [ + 'getInitFeedbacksObservable', + 'findProductFeedbacksByCriteria', + 'handleFeedbackApiResponse', + 'findProductFeedbackOfUser', + 'totalElements' + ] + ); + mockAuthService = jasmine.createSpyObj('AuthService', ['getToken']); + mockAppModalService = jasmine.createSpyObj('AppModalService', [ + 'openAddFeedbackDialog' + ]); + mockProductStarRatingService = jasmine.createSpyObj( + 'ProductStarRatingService', + ['getRatingObservable', 'starRatings', 'totalComments', 'reviewNumber'] ); await TestBed.configureTestingModule({ @@ -61,15 +91,24 @@ describe('ProductDetailComponent', () => { MatomoTestingModule.forRoot() ], providers: [ + ProductStarRatingService, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), + { + provide: ProductStarRatingService, + useValue: mockProductStarRatingService + }, + { + provide: ProductFeedbackService, + useValue: mockProductFeedbackService + }, + { provide: AuthService, useValue: mockAuthService }, + { provide: AppModalService, useValue: mockAppModalService }, { provide: ActivatedRoute, useValue: { - snapshot: { - params: { id: products[0].id }, - queryParams: { type: TypeOption.CONNECTORS } - }, + snapshot: { params: { id: products[0].id } }, + queryParams: of({ type: TypeOption.CONNECTORS }), fragment: of('description') } }, @@ -100,6 +139,20 @@ describe('ProductDetailComponent', () => { ) as jasmine.SpyObj; titleService = TestBed.inject(Title); + mockProductFeedbackService.getInitFeedbacksObservable.and.returnValue( + of([MOCK_FEEDBACK_API_RESPONSE] as any as FeedbackApiResponse) + ); + + mockProductFeedbackService.findProductFeedbackOfUser.and.returnValue( + of({} as any as Feedback) + ); + mockAppModalService.openAddFeedbackDialog.and.returnValue( + Promise.resolve() + ); + mockAuthService.getToken.and.returnValue('token'); + mockProductStarRatingService.getRatingObservable.and.returnValue( + of([] as any as StarRatingCounting[]) + ); }); beforeEach(() => { @@ -115,9 +168,7 @@ describe('ProductDetailComponent', () => { }); it('should have title like the name DE', () => { - languageService.selectedLanguage.and.returnValue( - Language.DE - ); + languageService.selectedLanguage.and.returnValue(Language.DE); component.updateWebBrowserTitle(); fixture.detectChanges(); @@ -193,8 +244,7 @@ describe('ProductDetailComponent', () => { fixture.detectChanges(); const description = fixture.debugElement.query(By.css('#description')); expect(description).toBeFalsy(); - } - ) + }); it('should return true for description when in EN language it is not null and not undefined and not empty', () => { const mockContent: ProductModuleContent = { @@ -204,9 +254,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('description')).toBeTrue(); @@ -220,9 +268,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('description')).toBeTrue(); @@ -236,9 +282,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('description')).toBeTrue(); @@ -247,14 +291,12 @@ describe('ProductDetailComponent', () => { it('should return true for description when in DE language it is undefined but in EN language it has value', () => { const mockContent: ProductModuleContent = { ...MOCK_PRODUCT_MODULE_CONTENT, - description: { en: 'Test description'} + description: { en: 'Test description' } }; const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('description')).toBeTrue(); @@ -286,9 +328,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('description')).toBeFalse(); @@ -297,14 +337,12 @@ describe('ProductDetailComponent', () => { it('should return false for description when in EN language it is undefined', () => { const mockContent: ProductModuleContent = { ...MOCK_PRODUCT_MODULE_CONTENT, - description: { de: "Test description" } + description: { de: 'Test description' } }; const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('description')).toBeFalse(); @@ -318,9 +356,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('description')).toBeFalse(); @@ -334,9 +370,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('description')).toBeFalse(); @@ -350,9 +384,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('setup')).toBeTrue(); @@ -366,9 +398,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('setup')).toBeTrue(); @@ -382,9 +412,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('setup')).toBeTrue(); @@ -393,14 +421,12 @@ describe('ProductDetailComponent', () => { it('should return true for setup when in DE language it is undefined but in EN language it has value', () => { const mockContent: ProductModuleContent = { ...MOCK_PRODUCT_MODULE_CONTENT, - setup: { en: 'Test setup'} + setup: { en: 'Test setup' } }; const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('setup')).toBeTrue(); @@ -416,14 +442,12 @@ describe('ProductDetailComponent', () => { it('should return false for setup when in EN language it is an empty string', () => { const mockContent: ProductModuleContent = { ...MOCK_PRODUCT_MODULE_CONTENT, - setup: { en: '', de: "Test setup" } + setup: { en: '', de: 'Test setup' } }; const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('setup')).toBeFalse(); @@ -432,14 +456,12 @@ describe('ProductDetailComponent', () => { it('should return false for setup when in EN language it is undefined', () => { const mockContent: ProductModuleContent = { ...MOCK_PRODUCT_MODULE_CONTENT, - setup: { de: "Test setup" } + setup: { de: 'Test setup' } }; const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('setup')).toBeFalse(); @@ -453,9 +475,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('setup')).toBeFalse(); @@ -469,9 +489,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('setup')).toBeFalse(); @@ -485,9 +503,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('demo')).toBeTrue(); @@ -501,9 +517,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('demo')).toBeTrue(); @@ -517,9 +531,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('demo')).toBeTrue(); @@ -528,14 +540,12 @@ describe('ProductDetailComponent', () => { it('should return true for demo when in DE language it is undefined but in EN language it has value', () => { const mockContent: ProductModuleContent = { ...MOCK_PRODUCT_MODULE_CONTENT, - demo: { en: 'Test demo'} + demo: { en: 'Test demo' } }; const selectedLanguage = Language.DE; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('demo')).toBeTrue(); @@ -556,9 +566,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('demo')).toBeFalse(); @@ -567,14 +575,12 @@ describe('ProductDetailComponent', () => { it('should return false for demo when in EN language it is undefined', () => { const mockContent: ProductModuleContent = { ...MOCK_PRODUCT_MODULE_CONTENT, - demo: { de: "Test demo" } + demo: { de: 'Test demo' } }; const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('demo')).toBeFalse(); @@ -588,9 +594,7 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('demo')).toBeFalse(); @@ -604,15 +608,12 @@ describe('ProductDetailComponent', () => { const selectedLanguage = Language.EN; - languageService.selectedLanguage.and.returnValue( - selectedLanguage - ); + languageService.selectedLanguage.and.returnValue(selectedLanguage); component.productModuleContent.set(mockContent); expect(component.getContent('demo')).toBeFalse(); }); - it('should display dropdown horizontally on small viewport', () => { viewport.set(540); const tabGroup = fixture.debugElement.query(By.css('.row-tab')); @@ -737,7 +738,7 @@ describe('ProductDetailComponent', () => { it('should return DESIGNER_ENV as acction type in Designer Env', () => { routingQueryParamService.isDesignerEnv.and.returnValue(true); - component.updateProductDetailActionType({ sourceUrl: 'some-url'} as any); + component.updateProductDetailActionType({ sourceUrl: 'some-url' } as any); expect(component.productDetailActionType()).toBe( ProductDetailActionType.DESIGNER_ENV ); @@ -756,7 +757,6 @@ describe('ProductDetailComponent', () => { By.css('#app-product-installation-count-action') ); expect(installationCount).toBeFalsy(); - }); it('should return STANDARD as acction type when when productDetail.sourceUrl is defined', () => { @@ -771,7 +771,7 @@ describe('ProductDetailComponent', () => { it('displayed tabs array should have size 0 if product module content description, setup, demo, dependcy are all empty', () => { const mockContent: ProductModuleContent = { - ...MOCK_PRODUCT_MODULE_CONTENT, + ...MOCK_PRODUCT_MODULE_CONTENT }; component.productModuleContent.set(mockContent); @@ -780,14 +780,20 @@ describe('ProductDetailComponent', () => { it('should hide tab and tab content when all tabs have no content', () => { const mockContent: ProductModuleContent = { - ...MOCK_PRODUCT_MODULE_CONTENT, + ...MOCK_PRODUCT_MODULE_CONTENT }; component.productModuleContent.set(mockContent); const tabGroup = fixture.debugElement.query(By.css('.tab-group')); const tabs = tabGroup.query(By.css('.row-tab d-none d-xl-block col-12')); - const dropdown = tabGroup.query(By.css('.dropdown-tab d-block d-xl-none d-flex flex-row justify-content-center align-items-center w-100')); - const tabContent = tabGroup.query(By.css('.tab-content col-12 default-cursor')); + const dropdown = tabGroup.query( + By.css( + '.dropdown-tab d-block d-xl-none d-flex flex-row justify-content-center align-items-center w-100' + ) + ); + const tabContent = tabGroup.query( + By.css('.tab-content col-12 default-cursor') + ); expect(tabs).toBeFalsy(); expect(dropdown).toBeFalsy(); @@ -795,18 +801,32 @@ describe('ProductDetailComponent', () => { }); it('should generate right text for the rate connector', () => { - const rateConnector = fixture.debugElement.query(By.css('.rate-connector-btn')); - expect(rateConnector.childNodes[0].nativeNode.textContent).toContain("common.feedback.rateFeedbackForConnectorBtnLabel"); + const rateConnector = fixture.debugElement.query( + By.css('.rate-connector-btn') + ); + expect(rateConnector.childNodes[0].nativeNode.textContent).toContain( + 'common.feedback.rateFeedbackForConnectorBtnLabel' + ); - const rateConnectorEmptyText = fixture.debugElement.query(By.css('.rate-empty-text')); - expect(rateConnectorEmptyText.childNodes[0].nativeNode.textContent).toContain("common.feedback.noFeedbackForConnectorLabel"); + const rateConnectorEmptyText = fixture.debugElement.query( + By.css('.rate-empty-text') + ); + expect( + rateConnectorEmptyText.childNodes[0].nativeNode.textContent + ).toContain('common.feedback.noFeedbackForConnectorLabel'); component.route.snapshot.params['id'] = 'cronjob'; - spyOn(component, 'getProductById').and.returnValue(of(MOCK_CRON_JOB_PRODUCT_DETAIL)); + spyOn(component, 'getProductById').and.returnValue( + of(MOCK_CRON_JOB_PRODUCT_DETAIL) + ); component.ngOnInit(); fixture.detectChanges(); - expect(rateConnector.childNodes[0].nativeNode.textContent).toContain("common.feedback.rateFeedbackForUtilityBtnLabel"); - expect(rateConnectorEmptyText.childNodes[0].nativeNode.textContent).toContain("common.feedback.noFeedbackForUtilityLabel"); + expect(rateConnector.childNodes[0].nativeNode.textContent).toContain( + 'common.feedback.rateFeedbackForUtilityBtnLabel' + ); + expect( + rateConnectorEmptyText.childNodes[0].nativeNode.textContent + ).toContain('common.feedback.noFeedbackForUtilityLabel'); }); it('maven tab should not display when product module content is missing', () => { @@ -816,6 +836,7 @@ describe('ProductDetailComponent', () => { let mavenTab = fixture.debugElement.query( By.css('app-product-detail-maven-content') ); + expect(mavenTab).toBeTruthy(); component.productModuleContent.set({} as any as ProductModuleContent); fixture.detectChanges(); diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.ts index f8f6728b..1ebd42e8 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.ts @@ -149,7 +149,6 @@ export class ProductDetailComponent { } ngOnInit(): void { - this.loadingService.showLoading(LoadingComponentId.DETAIL_PAGE); this.router.navigate([], { relativeTo: this.route, queryParamsHandling: 'merge', @@ -158,29 +157,32 @@ export class ProductDetailComponent { const productId = this.route.snapshot.params[ROUTER.ID]; this.productDetailService.productId.set(productId); if (productId) { + this.loadingService.showLoading(LoadingComponentId.DETAIL_PAGE); forkJoin({ productDetail: this.getProductDetailObservable(productId), - productFeedBack: this.productFeedbackService.getInitFeedbacksObservable(), + productFeedBack: + this.productFeedbackService.getInitFeedbacksObservable(), rating: this.productStarRatingService.getRatingObservable(productId), - userFeedback: this.productFeedbackService.findProductFeedbackOfUser() + userFeedback: this.productFeedbackService.findProductFeedbackOfUser(), + params: this.route.queryParams }).subscribe(res => { this.handleProductDetail(res.productDetail); - this.productFeedbackService.handleFeedbackApiResponse(res.productFeedBack); - res.rating; + this.productFeedbackService.handleFeedbackApiResponse( + res.productFeedBack + ); this.updateDropdownSelection(); this.checkMediaSize(); - this.route.queryParams.subscribe(params => { - this.showPopup = params['showPopup'] === 'true'; - if (this.showPopup && this.authService.getToken()) { - this.appModalService - .openAddFeedbackDialog() - .then(() => this.removeQueryParam()) - .catch(() => this.removeQueryParam()); - } - }); + this.showPopup = res.params['showPopup'] === 'true'; + console.log(this.showPopup); + + if (this.showPopup && this.authService.getToken()) { + this.appModalService + .openAddFeedbackDialog() + .then(() => this.removeQueryParam()) + .catch(() => this.removeQueryParam()); + } this.loadingService.hideLoading(LoadingComponentId.DETAIL_PAGE); }); - } } @@ -193,7 +195,7 @@ export class ProductDetailComponent { return this.getProductById(productId, isShowDevVersion); } - handleProductDetail(productDetail: ProductDetail) { + handleProductDetail(productDetail: ProductDetail): void { this.productDetail.set(productDetail); this.productModuleContent.set(productDetail.productModuleContent); this.metaProductJsonUrl = productDetail.metaProductJsonUrl; @@ -215,15 +217,15 @@ export class ProductDetailComponent { } } - onClickingBackToHomepageButton() { + onClickingBackToHomepageButton(): void { this.router.navigate([API_URI.APP]); } - onLogoError() { + onLogoError(): void { this.logoUrl = DEFAULT_IMAGE_URL; } - handleProductContentVersion() { + handleProductContentVersion(): void { if (this.isEmptyProductContent()) { return; } @@ -232,7 +234,7 @@ export class ProductDetailComponent { ); } - updateProductDetailActionType(productDetail: ProductDetail) { + updateProductDetailActionType(productDetail: ProductDetail): void { if (productDetail?.sourceUrl === undefined) { this.productDetailActionType.set(ProductDetailActionType.CUSTOM_SOLUTION); } else if (this.routingQueryParamService.isDesignerEnv()) { @@ -242,7 +244,7 @@ export class ProductDetailComponent { } } - scrollToTop() { + scrollToTop(): void { window.scrollTo({ left: 0, top: 0, behavior: 'instant' }); } @@ -298,7 +300,6 @@ export class ProductDetailComponent { ), dependency: content.isDependency }; - return conditions[value] ?? false; } @@ -307,7 +308,7 @@ export class ProductDetailComponent { return !content || Object.keys(content).length === 0; } - loadDetailTabs(selectedVersion: string) { + loadDetailTabs(selectedVersion: string): void { let version = selectedVersion || this.productDetail().newestReleaseVersion; version = version.replace(VERSION.displayPrefix, ''); this.productService @@ -319,17 +320,17 @@ export class ProductDetailComponent { }); } - onTabChange(event: string) { + onTabChange(event: string): void { this.setActiveTab(event); this.isTabDropdownShown.update(value => !value); this.onTabDropdownShown(); } - getSelectedTabLabel() { + getSelectedTabLabel(): string { return CommonUtils.getLabel(this.activeTab, PRODUCT_DETAIL_TABS); } - updateDropdownSelection() { + updateDropdownSelection(): void { const dropdown = document.getElementById( 'tab-group-dropdown' ) as HTMLSelectElement; @@ -338,7 +339,7 @@ export class ProductDetailComponent { } } - setActiveTab(tab: string) { + setActiveTab(tab: string): void { this.activeTab = tab; const hash = '#tab-' + tab; const path = window.location.pathname; @@ -357,16 +358,16 @@ export class ProductDetailComponent { localStorage.setItem(STORAGE_ITEM, JSON.stringify(savedTab)); } - onShowInfoContent() { + onShowInfoContent(): void { this.isDropdownOpen.update(value => !value); } - onTabDropdownShown() { + onTabDropdownShown(): void { this.isTabDropdownShown.set(!this.isTabDropdownShown()); } @HostListener('document:click', ['$event']) - handleClickOutside(event: MouseEvent) { + handleClickOutside(event: MouseEvent): void { const formSelect = this.elementRef.nativeElement.querySelector('.form-select'); @@ -380,11 +381,11 @@ export class ProductDetailComponent { } @HostListener('window:resize', ['$event']) - onResize() { + onResize(): void { this.checkMediaSize(); } - checkMediaSize() { + checkMediaSize(): void { const mediaQuery = window.matchMedia('(max-width: 767px)'); if (mediaQuery.matches) { this.isMobileMode.set(true); @@ -393,7 +394,7 @@ export class ProductDetailComponent { } } - onClickRateBtn() { + onClickRateBtn(): void { const productId = this.productDetailService.productId(); if (this.authService.getToken()) { this.appModalService.openAddFeedbackDialog(); @@ -402,7 +403,7 @@ export class ProductDetailComponent { } } - receiveInstallationCountData(data: number) { + receiveInstallationCountData(data: number): void { this.installationCount = data; } @@ -421,7 +422,7 @@ export class ProductDetailComponent { } } - getDisplayedTabsSignal() { + getDisplayedTabsSignal(): ItemDropdown[] { this.updateWebBrowserTitle(); const displayedTabs: ItemDropdown[] = []; for (const detailTab of this.detailTabs) { @@ -449,7 +450,6 @@ export class ProductDetailComponent { productDetail.vendorImage = vendorImage || vendorImageDarkMode; productDetail.vendorImageDarkMode = vendorImageDarkMode || vendorImage; } - return productDetail; } } diff --git a/marketplace-ui/src/app/modules/product/product.service.spec.ts b/marketplace-ui/src/app/modules/product/product.service.spec.ts index fdac4432..fddf9522 100644 --- a/marketplace-ui/src/app/modules/product/product.service.spec.ts +++ b/marketplace-ui/src/app/modules/product/product.service.spec.ts @@ -20,25 +20,19 @@ describe('ProductService', () => { let products = MOCK_PRODUCTS._embedded.products; let service: ProductService; let httpMock: HttpTestingController; - let loadingServiceSpy: jasmine.SpyObj; beforeEach(() => { - const spyLoading = jasmine.createSpyObj('LoadingService', ['show', 'hide']); - TestBed.configureTestingModule({ imports: [], providers: [ ProductService, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), - { provide: LoadingService, useValue: spyLoading } + LoadingService ] }); service = TestBed.inject(ProductService); httpMock = TestBed.inject(HttpTestingController); - loadingServiceSpy = TestBed.inject( - LoadingService - ) as jasmine.SpyObj; }); it('should be created', () => { @@ -194,9 +188,6 @@ describe('ProductService', () => { expect(req.request.method).toBe('GET'); req.flush(mockResponse); - - expect(loadingServiceSpy.showLoading).not.toHaveBeenCalled(); - expect(loadingServiceSpy.hideLoading).not.toHaveBeenCalled(); }); it('getProductDetailsWithVersion should return a product detail', () => { diff --git a/marketplace-ui/src/app/modules/product/product.service.ts b/marketplace-ui/src/app/modules/product/product.service.ts index b6783daf..aaabb618 100644 --- a/marketplace-ui/src/app/modules/product/product.service.ts +++ b/marketplace-ui/src/app/modules/product/product.service.ts @@ -7,10 +7,7 @@ import { ProductApiResponse } from '../../shared/models/apis/product-response.mo import { Criteria } from '../../shared/models/criteria.model'; import { ProductDetail } from '../../shared/models/product-detail.model'; import { VersionData } from '../../shared/models/vesion-artifact.model'; -import { - SkipLoading, - LoadingComponent -} from '../../core/interceptors/api.interceptor'; +import { LoadingComponent } from '../../core/interceptors/api.interceptor'; import { VersionAndUrl } from '../../shared/models/version-and-url'; import { API_URI } from '../../shared/constants/api.constant'; import { LoadingComponentId } from '../../shared/enums/loading-component-id'; diff --git a/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.spec.ts b/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.spec.ts index aad88c55..4a7fde2c 100644 --- a/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.spec.ts +++ b/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { LoadingSpinnerComponent } from './loading-spinner.component'; +import { LoadingComponentId } from '../../enums/loading-component-id'; describe('LoadingSpinnerComponent', () => { let component: LoadingSpinnerComponent; @@ -16,25 +17,30 @@ describe('LoadingSpinnerComponent', () => { fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => expect(component).toBeTruthy()); - it('should apply position-fixed class when isFixPosition is true', () => { - const containerElement = fixture.debugElement.query(By.css('.spinner-container')); - expect(containerElement.nativeElement.classList.contains('position-fixed')).toBe(true); - expect(containerElement.nativeElement.classList.contains('position-absolute')).toBe(false); + it('should display when isLoading state is true', () => { + component.key = LoadingComponentId.LANDING_PAGE; + component.loadingService.showLoading(LoadingComponentId.LANDING_PAGE); + fixture.detectChanges(); + expect(component.isLoading()).toBeTrue(); }); - it('should apply position-absolute class when isFixPosition is false', () => { + it('should display when isLoading state is false', () => { + component.key = LoadingComponentId.LANDING_PAGE; + component.loadingService.hideLoading(LoadingComponentId.LANDING_PAGE); fixture.detectChanges(); - const containerElement = fixture.debugElement.query(By.css('.spinner-container')); - expect(containerElement.nativeElement.classList.contains('position-absolute')).toBe(true); - expect(containerElement.nativeElement.classList.contains('position-fixed')).toBe(false); + expect(component.isLoading()).toBeFalse(); }); - it('should contain a spinner-border element', () => { - const spinnerElement = fixture.debugElement.query(By.css('.spinner-border')); - expect(spinnerElement).toBeTruthy(); + it('container class should come from input', () => { + component.key = LoadingComponentId.LANDING_PAGE; + component.containerClasses = 'spinner-container'; + component.loadingService.showLoading(LoadingComponentId.LANDING_PAGE); + fixture.detectChanges(); + const containerElement = fixture.debugElement.query( + By.css('.spinner-container') + ); + expect(containerElement.nativeElement).toBeTruthy(); }); }); diff --git a/marketplace-ui/src/app/shared/mocks/mock-data.ts b/marketplace-ui/src/app/shared/mocks/mock-data.ts index cf319dc7..382329ef 100644 --- a/marketplace-ui/src/app/shared/mocks/mock-data.ts +++ b/marketplace-ui/src/app/shared/mocks/mock-data.ts @@ -1,7 +1,9 @@ +import { FeedbackApiResponse } from '../models/apis/feedback-response.model'; import { ProductApiResponse } from '../models/apis/product-response.model'; import { ExternalDocument } from '../models/external-document.model'; import { ProductDetail } from '../models/product-detail.model'; import { ProductModuleContent } from '../models/product-module-content.model'; +import { StarRatingCounting } from '../models/star-rating-counting.model'; export const MOCK_PRODUCTS = { _embedded: { @@ -226,7 +228,8 @@ export const MOCK_CRON_JOB_PRODUCT_DETAIL: ProductDetail = { de: 'Das Cron-Job-Utility übernimmt die automatische Verwaltung deiner zeitgesteuerten Aufgaben.', en: 'Cron Job Utility handles your scheduled jobs autonomously.' }, - logoUrl: 'https://raw.githubusercontent.com/axonivy-market/market/feature/MARP-463-Multilingualism-for-Website/market/utils/cronjob/logo.png', + logoUrl: + 'https://raw.githubusercontent.com/axonivy-market/market/feature/MARP-463-Multilingualism-for-Website/market/utils/cronjob/logo.png', type: 'util', tags: ['utils'], vendor: 'Axon Ivy AG', @@ -234,7 +237,8 @@ export const MOCK_CRON_JOB_PRODUCT_DETAIL: ProductDetail = { newestReleaseVersion: 'v10.0.4', cost: 'Free', sourceUrl: 'https://github.com/axonivy-market/cronjob', - statusBadgeUrl: 'https://github.com/axonivy-market/cronjob/actions/workflows/ci.yml/badge.svg', + statusBadgeUrl: + 'https://github.com/axonivy-market/cronjob/actions/workflows/ci.yml/badge.svg', language: 'English', industry: 'Cross-Industry', compatibility: '10.0+', @@ -246,10 +250,10 @@ export const MOCK_CRON_JOB_PRODUCT_DETAIL: ProductDetail = { en: '**Cron Job** is a job-firing schedule that recurs based on calendar-like notions.\n\nThe [Quartz framework](http://www.quartz-scheduler.org/) is used as underlying scheduler framework.\n\nWith Cron Job, you can specify firing-schedules such as “every Friday at noon”, or “every weekday and 9:30 am”, or even “every 5 minutes between 9:00 am and 10:00 am on every Monday, Wednesday and Friday during January”.\n\nFor more details about Cron Expressions please refer to [Lesson 6: CronTrigger](http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/tutorial-lesson-06.html)' }, setup: { - en: 'No special setup is needed for this demo. Only start the Engine and watch out the logging which will be updated every 5 seconds with the following logging entry:\n\n```\n\nCron Job ist started at: 2023-01-27 10:43:20.\n\n```', + en: 'No special setup is needed for this demo. Only start the Engine and watch out the logging which will be updated every 5 seconds with the following logging entry:\n\n```\n\nCron Job ist started at: 2023-01-27 10:43:20.\n\n```' }, demo: { - en: 'In this demo, the CronByGlobalVariableTriggerStartEventBean is defined as the Java class to be executed in the Ivy Program Start element.\n\n![Program Start Element screenshot](https://raw.githubusercontent.com/axonivy-market/cronjob/v10.0.4/cronjob-product/ProgramStartElement.png)\n\nThis bean gets a cron expression via the variable defined as Cron expression and it will schedule by using the expression.\n\n![custom editor UI screenshot](https://raw.githubusercontent.com/axonivy-market/cronjob/v10.0.4/cronjob-product/customEditorUI.png)\n\nFor this demo, the Cron expression is defining the time to start the cron that simply fires every 5 seconds.\n\n```\n\n demoStartCronPattern: 0/5 * * * * ?\n\n```', + en: 'In this demo, the CronByGlobalVariableTriggerStartEventBean is defined as the Java class to be executed in the Ivy Program Start element.\n\n![Program Start Element screenshot](https://raw.githubusercontent.com/axonivy-market/cronjob/v10.0.4/cronjob-product/ProgramStartElement.png)\n\nThis bean gets a cron expression via the variable defined as Cron expression and it will schedule by using the expression.\n\n![custom editor UI screenshot](https://raw.githubusercontent.com/axonivy-market/cronjob/v10.0.4/cronjob-product/customEditorUI.png)\n\nFor this demo, the Cron expression is defining the time to start the cron that simply fires every 5 seconds.\n\n```\n\n demoStartCronPattern: 0/5 * * * * ?\n\n```' }, isDependency: true, name: 'cron job', @@ -280,7 +284,8 @@ export const MOCK_PRODUCT_DETAIL: ProductDetail = { de: "TODO Atlassian's Jira connector lets you track issues directly from the Axon Ivy platform." }, installationCount: 1, - logoUrl: 'https://raw.githubusercontent.com/axonivy-market/market/master/market/connector/jira/logo.png', + logoUrl: + 'https://raw.githubusercontent.com/axonivy-market/market/master/market/connector/jira/logo.png', type: 'connector', tags: ['helper'], vendor: 'FROX AG', @@ -289,7 +294,8 @@ export const MOCK_PRODUCT_DETAIL: ProductDetail = { newestReleaseVersion: 'v10.0.0', cost: 'Free', sourceUrl: 'https://github.com/axonivy-market/jira-connector', - statusBadgeUrl: 'https://github.com/axonivy-market/jira-connector/actions/workflows/ci.yml/badge.svg', + statusBadgeUrl: + 'https://github.com/axonivy-market/jira-connector/actions/workflows/ci.yml/badge.svg', language: 'English', industry: 'Cross-Industry', compatibility: '9.2+', @@ -300,17 +306,17 @@ export const MOCK_PRODUCT_DETAIL: ProductDetail = { en: "Axon Ivy's [Atlassian Jira Connector ](https://www.atlassian.com/software/jira) gives you full power to track issues within your process work. The connector:\n\n- Features three main functionalities (create comment, create issue, and get issue).\n- Provides access to the core API of Atlassian Jira.\n- Supports you with an easy-to-copy demo implementation to reduce your integration effort.\n- Enables low code citizen developers to integrate issue tracking tools without writing a single line of code." }, setup: { - en: 'Open the `Config/variables.yaml` in your Axon Ivy Designer and paste the\ncode below and adjust the values to your environment.\n\n```\nVariables:\n\n jira-connector:\n \n # Url to the Jira server\n Url: "https://localhost"\n\n # Username to connect to the Jira server\n Username: "admin"\n\n # Password to connect to the Jira server\n Password: "1234"\n```', + en: 'Open the `Config/variables.yaml` in your Axon Ivy Designer and paste the\ncode below and adjust the values to your environment.\n\n```\nVariables:\n\n jira-connector:\n \n # Url to the Jira server\n Url: "https://localhost"\n\n # Username to connect to the Jira server\n Username: "admin"\n\n # Password to connect to the Jira server\n Password: "1234"\n```' }, demo: { - en: '![jira-connector Demo 1](https://raw.githubusercontent.com/axonivy-market/jira-connector/v10.0.0/jira-connector-product/images/create-issue.png "Create Jira issue")\n![jira-connector Demo 2](https://raw.githubusercontent.com/axonivy-market/jira-connector/v10.0.0/jira-connector-product/images/create-comment.png "Craete Jira comment")', + en: '![jira-connector Demo 1](https://raw.githubusercontent.com/axonivy-market/jira-connector/v10.0.0/jira-connector-product/images/create-issue.png "Create Jira issue")\n![jira-connector Demo 2](https://raw.githubusercontent.com/axonivy-market/jira-connector/v10.0.0/jira-connector-product/images/create-comment.png "Craete Jira comment")' }, isDependency: true, name: 'Jira Connector', groupId: 'com.axonivy.connector.jira', artifactId: 'jira-connector', type: 'iar', - productId: 'jira-connector', + productId: 'jira-connector' }, mavenDropins: false, _links: { @@ -329,3 +335,30 @@ export const MOCK_EXTERNAL_DOCUMENT: ExternalDocument = { artifactName: 'Portal Guide', relativeLink: '/market-cache/portal/portal-guide/10.0.0/doc/index.html' }; + +export const MOCK_FEEDBACK_API_RESPONSE: FeedbackApiResponse = { + _embedded: { + feedbacks: [ + { + content: 'cool stuff', + rating: 5, + productId: 'portal' + } + ] + }, + _links: { + self: { href: '/feedbacks' } + }, + page: { + size: 10, + totalElements: 1, + totalPages: 1, + number: 0 + } +}; + +export const MOCK_RATING_STAR: StarRatingCounting[] = [ + { starRating: 0 }, + { starRating: 4 }, + { starRating: 3 } +]; diff --git a/marketplace-ui/src/app/shared/services/app-modal.service.ts b/marketplace-ui/src/app/shared/services/app-modal.service.ts index 999b2d5f..332a285b 100644 --- a/marketplace-ui/src/app/shared/services/app-modal.service.ts +++ b/marketplace-ui/src/app/shared/services/app-modal.service.ts @@ -10,7 +10,7 @@ import { SuccessDialogComponent } from '../../modules/product/product-detail/pro export class AppModalService { private readonly modalService = inject(NgbModal); - openShowFeedbacksDialog() { + openShowFeedbacksDialog(): void { this.modalService.open(ShowFeedbacksDialogComponent, { centered: true, modalDialogClass: 'show-feedbacks-modal-dialog', @@ -18,7 +18,7 @@ export class AppModalService { }); } - openAddFeedbackDialog() { + openAddFeedbackDialog(): Promise { const addFeedbackDialog = this.modalService.open( AddFeedbackDialogComponent, { @@ -30,7 +30,7 @@ export class AppModalService { return addFeedbackDialog.result; } - openSuccessDialog() { + openSuccessDialog(): void { this.modalService.open(SuccessDialogComponent, { fullscreen: 'md', centered: true,