From 9b9e02f299a6777a91a761f5611090b3723bd542 Mon Sep 17 00:00:00 2001 From: Himanshu Saini Date: Sun, 26 Jul 2026 22:55:23 +0530 Subject: [PATCH 1/2] modified --- src/app/common/calculator-test.spec.ts | 18 +++++++++++ src/app/common/calculator.spec.ts | 4 +-- src/app/hello-world/hello-world-test.spec.ts | 32 ++++++++++++++++++++ src/app/hello-world/hello-world.spec.ts | 1 + 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/app/common/calculator-test.spec.ts create mode 100644 src/app/hello-world/hello-world-test.spec.ts diff --git a/src/app/common/calculator-test.spec.ts b/src/app/common/calculator-test.spec.ts new file mode 100644 index 0000000..2434cd5 --- /dev/null +++ b/src/app/common/calculator-test.spec.ts @@ -0,0 +1,18 @@ +import {describe, expect, it, vi} from 'vitest'; +import {calculator} from './calculator'; + +describe("Calculator teste suite",()=>{ + it("Spy on calculator",()=>{ + const spyOnCal = vi.spyOn(calculator,'add') + const res = calculator.add(1,1); + expect(res).toBe(2) + expect(spyOnCal).toHaveBeenCalledTimes(1) + }) + + it("Using mock on cal",()=>{ + const spyOnCal = vi.spyOn(calculator,'add').mockReturnValue(5); + const res = calculator.add(1,1) + expect(spyOnCal).toHaveBeenCalledOnce() + expect(res).toBe(5) + }) +}) diff --git a/src/app/common/calculator.spec.ts b/src/app/common/calculator.spec.ts index 9d6a8e3..9eb5ba5 100644 --- a/src/app/common/calculator.spec.ts +++ b/src/app/common/calculator.spec.ts @@ -9,7 +9,7 @@ describe("Vitest Fundamentals", () => { expect(result).toBe(5); }) - it("shows how spies work", () => { + it.skipIf(false)("shows how spies work", () => { const spy = vi.spyOn(calculator, "add"); const result = calculator.add(2, 3); expect(result).toBe(5); @@ -17,7 +17,7 @@ describe("Vitest Fundamentals", () => { expect(spy).toHaveBeenCalledWith(2, 3); }) - it("shows how mocking works", () => { + it.only("shows how mocking works", () => { const spy = vi.spyOn(calculator, "add").mockReturnValue(5); const result = calculator.add(2, 3); expect(result).toBe(5); diff --git a/src/app/hello-world/hello-world-test.spec.ts b/src/app/hello-world/hello-world-test.spec.ts new file mode 100644 index 0000000..5d823c0 --- /dev/null +++ b/src/app/hello-world/hello-world-test.spec.ts @@ -0,0 +1,32 @@ +import {beforeEach, describe, expect,it} from 'vitest'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HelloWorld} from './hello-world'; +import {DebugElement} from '@angular/core'; + +describe("Hello world",()=>{ + let fixture:ComponentFixture + let debug:DebugElement; + let el:HTMLElement; + let component:HelloWorld; + + beforeEach(async ()=>{ + await TestBed.configureTestingModule({ + imports:[HelloWorld] + }).compileComponents(); + fixture = TestBed.createComponent(HelloWorld); + debug = fixture.debugElement; + el = debug.nativeElement; + component = fixture.componentInstance; + fixture.detectChanges() + }) + + it("verify component creation",()=>{ + expect(component).toBeDefined(); + }) + + it("verify dom message",()=>{ + let h1 = el.querySelector('h1'); + expect(h1).toBeDefined() + expect(h1?.textContent).toEqual(component.message) + }) +}) diff --git a/src/app/hello-world/hello-world.spec.ts b/src/app/hello-world/hello-world.spec.ts index d0cecc3..4185b7a 100644 --- a/src/app/hello-world/hello-world.spec.ts +++ b/src/app/hello-world/hello-world.spec.ts @@ -27,6 +27,7 @@ describe('HelloWorld', () => { it('should display the message', () => { const h1 = el.querySelector("h1"); expect(h1).toBeDefined(); + console.log(h1) expect(h1?.textContent).toEqual(component.message); }) From 95c7b90a0f0ab2a24576017fe4241dec6c6a303a Mon Sep 17 00:00:00 2001 From: Himanshu Saini Date: Wed, 29 Jul 2026 02:33:01 +0530 Subject: [PATCH 2/2] modified --- src/app/course-page/course-page-test.spec.ts | 161 ++++++++++++++++++ .../course-card-test.spec.ts | 52 ++++++ src/app/courses/courses-test.spec.ts | 69 ++++++++ src/app/tabs/tabs-test.spec.ts | 52 ++++++ 4 files changed, 334 insertions(+) create mode 100644 src/app/course-page/course-page-test.spec.ts create mode 100644 src/app/courses-card-list/course-card-test.spec.ts create mode 100644 src/app/courses/courses-test.spec.ts create mode 100644 src/app/tabs/tabs-test.spec.ts diff --git a/src/app/course-page/course-page-test.spec.ts b/src/app/course-page/course-page-test.spec.ts new file mode 100644 index 0000000..939f751 --- /dev/null +++ b/src/app/course-page/course-page-test.spec.ts @@ -0,0 +1,161 @@ +import {afterEach, beforeEach, describe, expect, it, Mock, vi} from 'vitest'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {CoursePage} from './course-page'; +import {DebugElement} from '@angular/core'; +import {getMockLessonsPage, MOCK_COURSES, MOCK_LESSONS} from '../testing/testing-data'; +import {CoursesService} from '../services/courses.service'; +import {ActivatedRoute} from '@angular/router'; +import {clickButton, getTableContent} from '../testing/testing-utils'; +import {By} from '@angular/platform-browser'; + +const FIRST_PAGE = getMockLessonsPage(1, '', 'asc', 0, 3); +const SECOND_PAGE = getMockLessonsPage(1, '', 'asc', 1, 3); +const SEARCH_RESULTS = getMockLessonsPage(1, 'Lesson 20', 'asc',0, 3); + + +describe("Complex Angular Component",()=>{ + let mockCourseService : {findLessons: Mock}; + let fixture:ComponentFixture + let debugElement:DebugElement + let component:CoursePage + + beforeEach(async ()=>{ + mockCourseService = { + findLessons: vi.fn() + } + await TestBed.configureTestingModule({ + imports:[CoursePage], + providers:[ + {provide:CoursesService,useValue:mockCourseService}, + {provide:ActivatedRoute, + useValue:{ + snapshot:{ + data:{ + course: MOCK_COURSES[0] + } + } + } + } + ] + }).compileComponents(); + fixture = TestBed.createComponent(CoursePage) + debugElement = fixture.debugElement + component = fixture.componentInstance; + }) + + it('should load lessons on init', async () => { + mockCourseService.findLessons.mockReturnValueOnce(FIRST_PAGE) + + await fixture.whenStable() + expect(mockCourseService.findLessons).toHaveBeenLastCalledWith(1,'',"asc",0,3) + + let courses = getTableContent(debugElement,"tbody tr td.description-cell"); + expect(courses).length(3) + expect(courses[0]).toBe("Lesson 1") + expect(courses[1]).toBe("Lesson 2") + expect(courses[2]).toBe("Lesson 3") + }); + + it('should show the loading spinner while fetching', async () => { + fixture.detectChanges() + const spinner = debugElement.query(By.css(".loading-spinner")) + expect(spinner).toBeDefined(); + expect(component.loading()).toBe(true) + }); + + + it('should navigate to next page', async () => { + mockCourseService.findLessons.mockReturnValueOnce(FIRST_PAGE) + await fixture.whenStable(); + let course = getTableContent(debugElement,"tbody tr td.description-cell"); + expect(mockCourseService.findLessons).toHaveBeenLastCalledWith(1,"","asc",0,3); + expect(course.length).toBe(3); + mockCourseService.findLessons.mockReturnValueOnce(SECOND_PAGE) + component.pageIndex.set(1) + await fixture.whenStable(); + course = getTableContent(debugElement,"tbody tr td.description-cell"); + expect(course).length(3) + expect(course[0]).toBe("Lesson 4") + expect(course[1]).toBe("Lesson 5") + expect(course[2]).toBe("Lesson 6") + }); + + it('should navigate to previous page', async () => { + component.pageIndex.set(1) + mockCourseService.findLessons.mockReturnValueOnce(SECOND_PAGE) + .mockReturnValueOnce(FIRST_PAGE) + await fixture.whenStable() + let course = getTableContent(debugElement,"tbody tr td.description-cell"); + expect(course).length(3) + expect(course[0]).toBe("Lesson 4") + expect(course[1]).toBe("Lesson 5") + expect(course[2]).toBe("Lesson 6") + + clickButton(debugElement,".page-controls button:first-child") + await fixture.whenStable() + course = getTableContent(debugElement,"tbody tr td.description-cell"); + expect(course).length(3) + expect(course[0]).toBe("Lesson 1") + expect(course[1]).toBe("Lesson 2") + expect(course[2]).toBe("Lesson 3") + }); + + it('should toggle sort direction', async () => { + mockCourseService.findLessons.mockReturnValueOnce(FIRST_PAGE) + await fixture.whenStable() + expect(component.sortDirection()).toBe("asc") + + mockCourseService.findLessons.mockReturnValueOnce( + MOCK_LESSONS.reverse().slice(0,3) + ) + clickButton(debugElement,".sortable") + await fixture.whenStable(); + let course = getTableContent(debugElement,"tbody tr td.description-cell"); + expect(course).length(3) + expect(course[0]).toBe("Lesson 20") + expect(course[1]).toBe("Lesson 19") + expect(course[2]).toBe("Lesson 18") + expect(component.sortDirection()).toBe("desc") + }); + + it('should update page size', async () => { + mockCourseService.findLessons.mockReturnValueOnce(FIRST_PAGE) + await fixture.whenStable() + + mockCourseService.findLessons.mockReturnValueOnce( + getMockLessonsPage(1,'',"asc",0,10) + ) + const selector = debugElement.query(By.css(".items-label select")).nativeElement as HTMLSelectElement; + selector.value = '10' + selector.dispatchEvent(new Event("change")) + await fixture.whenStable() + let course = getTableContent(debugElement,"tbody tr td.description-cell"); + expect(course).length(10) + expect(course[0]).toBe("Lesson 1") + expect(course[9]).toBe("Lesson 10") + }); + + it('should debounce search input by 400ms', async () => { + vi.useFakeTimers() + + mockCourseService.findLessons.mockReturnValueOnce(FIRST_PAGE); + fixture.detectChanges() + expect(mockCourseService.findLessons).toHaveBeenCalledTimes(1) + + mockCourseService.findLessons.mockReturnValueOnce(SEARCH_RESULTS) + component.onSearch("Lesson 20") + fixture.detectChanges() + expect(mockCourseService.findLessons).toHaveBeenCalledTimes(1) + + vi.advanceTimersByTime(500) + await vi.runAllTimersAsync() + expect(mockCourseService.findLessons).toHaveBeenCalledTimes(2) + let course = getTableContent(debugElement,"tbody tr td.description-cell"); + expect(course).length(1) + expect(course[0]).toBe("Lesson 20") + }); + + afterEach(() => { + vi.useRealTimers() + }) +}) diff --git a/src/app/courses-card-list/course-card-test.spec.ts b/src/app/courses-card-list/course-card-test.spec.ts new file mode 100644 index 0000000..2a54626 --- /dev/null +++ b/src/app/courses-card-list/course-card-test.spec.ts @@ -0,0 +1,52 @@ +import {beforeEach, describe, expect, it} from 'vitest'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {CoursesCardList} from './courses-card-list'; +import {DebugElement} from '@angular/core'; +import {MOCK_COURSES} from '../testing/testing-data'; +import {By} from '@angular/platform-browser'; +import {provideRouter} from '@angular/router'; + +describe("Test cards functionality",()=>{ + let fixture:ComponentFixture + let debugElement:DebugElement; + let component:CoursesCardList; + + beforeEach(async ()=>{ + await TestBed.configureTestingModule({ + imports:[ + CoursesCardList + ], + providers:[ + provideRouter([]) + ] + }).compileComponents(); + fixture = TestBed.createComponent(CoursesCardList) + debugElement = fixture.debugElement; + component = fixture.componentInstance; + fixture.componentRef.setInput("courses",MOCK_COURSES) + fixture.detectChanges() + }) + + + it("Course should have courses rendeered",()=>{ + const course = debugElement.queryAll(By.css('.course-card .card-header')) + const beginner = course[0].nativeElement + expect(beginner.textContent).toBe('Beginner Course') + }) + + it("it should display message when empty course list",()=>{ + fixture.componentRef.setInput("courses",[]) + fixture.detectChanges() + const noCourse = debugElement.query(By.css(".no-courses")) + expect(noCourse.nativeElement.textContent).toContain("No courses found.") + }) + + it("should display dialog when clicked on edit button",()=>{ + const editButton = debugElement.query(By.css(".edit-btn")) + editButton.nativeElement.click() + fixture.detectChanges() + const form = document.querySelectorAll(".course-form"); + expect(form, "The edit course form should be visible.").toBeTruthy(); + }) +}) + diff --git a/src/app/courses/courses-test.spec.ts b/src/app/courses/courses-test.spec.ts new file mode 100644 index 0000000..0f61a54 --- /dev/null +++ b/src/app/courses/courses-test.spec.ts @@ -0,0 +1,69 @@ +import {afterEach, beforeEach, describe, expect, it} from 'vitest'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {Courses} from './courses'; +import {DebugElement, inject} from '@angular/core'; +import {provideHttpClient} from '@angular/common/http'; +import {HttpTestingController, provideHttpClientTesting} from '@angular/common/http/testing'; +import {provideRouter} from '@angular/router'; +import {CoursesService} from '../services/courses.service'; +import {MOCK_COURSES} from '../testing/testing-data'; +import {By} from '@angular/platform-browser'; + +describe("Testing courses",()=>{ + let fixture:ComponentFixture + let component:Courses; + let debugElement:DebugElement + let httpMock:HttpTestingController + + beforeEach(async ()=>{ + await TestBed.configureTestingModule({ + imports:[ + Courses + ], + providers:[ + provideHttpClient(), + provideHttpClientTesting(), + provideRouter([]), + CoursesService + ] + }).compileComponents(); + fixture = TestBed.createComponent(Courses) + debugElement = fixture.debugElement; + component = fixture.componentInstance; + httpMock = TestBed.inject(HttpTestingController); + fixture.detectChanges() + }) + + it("should add beginner course",async ()=>{ + const httpCall = httpMock.expectOne("/api/courses") + httpCall.flush({ + payload:MOCK_COURSES + }); + await fixture.whenStable() + fixture.detectChanges() + const titles = debugElement.queryAll(By.css(".course-card .card-header")); + expect(titles).toHaveLength(1) + const beginnerEl = titles[0].nativeElement as HTMLElement + expect(beginnerEl.textContent).toContain("Beginner Course") + }) + + it("should show advance course when clicked",async ()=>{ + const req = httpMock.expectOne("/api/courses") + req.flush({ + payload:MOCK_COURSES + }) + await fixture.whenStable() + const advanceButton = debugElement.query(By.css(".tab-link:last-child")) + expect(advanceButton).toBeDefined() + advanceButton.nativeElement.click() + fixture.detectChanges() + const titles = debugElement.queryAll(By.css(".course-card .card-header")); + expect(titles).toHaveLength(1) + const beginnerEl = titles[0].nativeElement as HTMLElement + expect(beginnerEl.textContent).toContain("Advanced Course") + }) + + afterEach(()=>{ + httpMock.verify() + }) +}) diff --git a/src/app/tabs/tabs-test.spec.ts b/src/app/tabs/tabs-test.spec.ts new file mode 100644 index 0000000..7269e31 --- /dev/null +++ b/src/app/tabs/tabs-test.spec.ts @@ -0,0 +1,52 @@ +import {beforeEach, describe, expect, it, vi} from 'vitest'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {TabsComponent} from './tabs'; +import {DebugElement} from '@angular/core'; +import {TabData} from './tabs.model'; +import {MOCK_TABS} from '../testing/testing-data'; +import {By} from '@angular/platform-browser'; + +describe("Tabs custom tests",()=>{ + let fixture:ComponentFixture + let debugElement:DebugElement; + let component:TabsComponent; + const mockedTabs:TabData[] = MOCK_TABS + + beforeEach(async ()=>{ + await TestBed.configureTestingModule({ + imports:[TabsComponent] + }).compileComponents() + fixture = TestBed.createComponent(TabsComponent) + debugElement= fixture.debugElement + component = fixture.componentInstance; + fixture.componentRef.setInput("tabs",mockedTabs) + fixture.detectChanges() + }) + + it("it should create tab component",()=>{ + expect(component).toBeDefined() + }) + + it("it should render 2 button tabs",()=>{ + const buttonEl= debugElement.queryAll(By.css(".tab-link")); + expect(buttonEl.length).toEqual(2) + expect(buttonEl[0].nativeElement.textContent.trim()).toEqual("Beginner") + expect(buttonEl[1].nativeElement.textContent.trim()).toEqual("Advanced") + }) + + it("it should have active class when clicked",()=>{ + fixture.componentRef.setInput("activeTab","advanced") + fixture.detectChanges() + const button = debugElement.query(By.css(".tab-link:last-child")) + expect(component.activeTab()).toEqual("advanced") + }) + + it("it should emit tab changed when clicked",()=>{ + const emitSpy = vi.spyOn(component.tabChanged,"emit") + const button = debugElement.query(By.css(".tab-link:last-child")) + button.nativeElement.click() + fixture.detectChanges() + expect(emitSpy).toHaveBeenCalledWith("advanced") + expect(emitSpy).toHaveBeenCalledOnce() + }) +})