diff --git a/apps/frontend/src/app/app.component.spec.ts b/apps/frontend/src/app/app.component.spec.ts index 73fa98a..9bcc636 100644 --- a/apps/frontend/src/app/app.component.spec.ts +++ b/apps/frontend/src/app/app.component.spec.ts @@ -1,27 +1,29 @@ import { TestBed } from '@angular/core/testing' import { AppComponent } from './app.component' -import { NxWelcomeComponent } from './nx-welcome.component' import { RouterModule } from '@angular/router' +import { AuthService } from './core/auth.service' +import { HttpClient } from '@angular/common/http' describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [AppComponent, NxWelcomeComponent, RouterModule.forRoot([])], + imports: [AppComponent, RouterModule.forRoot([])], + providers: [ + { + provide: AuthService, + useValue: {}, + }, + { + provide: HttpClient, + useValue: {}, + }, + ], }).compileComponents() }) - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent) - fixture.detectChanges() - const compiled = fixture.nativeElement as HTMLElement - expect(compiled.querySelector('h1')?.textContent).toContain( - 'Welcome frontend', - ) - }) - - it(`should have as title 'frontend'`, () => { + it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent) const app = fixture.componentInstance - expect(app.title).toEqual('frontend') + expect(app).toBeTruthy() }) }) diff --git a/apps/frontend/src/app/features/home/home.component.spec.ts b/apps/frontend/src/app/features/home/home.component.spec.ts index db8b64c..dd8c6e6 100644 --- a/apps/frontend/src/app/features/home/home.component.spec.ts +++ b/apps/frontend/src/app/features/home/home.component.spec.ts @@ -20,11 +20,29 @@ describe('HomeComponent', () => { expect(component).toBeTruthy() }) - test('should search vote', inject([Router], (mockRouter: Router) => { - const spyRouter = spyOn(mockRouter, 'navigate').and.stub() - + test('searchVote', () => { + const router = TestBed.inject(Router) + const navigateSpy = jest.spyOn(router, 'navigate') component.voteId.setValue('123') component.searchVote() - expect(spyRouter.calls.first().args[0]).toContain('voting' && '123') - })) + expect(navigateSpy).toHaveBeenCalledWith(['/voting', '123']) + }) + + test('createVote', () => { + const router = TestBed.inject(Router) + const navigateSpy = jest.spyOn(router, 'navigate') + component.createVote() + expect(navigateSpy).toHaveBeenCalledWith(['/voting', 'create']) + }) + + test('pasteVoteId', async () => { + const clipboard = { + readText: jest.fn().mockResolvedValue('123'), + } + Object.defineProperty(navigator, 'clipboard', { + value: clipboard, + }) + await component.pasteVoteId() + expect(component.voteId.value).toBe('123') + }) }) diff --git a/apps/frontend/src/app/features/login/login.component.spec.ts b/apps/frontend/src/app/features/login/login.component.spec.ts index 70c2ef2..b5d2ff4 100644 --- a/apps/frontend/src/app/features/login/login.component.spec.ts +++ b/apps/frontend/src/app/features/login/login.component.spec.ts @@ -1,21 +1,38 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { LoginComponent } from './login.component' +import { AuthService } from '../../core/auth.service' +import { ChangeDetectorRef } from '@angular/core' +import { ReactiveFormsModule } from '@angular/forms' describe('LoginComponent', () => { let component: LoginComponent let fixture: ComponentFixture + let authService: AuthService beforeEach(async () => { + const mockAuthService = { + loginUsingPrivateKey: jest.fn().mockResolvedValue(true), + } await TestBed.configureTestingModule({ - imports: [LoginComponent], + imports: [ReactiveFormsModule, LoginComponent], + providers: [ + { provide: AuthService, useValue: mockAuthService }, + ChangeDetectorRef, + ], }).compileComponents() fixture = TestBed.createComponent(LoginComponent) component = fixture.componentInstance + authService = TestBed.inject(AuthService) fixture.detectChanges() }) it('should create', () => { expect(component).toBeTruthy() }) + + it('should call loginUsingPrivateKey on authService', async () => { + await component.logInUsingPrivateKey() + expect(authService.loginUsingPrivateKey).toHaveBeenCalled() + }) }) diff --git a/apps/frontend/src/app/features/register/register.component.spec.ts b/apps/frontend/src/app/features/register/register.component.spec.ts index 0401f8b..ced971e 100644 --- a/apps/frontend/src/app/features/register/register.component.spec.ts +++ b/apps/frontend/src/app/features/register/register.component.spec.ts @@ -1,21 +1,42 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { RegisterComponent } from './register.component' +import { AuthService } from '../../core/auth.service' +import { ChangeDetectorRef } from '@angular/core' describe('RegisterComponent', () => { let component: RegisterComponent let fixture: ComponentFixture + let authService: AuthService beforeEach(async () => { + const mockAuthService = { + generateKeypair: jest.fn().mockReturnValue({ + publicKey: jest.fn().mockReturnValue('publicKey'), + secret: jest.fn().mockReturnValue, + }), + fundAccount: jest.fn().mockResolvedValue(true), + } + await TestBed.configureTestingModule({ imports: [RegisterComponent], + providers: [ + { provide: AuthService, useValue: mockAuthService }, + ChangeDetectorRef, + ], }).compileComponents() fixture = TestBed.createComponent(RegisterComponent) component = fixture.componentInstance + authService = TestBed.inject(AuthService) fixture.detectChanges() }) it('should create', () => { expect(component).toBeTruthy() }) + + it('should generate keypair', () => { + component.generateKeypair() + expect(authService.generateKeypair).toHaveBeenCalled() + }) }) diff --git a/apps/frontend/src/app/features/voting/cast/cast.component.spec.ts b/apps/frontend/src/app/features/voting/cast/cast.component.spec.ts index 8d80aed..367f109 100644 --- a/apps/frontend/src/app/features/voting/cast/cast.component.spec.ts +++ b/apps/frontend/src/app/features/voting/cast/cast.component.spec.ts @@ -1,22 +1,139 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' - +import { ReactiveFormsModule } from '@angular/forms' +import { Router } from '@angular/router' import { CastComponent } from './cast.component' +import { VoteConfigService } from '../../../core/vote-transaction.service' +import { CastVoteService } from '../../../core/stellar/castVote.service' +import { ConfirmReloadService } from '../../../shared/services/confirm-reload/confirm-reload.service' +import { GetVoteOptionService } from '../../../core/stellar/getVoteOption.service' +import { LoadingComponent } from '../../../shared/feedback/loading/loading.component' +import { ErrorComponent } from '../../../shared/feedback/error/error.component' describe('CastComponent', () => { let component: CastComponent let fixture: ComponentFixture + let voteConfigService: VoteConfigService + let router: Router + let confirmReloadService: ConfirmReloadService beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [CastComponent], + imports: [ + CastComponent, + LoadingComponent, + ErrorComponent, + ReactiveFormsModule, + ], + providers: [ + { + provide: VoteConfigService, + useValue: { + getBaseVoteConfig: jest.fn(), + }, + }, + { + provide: CastVoteService, + useValue: { + castVote: jest.fn(), + }, + }, + { + provide: ConfirmReloadService, + useValue: { + confirmReload: jest.fn().mockReturnValue(true), + }, + }, + { + provide: GetVoteOptionService, + useValue: {}, + }, + { + provide: Router, + useValue: { + navigate: jest.fn(), + }, + }, + ], }).compileComponents() fixture = TestBed.createComponent(CastComponent) component = fixture.componentInstance - fixture.detectChanges() + voteConfigService = TestBed.inject(VoteConfigService) + router = TestBed.inject(Router) + confirmReloadService = TestBed.inject(ConfirmReloadService) }) it('should create', () => { expect(component).toBeTruthy() }) + + it('should initialize the form with required fields', () => { + expect(component.voteForm.contains('selectedOption')).toBeTruthy() + expect(component.voteForm.get('selectedOption')?.valid).toBeFalsy() + }) + + it('should handle input changes and remove duplicates', async () => { + const options = ['Option1', 'Option2', 'Option1'] + const data = ['Data1', 'Data2', 'Data1'] + + component.optionsArr = options + component.dataArr = data + await component.ngOnChanges({ + optionsArr: { + currentValue: options, + previousValue: [], + firstChange: true, + isFirstChange(): boolean { + return true + }, + }, + dataArr: { + currentValue: data, + previousValue: [], + firstChange: true, + isFirstChange(): boolean { + return true + }, + }, + }) + + expect(component.optionsArr).toEqual(['Option1', 'Option2']) + expect(component.dataArr).toEqual(['Data1', 'Data2']) + }) + + it('should handle errors when fetching vote configuration', async () => { + jest + .spyOn(voteConfigService, 'getBaseVoteConfig') + .mockRejectedValueOnce(new Error('Fetch error')) + + await component.ngOnChanges({}) + + expect(component.hasError).toBeTruthy() + expect(component.errorMessage).toBe('Failed to load vote configuration.') + }) + + it('should navigate to results view if form is clean', () => { + component.voteId = '123' + component.viewResults() + + expect(router.navigate).toHaveBeenCalledWith(['/voting/r', '123']) + }) + + it('should not navigate to results view if form is dirty and confirm is false', () => { + jest.spyOn(confirmReloadService, 'confirmReload').mockReturnValueOnce(false) + component.voteForm.markAsDirty() + component.voteId = '123' + component.viewResults() + + expect(router.navigate).not.toHaveBeenCalled() + }) + + it('should reset error state on errorAction call', () => { + component.hasError = true + component.errorMessage = 'Some error' + component.errorAction() + + expect(component.hasError).toBeFalsy() + expect(component.errorMessage).toBe('') + }) }) diff --git a/apps/frontend/src/app/features/voting/create/create.component.spec.ts b/apps/frontend/src/app/features/voting/create/create.component.spec.ts index a20cd69..16105b0 100644 --- a/apps/frontend/src/app/features/voting/create/create.component.spec.ts +++ b/apps/frontend/src/app/features/voting/create/create.component.spec.ts @@ -1,5 +1,11 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { CreateComponent } from './create.component' +import { ActivatedRoute } from '@angular/router' +import { of } from 'rxjs' +import { VoteConfigService } from '../../../core/vote-transaction.service' +import { AuthService } from '../../../core/auth.service' +import { HttpClient } from '@angular/common/http' +import { CreateVoteService } from '../../../core/stellar/createVote.service' describe('CreateComponent', () => { let component: CreateComponent @@ -8,6 +14,39 @@ describe('CreateComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [CreateComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + params: of({ id: 'test-vote-id' }), + }, + }, + { + provide: VoteConfigService, + useValue: { + getBaseVoteConfig: jest.fn().mockResolvedValue({ + server: {}, + sourceKeypair: {}, + }), + }, + }, + { + provide: AuthService, + useValue: {}, + }, + { + provide: HttpClient, + useValue: {}, + }, + { + provide: CreateVoteService, + useValue: { + createVote: jest.fn().mockResolvedValue({ + status: 'SUCCESS', + }), + }, + }, + ], }).compileComponents() fixture = TestBed.createComponent(CreateComponent) @@ -18,4 +57,45 @@ describe('CreateComponent', () => { it('should create', () => { expect(component).toBeTruthy() }) + + it('should have a voteForm', () => { + expect(component.voteForm).toBeTruthy() + }) + + it('should have isLoading set to false', () => { + expect(component.isLoading).toBeFalsy() + }) + + it('should have hasError set to false', () => { + expect(component.hasError).toBeFalsy() + }) + + it('should have errorMessage set to an empty string', () => { + expect(component.errorMessage).toBe('') + }) + + it('should have successMessage set to an empty string', () => { + expect(component.successMessage).toBe('') + }) + + it('should have options set to an array with 2 options', () => { + expect(component.options.length).toBe(2) + }) + + it('should submit form', () => { + component.voteForm.controls['id'].setValue('test-id') + component.voteForm.controls['title'].setValue('test-title') + component.voteForm.controls['description'].setValue('test-description') + component.voteForm.controls['options'].setValue([ + { option: 'Option A' }, + { option: 'Option B' }, + ]) + + component.onSubmit() + + expect(component.isLoading).toBeTruthy() + expect(component.hasError).toBeFalsy() + expect(component.errorMessage).toBe('') + expect(component.successMessage).toBe('') + }) }) diff --git a/apps/frontend/src/app/features/voting/create/share/share.component.spec.ts b/apps/frontend/src/app/features/voting/create/share/share.component.spec.ts index d331b37..f3361a4 100644 --- a/apps/frontend/src/app/features/voting/create/share/share.component.spec.ts +++ b/apps/frontend/src/app/features/voting/create/share/share.component.spec.ts @@ -15,7 +15,32 @@ describe('ShareComponent', () => { fixture.detectChanges() }) + beforeAll(() => { + Object.defineProperty(window.URL, 'createObjectURL', { + writable: true, + value: jest.fn(), + }) + Object.defineProperty(window.URL, 'revokeObjectURL', { + writable: true, + value: jest.fn(), + }) + }) + it('should create', () => { expect(component).toBeTruthy() }) + + it('should download vote ID', () => { + const voteId = 'test-vote-id' + const voteTitle = 'test-vote-title' + + component.voteId = voteId + component.voteTitle = voteTitle + + const downloadVoteIDSpy = jest.spyOn(component, 'downloadVoteID') + + component.downloadVoteID() + + expect(downloadVoteIDSpy).toHaveBeenCalled() + }) }) diff --git a/apps/frontend/src/app/features/voting/results/results.component.spec.ts b/apps/frontend/src/app/features/voting/results/results.component.spec.ts index cc3cd66..8d97bb5 100644 --- a/apps/frontend/src/app/features/voting/results/results.component.spec.ts +++ b/apps/frontend/src/app/features/voting/results/results.component.spec.ts @@ -1,6 +1,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { ResultsComponent } from './results.component' +import { ActivatedRoute } from '@angular/router' +import { VoteConfigService } from '../../../core/vote-transaction.service' +import { AuthService } from '../../../core/auth.service' +import { HttpClient } from '@angular/common/http' +import { GetVoteResultsService } from '../../../core/stellar/getVoteResults.service' +import { GetVoteService } from '../../../core/stellar/getVote.service' +import { CheckUserVotedService } from '../../../core/stellar/checkUserVoted.service' +import { of } from 'rxjs' describe('ResultsComponent', () => { let component: ResultsComponent @@ -9,6 +17,58 @@ describe('ResultsComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ResultsComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + params: of({ id: 'test-vote-id' }), + }, + }, + { + provide: VoteConfigService, + useValue: { + getBaseVoteConfig: jest.fn().mockResolvedValue({ + server: {}, + sourceKeypair: {}, + }), + }, + }, + { + provide: AuthService, + useValue: {}, + }, + { + provide: HttpClient, + useValue: {}, + }, + { + provide: GetVoteResultsService, + useValue: { + getVoteResults: jest.fn().mockResolvedValue({ + dataArr: [ + { key: 'Option A', val: '10' }, + { key: 'Option B', val: '20' }, + ], + }), + }, + }, + { + provide: GetVoteService, + useValue: { + getVote: jest.fn().mockResolvedValue({ + dataArr: ['Option A', 'Option B'], + }), + }, + }, + { + provide: CheckUserVotedService, + useValue: { + checkIfUserHasVoted: jest.fn().mockResolvedValue({ + hasVoted: true, + }), + }, + }, + ], }).compileComponents() fixture = TestBed.createComponent(ResultsComponent) @@ -19,4 +79,22 @@ describe('ResultsComponent', () => { it('should create', () => { expect(component).toBeTruthy() }) + + it('should initialize the component', () => { + expect(component.isLoading).toBe(true) + expect(component.hasError).toBe(false) + expect(component.voteId).toBe('test-vote-id') + expect(component.dataArr).toEqual([]) + expect(component.resultArr).toEqual([]) + expect(component.totalVotes).toBe(0) + }) + + it('should call ngOnInit', () => { + component.ngOnInit() + expect(component.isLoading).toBe(true) + expect(component.hasError).toBe(false) + expect(component.voteId).toBe('test-vote-id') + expect(component.dataArr).toEqual([]) + expect(component.resultArr).toEqual([]) + }) }) diff --git a/apps/frontend/src/app/features/voting/voting.component.spec.ts b/apps/frontend/src/app/features/voting/voting.component.spec.ts index 0ea64c1..face3cb 100644 --- a/apps/frontend/src/app/features/voting/voting.component.spec.ts +++ b/apps/frontend/src/app/features/voting/voting.component.spec.ts @@ -1,6 +1,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' +import { RouterTestingModule } from '@angular/router/testing' +import { ActivatedRoute } from '@angular/router' +import { of } from 'rxjs' // Import this if you want to mock Observables import { VotingComponent } from './voting.component' +import { VoteConfigService } from '../../core/vote-transaction.service' +import { AuthService } from '../../core/auth.service' +import { HttpClient } from '@angular/common/http' describe('VotingComponent', () => { let component: VotingComponent @@ -9,6 +15,28 @@ describe('VotingComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [VotingComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + paramMap: of({ get: () => 'someValue' }), + }, + }, + { + provide: VoteConfigService, + useValue: { + getBaseVoteConfig: () => Promise.resolve({ server: 'someServer' }), + }, + }, + { + provide: AuthService, + useValue: {}, + }, + { + provide: HttpClient, + useValue: {}, + }, + ], }).compileComponents() fixture = TestBed.createComponent(VotingComponent) @@ -19,4 +47,16 @@ describe('VotingComponent', () => { it('should create', () => { expect(component).toBeTruthy() }) + + it('should call ngOnInit', () => { + jest.spyOn(component, 'ngOnInit') + component.ngOnInit() + expect(component.ngOnInit).toHaveBeenCalled() + }) + + it('should call ngOnDestroy', () => { + jest.spyOn(component, 'ngOnDestroy') + component.ngOnDestroy() + expect(component.ngOnDestroy).toHaveBeenCalled() + }) }) diff --git a/apps/frontend/src/app/shared/navigation/navbar/navbar.component.spec.ts b/apps/frontend/src/app/shared/navigation/navbar/navbar.component.spec.ts index e2f359a..250ccb7 100644 --- a/apps/frontend/src/app/shared/navigation/navbar/navbar.component.spec.ts +++ b/apps/frontend/src/app/shared/navigation/navbar/navbar.component.spec.ts @@ -1,5 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { NavbarComponent } from './navbar.component' +import { AuthService } from '../../../core/auth.service' +import { HttpClient } from '@angular/common/http' describe('NavbarComponent', () => { let component: NavbarComponent @@ -8,6 +10,20 @@ describe('NavbarComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [NavbarComponent], + providers: [ + { + provide: AuthService, + useValue: { + loginStatusChanged: { + subscribe: jest.fn(), + }, + }, + }, + { + provide: HttpClient, + useValue: {}, + }, + ], }).compileComponents() fixture = TestBed.createComponent(NavbarComponent) diff --git a/apps/frontend/src/app/shared/services/confirm-reload/confirm-reload.service.spec.ts b/apps/frontend/src/app/shared/services/confirm-reload/confirm-reload.service.spec.ts index e69de29..a4a20cd 100644 --- a/apps/frontend/src/app/shared/services/confirm-reload/confirm-reload.service.spec.ts +++ b/apps/frontend/src/app/shared/services/confirm-reload/confirm-reload.service.spec.ts @@ -0,0 +1,30 @@ +import { ConfirmReloadService } from './confirm-reload.service' +import { TestBed } from '@angular/core/testing' + +describe('ConfirmReloadService', () => { + let service: ConfirmReloadService + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ConfirmReloadService], + }) + service = TestBed.inject(ConfirmReloadService) + }) + + it('should be created', () => { + expect(service).toBeTruthy() + }) + + describe('confirmReload', () => { + it('should return the result of the confirm dialog', () => { + const message = + 'You have unsaved changes. Are you sure you want to leave this page?' + const spy = jest.spyOn(window, 'confirm').mockReturnValue(true) + + const result = service.confirmReload(message) + + expect(spy).toHaveBeenCalledWith(message) + expect(result).toBe(true) + }) + }) +})