diff --git a/blogs-analyzer-ui/.gitignore b/blogs-analyzer-ui/.gitignore index 9ed5ea9..ef65b22 100644 --- a/blogs-analyzer-ui/.gitignore +++ b/blogs-analyzer-ui/.gitignore @@ -42,3 +42,4 @@ testem.log Thumbs.db package-lock.json .editorconfig +.scannerwork \ No newline at end of file diff --git a/blogs-analyzer-ui/sonar-project.properties b/blogs-analyzer-ui/sonar-project.properties index 5a1aa63..2b420f3 100644 --- a/blogs-analyzer-ui/sonar-project.properties +++ b/blogs-analyzer-ui/sonar-project.properties @@ -6,6 +6,6 @@ sonar.host.url=https://sonarcloud.io sonar.organization=nashtech sonar.projectKey=blogs-analyzer-ui sonar.projectName=blogs-analyzer-ui -sonar.exclusions=**/node_modules/**,**/*.html,**/*.scss,**/*.spec.ts,**/*.css,**/models/**,**/assets/** +sonar.exclusions=**/node_modules/**,**/*.html,**/*.scss,**/*.spec.ts,**/*.css,**/models/**,**/assets/**,**/environments/** sonar.javascript.lcov.reportPaths=coverage/lcov.info sonar.token=013631d793a41fe3c4b7d4fefb99224b542318c7 \ No newline at end of file diff --git a/blogs-analyzer-ui/src/app/dashboard/components/home/home.component.spec.ts b/blogs-analyzer-ui/src/app/dashboard/components/home/home.component.spec.ts index 69c0d6f..3ee34d8 100644 --- a/blogs-analyzer-ui/src/app/dashboard/components/home/home.component.spec.ts +++ b/blogs-analyzer-ui/src/app/dashboard/components/home/home.component.spec.ts @@ -5,23 +5,76 @@ import { RouterTestingModule } from "@angular/router/testing"; import { HttpClientModule } from "@angular/common/http"; import { MatCardModule } from "@angular/material/card"; import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { BlogService } from "../../../services/blog.service"; +import { Router } from "@angular/router"; +import { of } from "rxjs"; describe('HomeComponent', () => { let component: HomeComponent; let fixture: ComponentFixture; + let blogService: jasmine.SpyObj; + let router: jasmine.SpyObj; beforeEach(() => { + const blogServiceSpy = jasmine.createSpyObj('BlogService', ['searchPostsByTitle', 'getPostById', 'getPostByAuthorId']); + const routerSpy = jasmine.createSpyObj('Router', ['navigate']); + TestBed.configureTestingModule({ declarations: [HomeComponent], imports: [RouterTestingModule, HttpClientModule, MatCardModule], + providers: [ + { provide: BlogService, useValue: blogServiceSpy }, + { provide: Router, useValue: routerSpy } + ], schemas: [NO_ERRORS_SCHEMA] }); + fixture = TestBed.createComponent(HomeComponent); component = fixture.componentInstance; + blogService = TestBed.inject(BlogService) as jasmine.SpyObj; + router = TestBed.inject(Router) as jasmine.SpyObj; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call searchPostsByTitle and navigate on searchByTitle', () => { + const mockData = [{ id: 1, title: 'Test Post' }]; + blogService.searchPostsByTitle.and.returnValue(of(mockData)); + + component.searchTitle = 'Test'; + component.searchByTitle(); + + expect(blogService.searchPostsByTitle).toHaveBeenCalledWith('Test'); + expect(router.navigate).toHaveBeenCalledWith(['/quality-check'], { state: { data: mockData } }); + }); + + it('should not call searchPostsByTitle if searchTitle is empty', () => { + component.searchTitle = ''; + component.searchByTitle(); + + expect(blogService.searchPostsByTitle).not.toHaveBeenCalled(); + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should call getPostById and navigate on getBlogById', () => { + const mockData = { id: 1, title: 'Test Post' }; + blogService.getPostById.and.returnValue(of(mockData)); + + component.blogId = 1; + component.getBlogById(); + + expect(blogService.getPostById).toHaveBeenCalledWith(1); + expect(router.navigate).toHaveBeenCalledWith(['/quality-check'], { state: { data: mockData } }); + }); + + it('should not call getPostById if blogId is not set', () => { + component.blogId = 0; + component.getBlogById(); + + expect(blogService.getPostById).not.toHaveBeenCalled(); + expect(router.navigate).not.toHaveBeenCalled(); + }); }); diff --git a/blogs-analyzer-ui/src/app/dashboard/components/home/home.component.ts b/blogs-analyzer-ui/src/app/dashboard/components/home/home.component.ts index 79bb8b2..3874b32 100644 --- a/blogs-analyzer-ui/src/app/dashboard/components/home/home.component.ts +++ b/blogs-analyzer-ui/src/app/dashboard/components/home/home.component.ts @@ -15,9 +15,6 @@ export class HomeComponent { constructor(private blogService: BlogService, private router: Router) { } - ngOnInit(): void { - } - searchByTitle() { if (this.searchTitle) { this.blogService.searchPostsByTitle(this.searchTitle).subscribe((data: any[]) => { diff --git a/blogs-analyzer-ui/src/app/dashboard/components/tabular-view/tabular-view.component.spec.ts b/blogs-analyzer-ui/src/app/dashboard/components/tabular-view/tabular-view.component.spec.ts index ed80d9a..fd25c14 100644 --- a/blogs-analyzer-ui/src/app/dashboard/components/tabular-view/tabular-view.component.spec.ts +++ b/blogs-analyzer-ui/src/app/dashboard/components/tabular-view/tabular-view.component.spec.ts @@ -1,10 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { TabularViewComponent } from './tabular-view.component'; -import { RouterTestingModule } from "@angular/router/testing"; -import { HttpClientModule } from "@angular/common/http"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { MatCardModule } from "@angular/material/card"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { HttpClientModule } from "@angular/common/http"; +import { RouterTestingModule } from "@angular/router/testing"; describe('TabularViewComponent', () => { let component: TabularViewComponent; @@ -24,4 +23,41 @@ describe('TabularViewComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should change page and fetch posts on page change', () => { + spyOn(component, 'fetchPosts'); + component.onPageChange(2); + + expect(component.currentPage).toBe(2); + expect(component.fetchPosts).toHaveBeenCalledWith(2); + }); + + it('should not change page if page is invalid', () => { + spyOn(component, 'fetchPosts'); + + component.onPageChange(0); + expect(component.currentPage).toBe(1); + expect(component.fetchPosts).not.toHaveBeenCalled(); + + component.isLastPage = true; + component.currentPage = 1; + component.onPageChange(2); + expect(component.currentPage).toBe(1); + expect(component.fetchPosts).not.toHaveBeenCalled(); + }); + + it('should navigate to the first page', () => { + spyOn(component, 'onPageChange'); + component.navigateToFirstPage(); + + expect(component.onPageChange).toHaveBeenCalledWith(1); + }); + + it('should navigate to the last page', () => { + component.totalPages = 5; + spyOn(component, 'onPageChange'); + component.navigateToLastPage(); + + expect(component.onPageChange).toHaveBeenCalledWith(5); + }); }); diff --git a/blogs-analyzer-ui/src/app/dashboard/components/tabular-view/tabular-view.component.ts b/blogs-analyzer-ui/src/app/dashboard/components/tabular-view/tabular-view.component.ts index 5cfb371..7d3195a 100644 --- a/blogs-analyzer-ui/src/app/dashboard/components/tabular-view/tabular-view.component.ts +++ b/blogs-analyzer-ui/src/app/dashboard/components/tabular-view/tabular-view.component.ts @@ -9,7 +9,7 @@ import { BlogService } from "../../../services/blog.service"; }) export class TabularViewComponent implements OnInit { protected columnDefs: any[]; - protected rowData: any[]; + rowData: any[]; loading: boolean = true; currentPage: number = 1; pageSize: number = 10; @@ -17,7 +17,7 @@ export class TabularViewComponent implements OnInit { totalPages: number = 1; @Output() clickEvent = new EventEmitter(); - constructor(private blogService: BlogService, private router: Router) { + constructor(public blogService: BlogService, public router: Router) { this.columnDefs = [ {headerName: 'Blog ID', field: 'id', width: 100}, {headerName: 'Title', field: 'title.rendered', flex: 2}, @@ -59,8 +59,8 @@ export class TabularViewComponent implements OnInit { fetchPosts(page: number): void { this.loading = true; - this.blogService.getAllPosts(page, this.pageSize).subscribe( - (data: any) => { + this.blogService.getAllPosts(page, this.pageSize).subscribe({ + next: (data: any) => { this.rowData = data.posts.map((post: any) => { return { id: post.id, @@ -75,11 +75,11 @@ export class TabularViewComponent implements OnInit { this.isLastPage = data.isLastPage; this.loading = false; }, - (error: any) => { + error: (error: any) => { console.error('Error fetching posts:', error); this.loading = false; } - ); + }); } onPageChange(page: number): void { diff --git a/blogs-analyzer-ui/src/app/quality-check/quality-check.component.spec.ts b/blogs-analyzer-ui/src/app/quality-check/quality-check.component.spec.ts index 45da67e..4523d3c 100644 --- a/blogs-analyzer-ui/src/app/quality-check/quality-check.component.spec.ts +++ b/blogs-analyzer-ui/src/app/quality-check/quality-check.component.spec.ts @@ -1,28 +1,100 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { QualityCheckComponent } from './quality-check.component'; import { RouterTestingModule } from "@angular/router/testing"; import { HttpClientModule } from "@angular/common/http"; import { HeaderComponent } from "../dashboard/components/header/header.component"; import { MatIconModule } from "@angular/material/icon"; import { MatCardModule } from "@angular/material/card"; +import { BlogService } from "../services/blog.service"; +import { of, throwError } from 'rxjs'; describe('QualityCheckComponent', () => { let component: QualityCheckComponent; let fixture: ComponentFixture; + let blogService: BlogService; beforeEach(() => { TestBed.configureTestingModule({ declarations: [QualityCheckComponent, HeaderComponent], - imports: [RouterTestingModule, HttpClientModule, MatIconModule, MatCardModule] - + imports: [RouterTestingModule, HttpClientModule, MatIconModule, MatCardModule], + providers: [BlogService] }); fixture = TestBed.createComponent(QualityCheckComponent); component = fixture.componentInstance; + blogService = TestBed.inject(BlogService); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call location.back on goBack', () => { + const locationSpy = spyOn(component['location'], 'back'); + component.goBack(); + expect(locationSpy).toHaveBeenCalled(); + }); + + it('should call blogService.getBlogQuality with correct prompt on checkQuality', () => { + component.postData = 'Sample blog content'; + const expectedPrompt = `Review blog with the following content: Sample blog content + Parameters include fields like: + - Duplicate Content +- Spelling Mistakes +- Grammatical Errors +- Overall SEO Report +- Accuracy +- Depth and Completeness +- Clarity and Conciseness +- Logical Flow +- Technical Accuracy +- Targeted Audience +- Structure and Formatting +- Code Examples and Illustrations +- Links and References +- Overall Feedback + Display result in tabular view for respective percentages and accurate feedback;`; + + spyOn(blogService, 'getBlogQuality').and.returnValue(of('')); + component.checkQuality(); + expect(blogService.getBlogQuality).toHaveBeenCalledWith(expectedPrompt); + }); + + it('should parse a valid response correctly', () => { + const response = ` + | Label | Percentage | Comment | + | Duplicate Content | 10% | Some duplicate content | + | Spelling Mistakes | 5% | Some spelling mistakes |`; + const expectedResults = [ + { originalLabel: 'Duplicate Content', oppositeLabel: 'Original Content', value: 10, comment: 'Some duplicate content' }, + { originalLabel: 'Spelling Mistakes', oppositeLabel: 'Correct Spelling', value: 5, comment: 'Some spelling mistakes' } + ]; + const results = component.parseResponse(response); + expect(results).toEqual(expectedResults); + }); + + it('should handle an empty response', () => { + const response = ''; + const results = component.parseResponse(response); + expect(results).toEqual([]); + }); + + it('should handle a response with partially valid rows', () => { + const response = ` + | Label | Percentage | Comment | + | Duplicate Content | 10% | Some duplicate content | + | Invalid Row`; + const expectedResults = [ + { originalLabel: 'Duplicate Content', oppositeLabel: 'Original Content', value: 10, comment: 'Some duplicate content' } + ]; + const results = component.parseResponse(response); + expect(results).toEqual(expectedResults); + }); + + it('should handle blogService.getBlogQuality error', () => { + spyOn(blogService, 'getBlogQuality').and.returnValue(throwError('Error occurred')); + const consoleSpy = spyOn(console, 'error'); + component.checkQuality(); + expect(consoleSpy).toHaveBeenCalledWith('Error:', 'Error occurred'); + }); }); diff --git a/blogs-analyzer-ui/src/app/quality-check/quality-check.component.ts b/blogs-analyzer-ui/src/app/quality-check/quality-check.component.ts index 5704974..10ab0e5 100644 --- a/blogs-analyzer-ui/src/app/quality-check/quality-check.component.ts +++ b/blogs-analyzer-ui/src/app/quality-check/quality-check.component.ts @@ -39,20 +39,19 @@ export class QualityCheckComponent { } checkQuality() { - const prompt = `Review blog with the following content: ${this.postData} Parameters include fields like: ${this.labels.map(label => `- ${label.actual}`).join('\n')} Display result in tabular view for respective percentages and accurate feedback;`; - this.blogService.getBlogQuality(prompt).subscribe( - response => { + this.blogService.getBlogQuality(prompt).subscribe({ + next: response => { this.qualityResults = this.parseResponse(response); }, - error => { + error: error => { console.error('Error:', error); } - ); + }); } parseResponse(response: string): { originalLabel: string; oppositeLabel: string; value: number; comment: string }[] { diff --git a/blogs-analyzer-ui/src/app/report/report.component.spec.ts b/blogs-analyzer-ui/src/app/report/report.component.spec.ts index c39ed83..286ee94 100644 --- a/blogs-analyzer-ui/src/app/report/report.component.spec.ts +++ b/blogs-analyzer-ui/src/app/report/report.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ReportComponent } from './report.component'; -import { HighchartsChartModule } from "highcharts-angular"; +import { HighchartsChartModule } from 'highcharts-angular'; +import * as Highcharts from 'highcharts'; describe('ReportComponent', () => { let component: ReportComponent; @@ -20,4 +20,55 @@ describe('ReportComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should initialize with default values', () => { + expect(component.actualLabel).toEqual([]); + expect(component.oppositeLabel).toEqual([]); + expect(component.chartData).toEqual([]); + expect(component.chartTitle).toBe(''); + }); + + it('should update chart options on ngOnInit', () => { + spyOn(component, 'updateChartOptions'); + component.ngOnInit(); + expect(component.updateChartOptions).toHaveBeenCalled(); + }); + + it('should update chart options on ngOnChanges', () => { + spyOn(component, 'updateChartOptions'); + component.ngOnChanges(); + expect(component.updateChartOptions).toHaveBeenCalled(); + }); + + it('should set chart options correctly in updateChartOptions', () => { + component.chartData = [30, 70]; + component.actualLabel = ['Actual1', 'Opposite1']; + component.oppositeLabel = ['Actual2', 'Opposite2']; + component.chartTitle = 'Test Chart'; + + component.updateChartOptions(); + + const expectedChartOptions: Highcharts.Options = { + chart: { type: 'pie' }, + title: { text: 'Test Chart' }, + plotOptions: { + pie: { + innerSize: '50%', + dataLabels: { enabled: true } + } + }, + series: [{ + type: 'pie', + data: [ + { name: 'Actual1', y: 30 }, + { name: 'Opposite1', y: 70 }, + { name: 'Actual2', y: 70 }, + { name: 'Opposite2', y: 30 } + ] + }], + tooltip: { pointFormat: '{point.percentage:.1f}%' } + }; + + expect(component.chartOptions).toEqual(expectedChartOptions); + }); }); diff --git a/blogs-analyzer-ui/src/app/report/report.component.ts b/blogs-analyzer-ui/src/app/report/report.component.ts index c73dce8..e99426c 100644 --- a/blogs-analyzer-ui/src/app/report/report.component.ts +++ b/blogs-analyzer-ui/src/app/report/report.component.ts @@ -1,6 +1,6 @@ -import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, Input, OnChanges } from '@angular/core'; import * as Highcharts from 'highcharts'; -import { Options } from "highcharts"; +import { Options } from 'highcharts'; @Component({ selector: 'app-report', diff --git a/blogs-analyzer-ui/src/app/services/blog.service.ts b/blogs-analyzer-ui/src/app/services/blog.service.ts index 0e856ae..a18caaf 100644 --- a/blogs-analyzer-ui/src/app/services/blog.service.ts +++ b/blogs-analyzer-ui/src/app/services/blog.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpParams } from '@angular/common/http'; +import { HttpClient } from '@angular/common/http'; import { map, Observable } from 'rxjs'; import { environment } from '../../environments/environment'; @@ -42,6 +42,6 @@ export class BlogService { getBlogQuality(prompt: string): Observable { const url = `${this.baseUrl}/gemini/v1/review`; - return this.http.post(url, prompt, { responseType: 'text' }); + return this.http.post(url, prompt, {responseType: 'text'}); } }