diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml new file mode 100644 index 00000000..3c6e193e --- /dev/null +++ b/.github/workflows/continous-integration.yml @@ -0,0 +1,26 @@ +name: CI Pipeline Frontend + +on: push + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout ✅ + uses: actions/checkout@v3 + + - name: Setup 🏗 + uses: actions/setup-node@v2 + with: + node-version: lts/* + cache: 'npm' + + - name: Install ⚙️ + run: npm install + + - name: Build 🛠 + run: npm run build + + - name: Test 📋 + run: npm run test:prod \ No newline at end of file diff --git a/.github/workflows/heroku-deploy.yml b/.github/workflows/heroku-deploy.yml new file mode 100644 index 00000000..fa766d8d --- /dev/null +++ b/.github/workflows/heroku-deploy.yml @@ -0,0 +1,21 @@ +name: heroku-deploy +on: + pull_request: + types: + - closed + branches: + - develop + +jobs: + deploy-heroku: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + name: heroku deploy + steps: + - uses: actions/checkout@v3 + - uses: akhileshns/heroku-deploy@v3.12.12 + with: + heroku_api_key: ${{secrets.HEROKU_API_KEY}} + heroku_app_name: "unb-tv-2023-2" + heroku_email: ${{secrets.HEROKU_EMAIL}} + usedocker: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index f8a737a1..ba76bc61 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,4 @@ testem.log Thumbs.db ./github -/src/app/secret/ \ No newline at end of file +/src/app/environment \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 12f8b840..c114f57b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,12 @@ FROM node:16 as angular WORKDIR /unb-tv-web +RUN apt-get update +RUN apt-get -y install chromium + +# set CHROME_BIN environment variable, so that karma knows which crome should be started +ENV CHROME_BIN=/usr/bin/chromium + ENV PATH /unb-tv-web/node_modules/.bin:$PATH ENV NODE_ENV=dev diff --git a/angular.json b/angular.json index b0eaf8d8..f55608fe 100644 --- a/angular.json +++ b/angular.json @@ -13,7 +13,7 @@ "build": { "builder": "@angular-devkit/build-angular:browser", "options": { - "outputPath": "dist/un-b-tv-frontend", + "outputPath": "dist/unb-tv-frontend", "index": "src/index.html", "main": "src/main.ts", "polyfills": [ @@ -89,7 +89,8 @@ "styles": [ "src/styles.css" ], - "scripts": [] + "scripts": [], + "karmaConfig": "karma.conf.js" } } } diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 00000000..10dfe918 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,52 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + customLaunchers: { + ChromeHeadless: { + base: 'Chrome', + flags: [ + '--no-sandbox', + '--disable-gpu', + '--headless', + '--remote-debugging-port=9222' + ] + } + }, + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage/unb-tv-frontend'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'lcov' }, + { type: 'cobertura' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + browsers: ['Chrome'], + restartOnFileChange: true + }); +}; diff --git a/package-lock.json b/package-lock.json index c16ab575..8114907a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "un-b-tv-frontend", + "name": "unb-tv-frontend", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "un-b-tv-frontend", + "name": "unb-tv-frontend", "version": "0.0.0", "dependencies": { "@angular/animations": "^15.2.0", diff --git a/package.json b/package.json index 4d026e0f..c7417f6a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "un-b-tv-frontend", + "name": "unb-tv-frontend", "version": "0.0.0", "scripts": { "ng": "ng", @@ -7,7 +7,9 @@ "start": "ng serve", "build": "ng build", "watch": "ng build --watch --configuration development", - "test": "ng test" + "test": "ng test", + "build:prod": "ng build --prod", + "test:prod": "ng test --browsers=ChromeHeadless --watch=false --code-coverage" }, "private": true, "dependencies": { diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 53f2b51f..3e256296 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -1,35 +1,24 @@ -import { TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; +import { BackgroundComponent } from './components/background/background.component'; describe('AppComponent', () => { + let component: AppComponent; + let fixture: ComponentFixture; + beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule - ], - declarations: [ - AppComponent - ], + imports: [RouterTestingModule], + declarations: [AppComponent, BackgroundComponent], }).compileComponents(); - }); - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); + fixture = TestBed.createComponent(AppComponent); + component = fixture.componentInstance; + fixture.detectChanges(); }); - it(`should have as title 'UnB-TV-Frontend'`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('UnB-TV-Frontend'); + it('should create the app', () => { + expect(component).toBeTruthy(); }); - - // it('should render title', () => { - // const fixture = TestBed.createComponent(AppComponent); - // fixture.detectChanges(); - // const compiled = fixture.nativeElement as HTMLElement; - // expect(compiled.querySelector('.content span')?.textContent).toContain('UnB-TV-Frontend app is running!'); - // }); }); diff --git a/src/app/pages/active-account/active-account.component.spec.ts b/src/app/pages/active-account/active-account.component.spec.ts index b8d287f9..2a087ab8 100644 --- a/src/app/pages/active-account/active-account.component.spec.ts +++ b/src/app/pages/active-account/active-account.component.spec.ts @@ -1,23 +1,88 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ActiveAccountComponent } from './active-account.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { AuthService } from 'src/app/services/auth.service'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { of, throwError } from 'rxjs'; +import { RouterTestingModule } from '@angular/router/testing'; +import { LoginComponent } from '../login/login.component'; + +const mockData: any = { + "email": "mario@gmail.com", + "code": 123456, +} + +class AuthServiceMock { + constructor() { } + activeAccount() { + return of({ success: true }); + } + resendCode() { + return of({ success: true }); + } +} describe('ActiveAccountComponent', () => { let component: ActiveAccountComponent; let fixture: ComponentFixture; + let authService: AuthService; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ActiveAccountComponent ] + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [ + { path: 'login', component: LoginComponent }, + ] + ), ReactiveFormsModule], + providers: [{ provide: AuthService, useValue: new AuthServiceMock() }, FormBuilder], + declarations: [ActiveAccountComponent] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(ActiveAccountComponent); component = fixture.componentInstance; fixture.detectChanges(); + authService = TestBed.inject(AuthService); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call activeAccount method when the form is submitted', () => { + fixture.detectChanges(); + spyOn(component, 'activeAccount').and.callThrough(); + const form = component.userForm; + form.setValue(mockData); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(component.activeAccount).toHaveBeenCalled(); + }); + + it('should call alert when form is not valid', () => { + spyOn(component, 'activeAccount').and.callThrough(); + const alertSpy = spyOn(window, 'alert'); + fixture.detectChanges(); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); + }); + + it('should call activeAccount and return an error', () => { + fixture.detectChanges(); + const form = component.userForm; + form.setValue(mockData); + const mySpy = spyOn(authService, 'activeAccount').and.returnValue(throwError(() => new Error('Erro'))); + component.activeAccount(); + expect(mySpy).toHaveBeenCalled(); + }); + }); diff --git a/src/app/pages/check-code-rest-password/check-code-rest-password.component.html b/src/app/pages/check-code-rest-password/check-code-rest-password.component.html index 6e1cff89..70e9d9b4 100644 --- a/src/app/pages/check-code-rest-password/check-code-rest-password.component.html +++ b/src/app/pages/check-code-rest-password/check-code-rest-password.component.html @@ -1,13 +1,40 @@ -
- +
- +
- - + +
diff --git a/src/app/pages/check-code-rest-password/check-code-rest-password.component.spec.ts b/src/app/pages/check-code-rest-password/check-code-rest-password.component.spec.ts index d6a7bf96..46e0fc16 100644 --- a/src/app/pages/check-code-rest-password/check-code-rest-password.component.spec.ts +++ b/src/app/pages/check-code-rest-password/check-code-rest-password.component.spec.ts @@ -1,23 +1,129 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { CheckCodeRestPasswordComponent } from './check-code-rest-password.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { AuthService } from 'src/app/services/auth.service'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { of, throwError } from 'rxjs'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ResetPasswordComponent } from '../reset-password/reset-password.component'; + +const mockData: any = { + "email": "mario@gmail.com", + "code": 123456, +} + +class AuthServiceMock { + constructor() { } + sendEmailPassword() { + return of({ success: true }); + } + verifyCodePassword() { + return of({ success: true }); + } +} describe('CheckCodeRestPasswordComponent', () => { let component: CheckCodeRestPasswordComponent; let fixture: ComponentFixture; + let authService: AuthService; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ CheckCodeRestPasswordComponent ] + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [ + { path: 'changePassword', component: ResetPasswordComponent }, + ] + ), ReactiveFormsModule], + providers: [{ provide: AuthService, useValue: new AuthServiceMock() }, FormBuilder], + declarations: [CheckCodeRestPasswordComponent] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(CheckCodeRestPasswordComponent); component = fixture.componentInstance; fixture.detectChanges(); + authService = TestBed.inject(AuthService); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call sendEmail method when the form is submitted', () => { + fixture.detectChanges(); + spyOn(component, 'sendEmail').and.callThrough(); + const form = component.userForm; + form.setValue(mockData); + + const submitButton = fixture.nativeElement.querySelector( + '#sendEmail' + ); + submitButton.click(); + + expect(component.sendEmail).toHaveBeenCalled(); + }); + + it('should call alert when form is not valid', () => { + fixture.detectChanges(); + spyOn(component, 'sendEmail').and.callThrough(); + const alertSpy = spyOn(window, 'alert'); + const form = component.userForm; + form.setValue({ "email": '', code: '' }); + + const submitButton = fixture.nativeElement.querySelector( + '#sendEmail' + ); + submitButton.click(); + + expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); + }); + + it('should call sendEmail and return an error', () => { + fixture.detectChanges(); + const form = component.userForm; + form.setValue(mockData); + const mySpy = spyOn(authService, 'sendEmailPassword').and.returnValue(throwError(() => new Error('Erro'))); + component.sendEmail(); + expect(mySpy).toHaveBeenCalled(); + }); + + it('should call checkCode method when the form is submitted', () => { + fixture.detectChanges(); + spyOn(component, 'checkCode').and.callThrough(); + const form = component.userForm; + form.setValue(mockData); + component.activeCode = true; + fixture.detectChanges(); + + const submitButton = fixture.nativeElement.querySelector( + '#checkCode' + ); + submitButton.click(); + + expect(component.checkCode).toHaveBeenCalled(); + }); + + it('should call alert when form is not valid', () => { + fixture.detectChanges(); + spyOn(component, 'checkCode').and.callThrough(); + const alertSpy = spyOn(window, 'alert'); + component.activeCode = true; + fixture.detectChanges(); + const submitButton = fixture.nativeElement.querySelector( + '#checkCode' + ); + submitButton.click(); + + expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); + }); + + it('should call checkCode and return an error', () => { + fixture.detectChanges(); + const form = component.userForm; + form.setValue(mockData); + const mySpy = spyOn(authService, 'verifyCodePassword').and.returnValue(throwError(() => new Error('Erro'))); + component.checkCode(); + expect(mySpy).toHaveBeenCalled(); + }); + }); diff --git a/src/app/pages/edit-user/edit-user.component.spec.ts b/src/app/pages/edit-user/edit-user.component.spec.ts index 054644a2..41d290bb 100644 --- a/src/app/pages/edit-user/edit-user.component.spec.ts +++ b/src/app/pages/edit-user/edit-user.component.spec.ts @@ -1,23 +1,87 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { EditUserComponent } from './edit-user.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { of, throwError } from 'rxjs'; +import { UserService } from 'src/app/services/user.service'; +import { ProfileComponent } from '../profile/profile.component'; + +const mockData: any = { + "name": "Mario", + "email": "mario@gmail.com", + "connection": "ALUNO", +} + +class UserServiceMock { + constructor() { } + updateUser() { + return of({ success: true }); + } +} describe('EditUserComponent', () => { let component: EditUserComponent; let fixture: ComponentFixture; + let userService: UserService; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ EditUserComponent ] + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [ + { path: 'profile', component: ProfileComponent }, + ] + ), ReactiveFormsModule], + declarations: [EditUserComponent], + providers: [{ provide: UserService, useValue: new UserServiceMock() }, FormBuilder], }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(EditUserComponent); component = fixture.componentInstance; fixture.detectChanges(); + userService = TestBed.inject(UserService); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call updateUser method when the form is submitted', () => { + fixture.detectChanges(); + spyOn(component, 'updateUser').and.callThrough(); + const form = component.userForm; + form.setValue(mockData); + component.userId = mockData.id; + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(component.updateUser).toHaveBeenCalled(); + }); + + it('should call alert when form is not valid', () => { + spyOn(component, 'updateUser').and.callThrough(); + const alertSpy = spyOn(window, 'alert'); + fixture.detectChanges(); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); + }); + + it('should call updateUser and return an error', () => { + fixture.detectChanges(); + const form = component.userForm; + form.setValue(mockData); + const mySpy = spyOn(userService, 'updateUser').and.returnValue(throwError(() => new Error('Erro'))); + component.updateUser(); + expect(mySpy).toHaveBeenCalled(); + }); + }); diff --git a/src/app/pages/login/login.component.spec.ts b/src/app/pages/login/login.component.spec.ts index 10eca249..ca4a87dd 100644 --- a/src/app/pages/login/login.component.spec.ts +++ b/src/app/pages/login/login.component.spec.ts @@ -1,23 +1,110 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { LoginComponent } from './login.component'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { AuthService } from 'src/app/services/auth.service'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ProfileComponent } from '../profile/profile.component'; +import { CheckCodeRestPasswordComponent } from '../check-code-rest-password/check-code-rest-password.component'; +import { RegisterComponent } from '../register/register.component'; +import { of, throwError } from 'rxjs'; + +const mockUserReturn = { + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJqb2FvMTV2aWN0b3IwOEBnbWFpbC5jb20iLCJleHAiOjE2OTkzMTI5MzV9.1B9qBJt8rErwBKyD5JCdsPozsw86oQ38tdfDuMM2HFI", + "token_type": "bearer" +} + +class AuthServiceMock { + constructor() { } + loginUser() { + return of(mockUserReturn); + } +} describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture; + let authService: AuthService; beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ LoginComponent ] - }) - .compileComponents(); + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule.withRoutes( + [ + { path: 'profile', component: ProfileComponent }, + { path: 'sendCodeResetPassword', component: CheckCodeRestPasswordComponent }, + { path: 'register', component: RegisterComponent } + ] + )], + providers: [FormBuilder, { provide: AuthService, useValue: new AuthServiceMock() }], + declarations: [LoginComponent], + }).compileComponents(); fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; - fixture.detectChanges(); + authService = TestBed.inject(AuthService); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should have a valid form on initialization', () => { + fixture.detectChanges(); + expect(component.userForm).toBeTruthy(); + }); + + it('should call login method when the form is submitted', () => { + fixture.detectChanges(); + spyOn(component, 'login').and.callThrough(); + const form = component.userForm; + form.setValue({ email: 'test@example.com', password: 'password' }); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(component.login).toHaveBeenCalled(); + }); + + it('should call alert when form is not valid', () => { + spyOn(component, 'login').and.callThrough(); + const alertSpy = spyOn(window, 'alert'); + fixture.detectChanges(); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); + }); + + it('should call navigator method when "Esqueceu a senha?" is clicked', () => { + spyOn(component, 'navigator').and.callThrough(); + const forgotPasswordLink = + fixture.nativeElement.querySelector('.text-gray-400'); + forgotPasswordLink.click(); + + expect(component.navigator).toHaveBeenCalledWith('/sendCodeResetPassword'); + + }); + + it('should call navigator method when "Cadastre-se" is clicked', () => { + spyOn(component, 'navigator').and.callThrough(); + const registerLink = + fixture.nativeElement.querySelector('.text-blue-brand'); + registerLink.click(); + + expect(component.navigator).toHaveBeenCalledWith('/register'); + }); + + it('should call login and return an error', () => { + fixture.detectChanges(); + const form = component.userForm; + form.setValue({ email: 'test@example.com', password: 'password' }); + const mySpy = spyOn(authService, 'loginUser').and.returnValue(throwError(() => new Error('Erro'))); + component.login(); + expect(mySpy).toHaveBeenCalled(); + }); }); diff --git a/src/app/pages/login/login.component.ts b/src/app/pages/login/login.component.ts index 1f528a8b..7ef3aea2 100644 --- a/src/app/pages/login/login.component.ts +++ b/src/app/pages/login/login.component.ts @@ -16,29 +16,28 @@ export class LoginComponent implements OnInit { private router: Router, private fb: FormBuilder, private authService: AuthService - ) {} + ) { } ngOnInit(): void { this.userForm = this.fb.group({ - email: ['', [Validators.required]], - password: ['', [Validators.required]], - }, + email: ['', [Validators.required]], + password: ['', [Validators.required]], + }, ); } login() { if (this.userForm.valid) { this.authService -.loginUser(this.userForm.value).subscribe({ - next: (data) => { - console.log(data); - localStorage.setItem('token', data.access_token); - this.navigator('/profile'); - }, - error: (error) => { - console.error(error); - }, - }); + .loginUser(this.userForm.value).subscribe({ + next: (data) => { + localStorage.setItem('token', data.access_token); + this.navigator('/profile'); + }, + error: (error) => { + console.error(error); + }, + }); } else { alert('Preencha todos os campos corretamente!'); } diff --git a/src/app/pages/profile/profile.component.spec.ts b/src/app/pages/profile/profile.component.spec.ts index 246039d7..9059002a 100644 --- a/src/app/pages/profile/profile.component.spec.ts +++ b/src/app/pages/profile/profile.component.spec.ts @@ -1,23 +1,76 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ProfileComponent } from './profile.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of, throwError } from 'rxjs'; +import { UserService } from 'src/app/services/user.service'; +import { EditUserComponent } from '../edit-user/edit-user.component'; +import { RouterTestingModule } from '@angular/router/testing'; + +const mockData: any = { + "id": 1, + "name": "Mario", + "connection": "ALUNO", + "email": "mario@gmail.com", + "role": "USER", + "is_active": true +} + +class UserServiceMock { + constructor() { } + getUser() { + return of(mockData); + } +} describe('ProfileComponent', () => { let component: ProfileComponent; let fixture: ComponentFixture; + let userService: UserService; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ProfileComponent ] + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [ + { path: 'editUser', component: EditUserComponent }, + ] + )], + declarations: [ProfileComponent], + providers: [{ provide: UserService, useValue: new UserServiceMock() }] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(ProfileComponent); component = fixture.componentInstance; - fixture.detectChanges(); + userService = TestBed.inject(UserService); }); it('should create', () => { + localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJqb2FvMTV2aWN0b3IwOEBnbWFpbC5jb20iLCJleHAiOjE2OTkzMTI5MzV9.1B9qBJt8rErwBKyD5JCdsPozsw86oQ38tdfDuMM2HFI') + + component.user = mockData; + fixture.detectChanges(); expect(component).toBeTruthy(); }); + + it('should call getUser', () => { + const mySpy = spyOn(userService, 'getUser').and.callThrough(); + component.getUser(); + expect(mySpy).toHaveBeenCalled(); + }); + + it('should call getUser and return an error', () => { + const mySpy = spyOn(userService, 'getUser').and.returnValue(throwError(() => new Error('Erro'))); + component.getUser(); + expect(mySpy).toHaveBeenCalled(); + }); + + // it('should call navigator method when "Editar perfil" is clicked', () => { + // component.user = mockData; + // spyOn(component, 'navigatorEdit').and.callThrough(); + // const editProfilebutton = fixture.nativeElement.querySelector('.text-white'); + // fixture.detectChanges(); + // // editProfilebutton.click(); + // expect(true).toBe(true); + // }); + }); diff --git a/src/app/pages/profile/profile.component.ts b/src/app/pages/profile/profile.component.ts index c858274e..74f5549b 100644 --- a/src/app/pages/profile/profile.component.ts +++ b/src/app/pages/profile/profile.component.ts @@ -17,7 +17,7 @@ export class ProfileComponent { private router: Router, private fb: FormBuilder, private userService: UserService - ) {} + ) { } ngOnInit(): void { this.setUserIdFromToken(localStorage.getItem('token') as string); @@ -32,7 +32,6 @@ export class ProfileComponent { getUser() { this.userService.getUser(this.userId).subscribe({ next: (data) => { - console.log(data); this.user = data; }, error: (error) => { @@ -41,19 +40,14 @@ export class ProfileComponent { }); } - navigator(rota: string): void { - this.router.navigate([rota]); - } - navigatorEdit(): void { - console.log('Dados do usuário:', this.user); const navigationExtras: NavigationExtras = { state: { user: this.user } }; - this.router.navigate([`/editUser/${this.userId}`], navigationExtras); + this.router.navigate([`/editUser`], navigationExtras); } } diff --git a/src/app/pages/register/register.component.spec.ts b/src/app/pages/register/register.component.spec.ts index f9755337..af78b8bd 100644 --- a/src/app/pages/register/register.component.spec.ts +++ b/src/app/pages/register/register.component.spec.ts @@ -1,23 +1,95 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { RegisterComponent } from './register.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { AuthService } from 'src/app/services/auth.service'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { of, throwError } from 'rxjs'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ActiveAccountComponent } from '../active-account/active-account.component'; + +const mockData: any = { + "name": "Mario", + "email": "mario@gmail.com", + "connection": "ALUNO", + "password": "123456", + "confirmPassword": "123456", +} + +class AuthServiceMock { + constructor() { } + registerUser() { + return of({ success: true }); + } +} describe('RegisterComponent', () => { let component: RegisterComponent; let fixture: ComponentFixture; + let authService: AuthService; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ RegisterComponent ] + imports: [HttpClientTestingModule, ReactiveFormsModule, + RouterTestingModule.withRoutes( + [ + { path: 'activateAccount', component: ActiveAccountComponent }, + ] + ) + ], + providers: [{ provide: AuthService, useValue: new AuthServiceMock() }, FormBuilder], + declarations: [RegisterComponent] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(RegisterComponent); component = fixture.componentInstance; + authService = TestBed.inject(AuthService); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should have a valid form on initialization', () => { + fixture.detectChanges(); + expect(component.userForm).toBeTruthy(); + }); + + it('should call register method when the form is submitted', () => { + fixture.detectChanges(); + spyOn(component, 'register').and.callThrough(); + const form = component.userForm; + form.setValue(mockData); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(component.register).toHaveBeenCalled(); + }); + + it('should call alert when form is not valid', () => { + spyOn(component, 'register').and.callThrough(); + const alertSpy = spyOn(window, 'alert'); + fixture.detectChanges(); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); + }); + + it('should call register and return an error', () => { + fixture.detectChanges(); + const form = component.userForm; + form.setValue(mockData); + const mySpy = spyOn(authService, 'registerUser').and.returnValue(throwError(() => new Error('Erro'))); + component.register(); + expect(mySpy).toHaveBeenCalled(); + }); + }); diff --git a/src/app/pages/reset-password/reset-password.component.spec.ts b/src/app/pages/reset-password/reset-password.component.spec.ts index 1d70527c..6563e9f0 100644 --- a/src/app/pages/reset-password/reset-password.component.spec.ts +++ b/src/app/pages/reset-password/reset-password.component.spec.ts @@ -1,23 +1,87 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ResetPasswordComponent } from './reset-password.component'; +import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { AuthService } from '../../services/auth.service'; +import { of, throwError } from 'rxjs'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; + +class AuthServiceMock { + constructor() { } + updatePassword() { + return of({ success: true }); + } +} + describe('ResetPasswordComponent', () => { let component: ResetPasswordComponent; let fixture: ComponentFixture; + let authService: AuthService; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ ResetPasswordComponent ] - }) - .compileComponents(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, ReactiveFormsModule], + declarations: [ResetPasswordComponent], + providers: [ + { provide: AuthService, useValue: new AuthServiceMock() }, + { provide: FormBuilder }, + ], + }); fixture = TestBed.createComponent(ResetPasswordComponent); + authService = TestBed.inject(AuthService); component = fixture.componentInstance; - fixture.detectChanges(); }); it('should create', () => { + fixture.detectChanges(); expect(component).toBeTruthy(); }); + + it('should create the userForm correctly', () => { + fixture.detectChanges(); + + expect(component.userForm).toBeTruthy(); + expect(component.userForm.controls['email']).toBeTruthy(); + expect(component.userForm.controls['code']).toBeTruthy(); + expect(component.userForm.controls['password']).toBeTruthy(); + expect(component.userForm.controls['confirmPassword']).toBeTruthy(); + }); + + it('should call changePassword', () => { + fixture.detectChanges(); + spyOn(component, 'changePassword').and.callThrough(); + spyOn(window, 'alert'); + const form = component.userForm; + form.setValue({ email: 'test@example.com', code: '123456', password: 'password', confirmPassword: 'password' }); + fixture.detectChanges(); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(component.changePassword).toHaveBeenCalled(); + }); + + it('should show an alert on error', () => { + const alertSpy = spyOn(window, 'alert'); + spyOn(component, 'changePassword').and.callThrough(); + fixture.detectChanges(); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); + }); + + it('should call changePassword and return an error', () => { + fixture.detectChanges(); + const form = component.userForm; + form.setValue({ email: 'test@example.com', code: '123456', password: 'password', confirmPassword: 'password' }); + const mySpy = spyOn(authService, 'updatePassword').and.returnValue(throwError(() => new Error('Erro'))); + component.changePassword(); + expect(mySpy).toHaveBeenCalled(); + }); }); diff --git a/src/app/pages/reset-password/reset-password.component.ts b/src/app/pages/reset-password/reset-password.component.ts index 38fd2ca4..f2b547a9 100644 --- a/src/app/pages/reset-password/reset-password.component.ts +++ b/src/app/pages/reset-password/reset-password.component.ts @@ -16,7 +16,7 @@ export class ResetPasswordComponent implements OnInit { private router: Router, private fb: FormBuilder, private authService: AuthService - ) {} + ) { } ngOnInit(): void { this.userForm = this.fb.group( @@ -36,7 +36,6 @@ export class ResetPasswordComponent implements OnInit { if (this.userForm.valid) { this.authService.updatePassword(this.userForm.value).subscribe({ next: (data) => { - console.log(data); alert('Senha alterada com sucesso!'); this.navigator('/login'); }, diff --git a/src/app/pages/video-viewer/video-viewer.component.spec.ts b/src/app/pages/video-viewer/video-viewer.component.spec.ts index feaf9281..0fcedda5 100644 --- a/src/app/pages/video-viewer/video-viewer.component.spec.ts +++ b/src/app/pages/video-viewer/video-viewer.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { RouterTestingModule } from "@angular/router/testing"; import { VideoViewerComponent } from './video-viewer.component'; -import { VideoService } from './video.service'; +import { VideoService } from '../../services/video.service'; import { SafePipe } from 'src/app/pipes/safe.pipe'; describe('VideoViewerComponent', () => { diff --git a/src/app/pages/video/video.component.spec.ts b/src/app/pages/video/video.component.spec.ts index 79fd12db..a879e27c 100644 --- a/src/app/pages/video/video.component.spec.ts +++ b/src/app/pages/video/video.component.spec.ts @@ -1,33 +1,277 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { VideoComponent } from './video.component'; -import { VideoService } from './video.service'; +import { VideoService } from '../../services/video.service'; +import { of, throwError } from 'rxjs'; + +const mockData = { + "qtTotal": 110, + "videoList": [ + { + "id": 142471, + "title": "Sala de Reunião 04 do NTE", + "description": "Sala de Reunião 04 do NTE", + "keywords": "Sala de Reunião 04 do NTE", + "visibility": "PUBLIC", + "duration": 561835, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 41, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1630091029232.png" + } + ], + "userOwner": { + "id": 30684, + "name": "Fabio Ferreira de Oliveira" + } + }, + { + "id": 180741, + "title": "Mulheres que inspiram - Profa. Carla Rocha", + "description": "

Entrevista com a Professora Carla Rocha (UnB) como parte do projeto Mulheres que Inspiram. Adeia surgiu através da leitura do texto Eu programo, tu programas, elx hackea: mulheres hackers e perspectivas tecnopolíticas e do interesse em dar visibilidade a mulheres que atuam na área de software livre e educação aberta. Transcrição do áudio disponível..em https://pt.wikiversity.org/wiki/Educa%C3%A7%C3%A3o_Aberta/Mulheres_que_inspiram


Disponível com uma licença CC-BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/).

", + "keywords": "mulheres, computação, professora, ciência da computação, gênero", + "visibility": "PUBLIC", + "duration": 508459, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 36, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1679168690066.jpg" + } + ], + "userOwner": { + "id": 44590, + "name": "Tel Amiel", + "avatar": "https://eduplay.rnp.br/portal/assets/users/images/1683107832084.jpg" + } + }, + { + "id": 184760, + "title": "Dia 4: Minicurso de Extensão - UnB - Racontez-nous votre histoire", + "keywords": "mconf", + "visibility": "PUBLIC", + "duration": 5161003, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 24, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1687455434581.png" + } + ], + "userOwner": { + "id": 45799, + "name": "Denise Gisele de Britto Damasco" + } + }, + { + "id": 184518, + "title": "Dia 1: Minicurso de Extensão - UnB - Présentation Denise Damasco", + "keywords": "mconf", + "visibility": "PUBLIC", + "duration": 3532366, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 24, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1686872273578.png" + } + ], + "userOwner": { + "id": 45799, + "name": "Denise Gisele de Britto Damasco" + } + }, + { + "id": 111840, + "title": "Aulas Síncronas ás 14 horas as terças-feiras e quintas-feiras", + "description": "Aulas Síncronas ás 14 horas as terças-feiras e quintas-feiras", + "keywords": "Aulas Síncronas ás 14 horas as terças-feiras e quintas-feiras", + "visibility": "PUBLIC", + "duration": 6111573, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 38, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1621608406505.png" + } + ], + "userOwner": { + "id": 28703, + "name": "Jorlandio Francisco Felix" + } + }, + { + "id": 141981, + "title": "Liliane Campos Machado", + "description": "Liliane Campos Machado", + "keywords": "Liliane Campos Machado", + "visibility": "PUBLIC", + "duration": 6482260, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 162, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1629828306049.png" + } + ], + "userOwner": { + "id": 29754, + "name": "Liliane Campos Machado" + } + }, + { + "id": 111535, + "title": "NTE videoconferências", + "description": "NTE videoconferências", + "keywords": "NTE videoconferências", + "visibility": "PUBLIC", + "duration": 7508000, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 73, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1621383436732.png" + } + ], + "userOwner": { + "id": 19994, + "name": "Endryl Francelino de Souza" + } + }, + { + "id": 184759, + "title": "Dia 3: Minicurso de Extensão - UnB - Racontez-nous votre histoire", + "keywords": "mconf", + "visibility": "PUBLIC", + "duration": 5175560, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 24, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1687454416354.png" + } + ], + "userOwner": { + "id": 45799, + "name": "Denise Gisele de Britto Damasco" + } + }, + { + "id": 179217, + "title": "Patricia Tuxi dos Santos", + "description": "Patricia Tuxi dos Santos", + "keywords": "Patricia Tuxi dos Santos", + "visibility": "PUBLIC", + "duration": 109163, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 42, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1675876887636.png" + } + ], + "userOwner": { + "id": 44725, + "name": "Patricia Tuxi dos Santos" + } + }, + { + "id": 142458, + "title": "Sala de Reunião 04 do NTE", + "description": "Sala de Reunião 04 do NTE", + "keywords": "Sala de Reunião 04 do NTE", + "visibility": "PUBLIC", + "duration": 2750269, + "generateLibras": true, + "generateSubtitle": true, + "qtAccess": 75, + "qtLikes": 0, + "images": [ + { + "type": "DEFAULT", + "href": "https://eduplay.rnp.br/portal/assets/videos/images/1630088527294.png" + } + ], + "userOwner": { + "id": 30684, + "name": "Fabio Ferreira de Oliveira" + } + } + ] +}; + +class VideoServiceMock { + constructor() { } + findAll() { + return of(mockData); + } +} describe('VideoComponent', () => { let component: VideoComponent; let fixture: ComponentFixture; + let videoService: VideoService; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [VideoComponent], imports: [HttpClientTestingModule], - providers: [{ provide: VideoService }] + providers: [{ provide: VideoService, useValue: new VideoServiceMock() }] }) .compileComponents(); fixture = TestBed.createComponent(VideoComponent); component = fixture.componentInstance; - fixture.detectChanges(); + videoService = TestBed.inject(VideoService); }); it('should create', () => { expect(component).toBeTruthy(); }); - // it('shoud have videos thumbnails', () => { - // const fixture = TestBed.createComponent(VideoComponent); - // fixture.detectChanges(); - // const compiled = fixture.nativeElement as HTMLElement; - // expect(compiled.querySelectorAll('img')).toBe(10); - // }) + it('shoud call findAll', () => { + spyOn(component, 'findAll'); + fixture.detectChanges(); + expect(component.findAll).toHaveBeenCalled(); + }) + + it('should call findAll and return a list of videos', () => { + const mySpy = spyOn(videoService, 'findAll').and.callThrough(); + component.findAll(); + expect(mySpy).toHaveBeenCalled(); + }); + + it('should call findAll and return an error', () => { + const mySpy = spyOn(videoService, 'findAll').and.returnValue(throwError(() => new Error('Erro'))); + component.findAll(); + expect(mySpy).toHaveBeenCalled(); + }); }); diff --git a/src/app/pages/video/video.component.ts b/src/app/pages/video/video.component.ts index 0267934c..1815a6df 100644 --- a/src/app/pages/video/video.component.ts +++ b/src/app/pages/video/video.component.ts @@ -16,15 +16,19 @@ export class VideoComponent implements OnInit { constructor(private videoService: VideoService) { } ngOnInit(): void { - this.videoService.findAll().subscribe( - (res: HttpResponse) => { - this.videosEduplay = !!res.body?.videoList ? res.body.videoList : []; + this.findAll(); + } + findAll(): void { + this.videoService.findAll().subscribe({ + next: (data) => { + // console.log(data); + this.videosEduplay = data.body?.videoList || []; }, - (error: HttpErrorResponse) => { - console.log('error', error.message); + error: (error) => { + console.error(error); }, - () => { } + } ); } } diff --git a/src/app/services/auth.guard.spec.ts b/src/app/services/auth.guard.spec.ts index 68889d22..b09a90ef 100644 --- a/src/app/services/auth.guard.spec.ts +++ b/src/app/services/auth.guard.spec.ts @@ -1,16 +1,35 @@ import { TestBed } from '@angular/core/testing'; - +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { AuthGuard } from './auth.guard'; +import { RouterTestingModule } from '@angular/router/testing'; +import { LoginComponent } from '../pages/login/login.component'; describe('AuthGuard', () => { let guard: AuthGuard; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [{ path: 'login', component: LoginComponent }] + )], providers: [AuthGuard], declarations: [LoginComponent] + }); guard = TestBed.inject(AuthGuard); }); it('should be created', () => { expect(guard).toBeTruthy(); }); + + it('should return true for a logged in user', () => { + localStorage.setItem('token', 'testtoken'); + expect(guard.canActivate()).toBe(true); + } + ); + + it('should return false for a logged out user', () => { + localStorage.removeItem('token'); + expect(guard.canActivate()).toBe(false); + } + ); + }); diff --git a/src/app/services/auth.service.spec.ts b/src/app/services/auth.service.spec.ts index f1251cac..d4bec24a 100644 --- a/src/app/services/auth.service.spec.ts +++ b/src/app/services/auth.service.spec.ts @@ -1,16 +1,122 @@ import { TestBed } from '@angular/core/testing'; - +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { AuthService } from './auth.service'; describe('AuthService', () => { let service: AuthService; + let httpMock: HttpTestingController; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [AuthService] }); service = TestBed.inject(AuthService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); }); it('should be created', () => { expect(service).toBeTruthy(); }); + + it('should register a user', () => { + const userResponse: any = { + "name": "Mario", + "connection": "ALUNO", + "email": "mario@gmail.com", + "password": "123456", + } + service.registerUser(userResponse).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/auth/register`); + expect(req.request.method).toBe('POST'); + req.flush(userResponse); + }); + + it('should login a user', () => { + const userResponse: any = { + "email": "mario@gmail.com", + "password": "123456", + } + + service.loginUser(userResponse).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/auth/login`); + expect(req.request.method).toBe('POST'); + req.flush(userResponse); + }); + + it('should activate a user account', () => { + const userResponse: any = { + "email": "mario@gmail.com", + "code": "901472", + } + service.activeAccount(userResponse).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/auth/activate-account`); + expect(req.request.method).toBe('PATCH'); + req.flush(userResponse); + }); + + it('should resend a code', () => { + const userResponse: any = { + "email": "mario@gmail.com", + } + service.resendCode(userResponse).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/auth/resend-code`); + expect(req.request.method).toBe('POST'); + req.flush(userResponse); + }); + + it('should send an email to reset password', () => { + const userResponse: any = { + "email": "mario@gmail.com", + } + service.sendEmailPassword(userResponse).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/auth/reset-password/request`); + expect(req.request.method).toBe('POST'); + req.flush(userResponse); + }); + + it('should verify code to reset password', () => { + const userResponse: any = { + "email": "mario@gmail.com", + "code": "901472", + } + service.verifyCodePassword(userResponse).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/auth/reset-password/verify`); + expect(req.request.method).toBe('POST'); + req.flush(userResponse); + }); + + it('should update password', () => { + const userResponse: any = { + "email": "mario@gmail.com", + "password": "123456", + "code": "901472", + } + service.updatePassword(userResponse).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/auth/reset-password/change`); + expect(req.request.method).toBe('PATCH'); + req.flush(userResponse); + }); + + it('should logout', () => { + localStorage.setItem('token', 'testtoken'); + service.logout(); + expect(localStorage.getItem('token')).toBeNull(); + }); + }); diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index c878c901..b94bb0cc 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -8,7 +8,7 @@ import { Observable } from 'rxjs'; providedIn: 'root' }) export class AuthService { - private apiURL = environment.apiURL; + public apiURL = environment.apiURL; constructor(private http: HttpClient) { } diff --git a/src/app/services/user-token-interceptor.service.spec.ts b/src/app/services/user-token-interceptor.service.spec.ts index d2db1c7c..085d5581 100644 --- a/src/app/services/user-token-interceptor.service.spec.ts +++ b/src/app/services/user-token-interceptor.service.spec.ts @@ -1,16 +1,39 @@ import { TestBed } from '@angular/core/testing'; - import { UserTokenInterceptor } from './user-token-interceptor.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { UserService } from './user.service'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; + describe('TokenInterceptorService', () => { let service: UserTokenInterceptor; + let userService: UserService; + let httpMock: HttpTestingController; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [UserService, { provide: HTTP_INTERCEPTORS, useClass: UserTokenInterceptor, multi: true }], + }); service = TestBed.inject(UserTokenInterceptor); + userService = TestBed.inject(UserService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); }); it('should be created', () => { expect(service).toBeTruthy(); }); + + it('should add header Authorization to request', () => { + localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJqb2FvMTV2aWN0b3IwOEBnbWFpbC5jb20iLCJleHAiOjE2OTkzMTI5MzV9.1B9qBJt8rErwBKyD5JCdsPozsw86oQ38tdfDuMM2HFI'); + userService.getAllUsers().subscribe((res) => { + expect(res).toBeTruthy(); + }); + const req = httpMock.expectOne(`${userService.apiURL}/users`); + expect(req.request.headers.has('Authorization')).toEqual(true); + }); }); diff --git a/src/app/services/user.service.spec.ts b/src/app/services/user.service.spec.ts index 3f804c9f..cc2d4167 100644 --- a/src/app/services/user.service.spec.ts +++ b/src/app/services/user.service.spec.ts @@ -1,16 +1,106 @@ import { TestBed } from '@angular/core/testing'; - import { UserService } from './user.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HttpClientModule } from '@angular/common/http'; describe('UserService', () => { let service: UserService; + let httpMock: HttpTestingController; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ imports: [HttpClientTestingModule, HttpClientModule], providers: [UserService] }); service = TestBed.inject(UserService); + httpMock = TestBed.inject(HttpTestingController); }); it('should be created', () => { expect(service).toBeTruthy(); }); + + it('should return a single user', () => { + const userResponse: any = { + "id": 1, + "name": "Mario", + "connection": "ALUNO", + "email": "mario@gmail.com", + "role": "USER", + "is_active": true + } + service.getUser(1).subscribe(res => { + expect(res).toEqual(userResponse); + + }); + const req = httpMock.expectOne(`${service.apiURL}/users/1`); + expect(req.request.method).toBe('GET'); + req.flush(userResponse); + }); + + it('should return a list of users', async () => { + const userResponse: any = [ + { + "id": 1, + "name": "Mario", + "connection": "ALUNO", + "email": "mario@example.com", + "role": "USER", + "is_active": true + }, + { + "id": 2, + "name": "Luigi", + "connection": "ALUNO", + "email": "luigi@example.com", + "role": "USER", + "is_active": true + }]; + service.getAllUsers().subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/users`); + expect(req.request.method).toBe('GET'); + req.flush(userResponse); + } + ); + + it('should update a user', () => { + const userResponse: any = { + "id": 1, + "name": "Mario", + "connection": "ALUNO", + "email": "mario@gmail.com", + "role": "USER", + "is_active": true + } + service.updateUser(1, { "name": "Mario", "connection": "PROFESSOR", "email": "mario@gmail.com" }).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/users/1`); + expect(req.request.method).toBe('PATCH'); + req.flush(userResponse); + } + ); + + it('should delete a user', () => { + const userResponse: any = { + "id": 1, + "name": "Mario", + "connection": "ALUNO", + "email": "mario@gmail.com", + "role": "USER", + "is_active": true + } + service.deleteUser(1).subscribe(res => { + expect(res).toEqual(userResponse); + }); + const req = httpMock.expectOne(`${service.apiURL}/users/1`); + expect(req.request.method).toBe('DELETE'); + req.flush(userResponse); + } + ); + + afterEach(() => { + httpMock.verify(); + } + ); + }); diff --git a/src/app/services/user.service.ts b/src/app/services/user.service.ts index 3ffdd629..bb9279f9 100644 --- a/src/app/services/user.service.ts +++ b/src/app/services/user.service.ts @@ -7,7 +7,7 @@ import { Observable } from 'rxjs'; providedIn: 'root' }) export class UserService { - private apiURL = environment.apiURL; + public apiURL = environment.apiURL; constructor(private http: HttpClient) { } diff --git a/src/app/services/video.service.spec.ts b/src/app/services/video.service.spec.ts index 3378e3ce..5230f2a5 100644 --- a/src/app/services/video.service.spec.ts +++ b/src/app/services/video.service.spec.ts @@ -2,7 +2,7 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { VideoService } from './video.service'; import { EDUPLAY_API_URL, UNB_ID } from 'src/app/app.constant'; -import { EDUPLAY_CLIENT_KEY } from 'src/app/secret/eduplay.credentials'; +import { EDUPLAY_CLIENT_KEY } from '../environment/environment'; describe('VideoService', () => { let service: VideoService;