From 8c1095bf59830b38a8c2098603a4893c83bfb1df Mon Sep 17 00:00:00 2001 From: DaviMarinho Date: Sun, 5 Nov 2023 22:36:01 -0300 Subject: [PATCH 01/16] Add ci/cd on frontend Signed-off-by: DaviMarinho --- .github/workflows/ci-cd.yml | 38 +++++++++++++++ angular.json | 2 +- package-lock.json | 4 +- package.json | 2 +- src/app/pages/login/login.component.spec.ts | 47 +++++++++++++++++-- .../video-viewer.component.spec.ts | 2 +- src/app/pages/video/video.component.spec.ts | 2 +- src/app/services/video.service.spec.ts | 2 +- 8 files changed, 87 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/ci-cd.yml diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 00000000..b27e8ca9 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,38 @@ +name: CI/CD Pipeline Frontend + +on: push + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: 16.x + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Test + run: ng test + + deploy: + needs: build + runs-on: ubuntu-latest + + steps: + - name: Deploy to Heroku + uses: akhileshns/heroku-deploy@v3.12.12 + with: + heroku_api_key: ${{ secrets.HEROKU_API_KEY }} + heroku_app_name: your-heroku-app-name + heroku_email: your-email@example.com + heroku_deploy_branch: main diff --git a/angular.json b/angular.json index b0eaf8d8..99fd3ee6 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": [ 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..34a46221 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", diff --git a/src/app/pages/login/login.component.spec.ts b/src/app/pages/login/login.component.spec.ts index 10eca249..67f55c61 100644 --- a/src/app/pages/login/login.component.spec.ts +++ b/src/app/pages/login/login.component.spec.ts @@ -1,16 +1,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { LoginComponent } from './login.component'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture; beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ LoginComponent ] - }) - .compileComponents(); + TestBed.configureTestingModule({ + declarations: [LoginComponent], + imports: [ReactiveFormsModule], + providers: [FormBuilder], + }).compileComponents(); fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; @@ -20,4 +21,40 @@ describe('LoginComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should have a valid form on initialization', () => { + expect(component.userForm.valid).toBeFalse(); + }); + + it('should call login method when the form is submitted', () => { + spyOn(component, 'login'); + const form = component.userForm; + form.setValue({ email: 'test@example.com', password: 'password' }); + fixture.detectChanges(); + + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); + + expect(component.login).toHaveBeenCalled(); + }); + + it('should call navigator method when "Esqueceu a senha?" is clicked', () => { + spyOn(component, 'navigator'); + 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'); + const registerLink = + fixture.nativeElement.querySelector('.text-blue-brand'); + registerLink.click(); + + expect(component.navigator).toHaveBeenCalledWith('/register'); + }); + }); }); 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..f91dc014 100644 --- a/src/app/pages/video/video.component.spec.ts +++ b/src/app/pages/video/video.component.spec.ts @@ -1,7 +1,7 @@ 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'; describe('VideoComponent', () => { let component: VideoComponent; 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; From 6b2d27e2633f6576e60e8be4a26b42fe02a0735e Mon Sep 17 00:00:00 2001 From: davimarinho Date: Sun, 5 Nov 2023 23:14:43 -0300 Subject: [PATCH 02/16] Add tests and improve ci/cd yml Signed-off-by: davimarinho --- .github/workflows/ci-cd.yml | 2 +- karma.conf.js | 7 +++ src/app/app.component.spec.ts | 21 +------- .../reset-password.component.spec.ts | 54 +++++++++++++++---- 4 files changed, 55 insertions(+), 29 deletions(-) create mode 100644 karma.conf.js diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b27e8ca9..eaa58a5a 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -22,7 +22,7 @@ jobs: run: npm run build - name: Test - run: ng test + run: npm run test deploy: needs: build diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 00000000..4d844bdc --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,7 @@ +module.exports = function(config) { + config.set({ + basePath: '../..', + frameworks: ['jasmine'], + //... + }); + }; \ No newline at end of file diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 53f2b51f..b183db08 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -5,12 +5,8 @@ import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule - ], - declarations: [ - AppComponent - ], + imports: [RouterTestingModule], + declarations: [AppComponent], }).compileComponents(); }); @@ -19,17 +15,4 @@ describe('AppComponent', () => { const app = fixture.componentInstance; expect(app).toBeTruthy(); }); - - 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 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/reset-password/reset-password.component.spec.ts b/src/app/pages/reset-password/reset-password.component.spec.ts index 1d70527c..82aa9d5f 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,59 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ResetPasswordComponent } from './reset-password.component'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { AuthService } from '../../services/auth.service'; +import { of, throwError } from 'rxjs'; describe('ResetPasswordComponent', () => { let component: ResetPasswordComponent; let fixture: ComponentFixture; + let authService: jasmine.SpyObj; + let formBuilder: FormBuilder; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ ResetPasswordComponent ] - }) - .compileComponents(); + beforeEach(() => { + authService = jasmine.createSpyObj('AuthService', ['updatePassword']); + formBuilder = new FormBuilder(); + TestBed.configureTestingModule({ + declarations: [ResetPasswordComponent], + providers: [ + { provide: AuthService, useValue: authService }, + { provide: FormBuilder, useValue: formBuilder }, + ], + }); fixture = TestBed.createComponent(ResetPasswordComponent); component = fixture.componentInstance; - fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + it('should create the userForm correctly', () => { + component.ngOnInit(); + + 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 authService.updatePassword and navigate on success', () => { + const response = { /* mock your response here */ }; + authService.updatePassword.and.callFake(() => of(response)); + const navigateSpy = spyOn(component, 'navigator'); + + component.changePassword(); + + expect(authService.updatePassword).toHaveBeenCalledWith(component.userForm.value); + expect(navigateSpy).toHaveBeenCalledWith('/login'); + }); + + it('should show an alert on error', () => { + authService.updatePassword.and.callFake(() => throwError('Error')); + const consoleErrorSpy = spyOn(console, 'error'); + const alertSpy = spyOn(window, 'alert'); + + component.changePassword(); + + expect(consoleErrorSpy).toHaveBeenCalled(); + expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); }); }); From 543f8b5a4f9b62eec3524a0cec8df1265f6f4b15 Mon Sep 17 00:00:00 2001 From: davimarinho Date: Mon, 6 Nov 2023 15:12:35 -0300 Subject: [PATCH 03/16] Fix ci/cd Signed-off-by: davimarinho --- .github/workflows/ci-cd.yml | 38 --------------------- .github/workflows/continous-integration.yml | 26 ++++++++++++++ .github/workflows/heroku-deploy.yml | 25 ++++++++++++++ karma.conf.js | 13 +++++-- 4 files changed, 62 insertions(+), 40 deletions(-) delete mode 100644 .github/workflows/ci-cd.yml create mode 100644 .github/workflows/continous-integration.yml create mode 100644 .github/workflows/heroku-deploy.yml diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml deleted file mode 100644 index eaa58a5a..00000000 --- a/.github/workflows/ci-cd.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: CI/CD Pipeline Frontend - -on: push - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: 16.x - - - name: Install dependencies - run: npm install - - - name: Build - run: npm run build - - - name: Test - run: npm run test - - deploy: - needs: build - runs-on: ubuntu-latest - - steps: - - name: Deploy to Heroku - uses: akhileshns/heroku-deploy@v3.12.12 - with: - heroku_api_key: ${{ secrets.HEROKU_API_KEY }} - heroku_app_name: your-heroku-app-name - heroku_email: your-email@example.com - heroku_deploy_branch: main diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml new file mode 100644 index 00000000..df948e02 --- /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 \ 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..9a513266 --- /dev/null +++ b/.github/workflows/heroku-deploy.yml @@ -0,0 +1,25 @@ +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@v2 + - uses: akhileshns/heroku-deploy@v3.12.12 + with: + heroku_api_key: ${{secrets.HEROKU_API_KEY}} + heroku_app_name: "alectrion-2023-1" + heroku_email: ${{secrets.HEROKU_EMAIL}} + usedocker: true + docker_build_args: | + GATEWAY_URL + env: + GATEWAY_URL: ${{ secrets.GATEWAY_URL }} \ No newline at end of file diff --git a/karma.conf.js b/karma.conf.js index 4d844bdc..03273616 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -2,6 +2,15 @@ module.exports = function(config) { config.set({ basePath: '../..', frameworks: ['jasmine'], - //... - }); + browsers: [ + 'Chrome', + 'ChromeHeadlessCI' + ], + customLaunchers: { + ChromeHeadlessCI: { + base: 'ChromeHeadless', + flags: ['--no-sandbox'] + } + }, + }) }; \ No newline at end of file From b4148076c1db034d4af0de323adc84f3b7fd7fcf Mon Sep 17 00:00:00 2001 From: davimarinho Date: Mon, 6 Nov 2023 15:14:24 -0300 Subject: [PATCH 04/16] fix cd Signed-off-by: davimarinho --- .github/workflows/heroku-deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/heroku-deploy.yml b/.github/workflows/heroku-deploy.yml index 9a513266..5eed75ce 100644 --- a/.github/workflows/heroku-deploy.yml +++ b/.github/workflows/heroku-deploy.yml @@ -12,11 +12,11 @@ jobs: runs-on: ubuntu-latest name: heroku deploy steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: akhileshns/heroku-deploy@v3.12.12 with: heroku_api_key: ${{secrets.HEROKU_API_KEY}} - heroku_app_name: "alectrion-2023-1" + heroku_app_name: "unb-tv-2023-2" heroku_email: ${{secrets.HEROKU_EMAIL}} usedocker: true docker_build_args: | From 74956b8237b53cfc30ecd4577b0aec73cf31fa86 Mon Sep 17 00:00:00 2001 From: davimarinho Date: Mon, 6 Nov 2023 15:16:06 -0300 Subject: [PATCH 05/16] fix cd Signed-off-by: davimarinho --- .github/workflows/heroku-deploy.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/heroku-deploy.yml b/.github/workflows/heroku-deploy.yml index 5eed75ce..fa766d8d 100644 --- a/.github/workflows/heroku-deploy.yml +++ b/.github/workflows/heroku-deploy.yml @@ -18,8 +18,4 @@ jobs: heroku_api_key: ${{secrets.HEROKU_API_KEY}} heroku_app_name: "unb-tv-2023-2" heroku_email: ${{secrets.HEROKU_EMAIL}} - usedocker: true - docker_build_args: | - GATEWAY_URL - env: - GATEWAY_URL: ${{ secrets.GATEWAY_URL }} \ No newline at end of file + usedocker: true \ No newline at end of file From 55c6c1b50b1b111018153e4c38e7de7f76c6f656 Mon Sep 17 00:00:00 2001 From: davimarinho Date: Mon, 6 Nov 2023 15:25:15 -0300 Subject: [PATCH 06/16] Fix karma.conf Signed-off-by: davimarinho --- karma.conf.js | 83 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 03273616..2eea6e9f 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,16 +1,71 @@ -module.exports = function(config) { - config.set({ - basePath: '../..', - frameworks: ['jasmine'], - browsers: [ - 'Chrome', - 'ChromeHeadlessCI' +module.exports = (config) => { + const coverage = config.singleRun ? ["coverage"] : []; + + config.set({ + frameworks: ["jasmine"], + plugins: [ + "karma-jasmine", + "karma-webpack", + "karma-coverage", + "karma-remap-istanbul", + "karma-chrome-launcher", ], - customLaunchers: { - ChromeHeadlessCI: { - base: 'ChromeHeadless', - flags: ['--no-sandbox'] - } + files: [ + "./src/tests.entry.ts", + { + pattern: "**/*.map", + served: true, + included: false, + watched: true, + }, + ], + preprocessors: { + "./src/tests.entry.ts": ["webpack", "sourcemap"], + "./src/**/!(*.test|tests.*).(ts|js)": ["sourcemap"], + }, + + webpack: { + plugins, + entry: "./src/tests.entry.ts", + devtool: "inline-source-map", + resolve: { + extensions: [".webpack.js", ".web.js", ".ts", ".js"], + }, + module: { + rules: combinedLoaders().concat( + config.singleRun ? [loaders.istanbulInstrumenter] : [] + ), + }, + stats: { colors: true, reasons: true }, + }, + webpackServer: { + noInfo: true, // prevent console spamming when running in Karma! + }, + + reporters: ["spec"] + .concat(coverage) + .concat(coverage.length > 0 ? ["karma-remap-istanbul"] : []), + + remapIstanbulReporter: { + src: "coverage/chrome/coverage-final.json", + reports: { + html: "coverage", + }, + }, + + coverageReporter: { + reporters: [{ type: "json" }], + dir: "./coverage/", + subdir: (browser) => { + return browser.toLowerCase().split(/[ /-]/)[0]; // returns 'chrome' + }, }, - }) - }; \ No newline at end of file + + port: 9876, + browsers: ["Chrome"], // Alternatively: 'PhantomJS' + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + captureTimeout: 6000, + }); +}; From cea6d3233a9c2e8e22dc2167170dc3a4b570e249 Mon Sep 17 00:00:00 2001 From: davimarinho Date: Mon, 6 Nov 2023 15:33:17 -0300 Subject: [PATCH 07/16] fix ci Signed-off-by: davimarinho --- .github/workflows/continous-integration.yml | 6 +++++- package.json | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index df948e02..26ca0a98 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -6,6 +6,10 @@ jobs: build: runs-on: ubuntu-latest + defaults: + run: + working-directory: ng-toolkit + steps: - name: Checkout ✅ uses: actions/checkout@v3 @@ -23,4 +27,4 @@ jobs: run: npm run build - name: Test 📋 - run: npm run test \ No newline at end of file + run: npm run test:prod \ No newline at end of file diff --git a/package.json b/package.json index 34a46221..c7417f6a 100644 --- a/package.json +++ b/package.json @@ -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": { From cfd678d8c5614c5e822cd6de4ad03474f622d571 Mon Sep 17 00:00:00 2001 From: davimarinho Date: Mon, 6 Nov 2023 15:34:21 -0300 Subject: [PATCH 08/16] fix ci Signed-off-by: davimarinho --- .github/workflows/continous-integration.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index 26ca0a98..3c6e193e 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -6,10 +6,6 @@ jobs: build: runs-on: ubuntu-latest - defaults: - run: - working-directory: ng-toolkit - steps: - name: Checkout ✅ uses: actions/checkout@v3 From 3e904e928fc196383ee19705577df8033bec2733 Mon Sep 17 00:00:00 2001 From: davimarinho Date: Mon, 6 Nov 2023 16:24:07 -0300 Subject: [PATCH 09/16] Fix karma and git ignore Signed-off-by: davimarinho --- .gitignore | 2 +- karma.conf.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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/karma.conf.js b/karma.conf.js index 2eea6e9f..6df1e511 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -39,7 +39,7 @@ module.exports = (config) => { stats: { colors: true, reasons: true }, }, webpackServer: { - noInfo: true, // prevent console spamming when running in Karma! + noInfo: true, }, reporters: ["spec"] @@ -54,15 +54,15 @@ module.exports = (config) => { }, coverageReporter: { - reporters: [{ type: "json" }], + reporters: [{ type: "json" }, { type: "lcov"}, { type: "cobertura"}], dir: "./coverage/", subdir: (browser) => { - return browser.toLowerCase().split(/[ /-]/)[0]; // returns 'chrome' + return browser.toLowerCase().split(/[ /-]/)[0]; }, }, port: 9876, - browsers: ["Chrome"], // Alternatively: 'PhantomJS' + browsers: ["Chrome"], colors: true, logLevel: config.LOG_INFO, autoWatch: true, From 4631b5dddf458d015efcb23db38c94d69bd2d91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Mon, 6 Nov 2023 18:08:50 -0300 Subject: [PATCH 10/16] =?UTF-8?q?Altera=C3=A7=C3=B5es=20para=20rodar=20tes?= =?UTF-8?q?tes=20pelo=20docker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 +++ angular.json | 3 +- karma.conf.js | 100 ++++++++++++++++++++------------------------------ 3 files changed, 48 insertions(+), 61 deletions(-) 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 99fd3ee6..f55608fe 100644 --- a/angular.json +++ b/angular.json @@ -89,7 +89,8 @@ "styles": [ "src/styles.css" ], - "scripts": [] + "scripts": [], + "karmaConfig": "karma.conf.js" } } } diff --git a/karma.conf.js b/karma.conf.js index 6df1e511..3687fb8e 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,71 +1,51 @@ -module.exports = (config) => { - const coverage = config.singleRun ? ["coverage"] : []; +// 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({ - frameworks: ["jasmine"], + customLaunchers: { + ChromeHeadless: { + base: 'Chrome', + flags: [ + '--no-sandbox', + '--disable-gpu', + '--headless', + '--remote-debugging-port=9222' + ] + } + }, + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - "karma-jasmine", - "karma-webpack", - "karma-coverage", - "karma-remap-istanbul", - "karma-chrome-launcher", + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') ], - files: [ - "./src/tests.entry.ts", - { - pattern: "**/*.map", - served: true, - included: false, - watched: true, + 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` }, - ], - preprocessors: { - "./src/tests.entry.ts": ["webpack", "sourcemap"], - "./src/**/!(*.test|tests.*).(ts|js)": ["sourcemap"], + clearContext: false // leave Jasmine Spec Runner output visible in browser }, - - webpack: { - plugins, - entry: "./src/tests.entry.ts", - devtool: "inline-source-map", - resolve: { - extensions: [".webpack.js", ".web.js", ".ts", ".js"], - }, - module: { - rules: combinedLoaders().concat( - config.singleRun ? [loaders.istanbulInstrumenter] : [] - ), - }, - stats: { colors: true, reasons: true }, - }, - webpackServer: { - noInfo: true, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces }, - - reporters: ["spec"] - .concat(coverage) - .concat(coverage.length > 0 ? ["karma-remap-istanbul"] : []), - - remapIstanbulReporter: { - src: "coverage/chrome/coverage-final.json", - reports: { - html: "coverage", - }, - }, - coverageReporter: { - reporters: [{ type: "json" }, { type: "lcov"}, { type: "cobertura"}], - dir: "./coverage/", - subdir: (browser) => { - return browser.toLowerCase().split(/[ /-]/)[0]; - }, + dir: require('path').join(__dirname, './coverage/unb-tv-frontend'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'lcov' }, + { type: 'cobertura' }, + ] }, - - port: 9876, - browsers: ["Chrome"], - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - captureTimeout: 6000, + reporters: ['progress', 'kjhtml'], + browsers: ['Chrome'], + restartOnFileChange: true }); }; From b6108c591a1fc2409031d086140827f20682df4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Mon, 6 Nov 2023 19:09:53 -0300 Subject: [PATCH 11/16] Corrige arquivos de testes geral --- karma.conf.js | 1 + src/app/app.component.spec.ts | 16 +++++++++++----- .../active-account.component.spec.ts | 10 +++++++--- .../check-code-rest-password.component.spec.ts | 10 +++++++--- .../pages/edit-user/edit-user.component.spec.ts | 9 ++++++--- src/app/pages/login/login.component.spec.ts | 6 ++++-- src/app/pages/profile/profile.component.spec.ts | 7 ++++--- .../pages/register/register.component.spec.ts | 10 +++++++--- src/app/services/auth.guard.spec.ts | 4 ++-- src/app/services/auth.service.spec.ts | 4 ++-- src/app/services/user.service.spec.ts | 4 ++-- 11 files changed, 53 insertions(+), 28 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 3687fb8e..10dfe918 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -42,6 +42,7 @@ module.exports = function (config) { { type: 'html' }, { type: 'lcov' }, { type: 'cobertura' }, + { type: 'text-summary' } ] }, reporters: ['progress', 'kjhtml'], diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index b183db08..3e256296 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -1,18 +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], + declarations: [AppComponent, BackgroundComponent], }).compileComponents(); + + fixture = TestBed.createComponent(AppComponent); + component = fixture.componentInstance; + fixture.detectChanges(); }); it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); + expect(component).toBeTruthy(); }); }); 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..2d16512e 100644 --- a/src/app/pages/active-account/active-account.component.spec.ts +++ b/src/app/pages/active-account/active-account.component.spec.ts @@ -1,6 +1,8 @@ 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 { ReactiveFormsModule } from '@angular/forms'; describe('ActiveAccountComponent', () => { let component: ActiveAccountComponent; @@ -8,9 +10,11 @@ describe('ActiveAccountComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ActiveAccountComponent ] + imports: [HttpClientTestingModule, ReactiveFormsModule], + providers: [AuthService], + declarations: [ActiveAccountComponent] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(ActiveAccountComponent); component = fixture.componentInstance; 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..86a55a1c 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,6 +1,8 @@ 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 { ReactiveFormsModule } from '@angular/forms'; describe('CheckCodeRestPasswordComponent', () => { let component: CheckCodeRestPasswordComponent; @@ -8,9 +10,11 @@ describe('CheckCodeRestPasswordComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ CheckCodeRestPasswordComponent ] + imports: [HttpClientTestingModule, ReactiveFormsModule], + providers: [AuthService], + declarations: [CheckCodeRestPasswordComponent] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(CheckCodeRestPasswordComponent); component = fixture.componentInstance; 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..c1728898 100644 --- a/src/app/pages/edit-user/edit-user.component.spec.ts +++ b/src/app/pages/edit-user/edit-user.component.spec.ts @@ -1,6 +1,8 @@ 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 { ReactiveFormsModule } from '@angular/forms'; describe('EditUserComponent', () => { let component: EditUserComponent; @@ -8,9 +10,10 @@ describe('EditUserComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ EditUserComponent ] + imports: [HttpClientTestingModule, RouterTestingModule, ReactiveFormsModule], + declarations: [EditUserComponent] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(EditUserComponent); component = fixture.componentInstance; diff --git a/src/app/pages/login/login.component.spec.ts b/src/app/pages/login/login.component.spec.ts index 67f55c61..e03216cb 100644 --- a/src/app/pages/login/login.component.spec.ts +++ b/src/app/pages/login/login.component.spec.ts @@ -1,6 +1,8 @@ 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'; describe('LoginComponent', () => { let component: LoginComponent; @@ -8,9 +10,9 @@ describe('LoginComponent', () => { beforeEach(async () => { TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, ReactiveFormsModule], + providers: [FormBuilder, AuthService], declarations: [LoginComponent], - imports: [ReactiveFormsModule], - providers: [FormBuilder], }).compileComponents(); fixture = TestBed.createComponent(LoginComponent); diff --git a/src/app/pages/profile/profile.component.spec.ts b/src/app/pages/profile/profile.component.spec.ts index 246039d7..18ff5af5 100644 --- a/src/app/pages/profile/profile.component.spec.ts +++ b/src/app/pages/profile/profile.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ProfileComponent } from './profile.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('ProfileComponent', () => { let component: ProfileComponent; @@ -8,9 +8,10 @@ describe('ProfileComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ProfileComponent ] + imports: [HttpClientTestingModule], + declarations: [ProfileComponent] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(ProfileComponent); component = fixture.componentInstance; diff --git a/src/app/pages/register/register.component.spec.ts b/src/app/pages/register/register.component.spec.ts index f9755337..21c94378 100644 --- a/src/app/pages/register/register.component.spec.ts +++ b/src/app/pages/register/register.component.spec.ts @@ -1,6 +1,8 @@ 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 { ReactiveFormsModule } from '@angular/forms'; describe('RegisterComponent', () => { let component: RegisterComponent; @@ -8,9 +10,11 @@ describe('RegisterComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ RegisterComponent ] + imports: [HttpClientTestingModule, ReactiveFormsModule], + providers: [AuthService], + declarations: [RegisterComponent] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(RegisterComponent); component = fixture.componentInstance; diff --git a/src/app/services/auth.guard.spec.ts b/src/app/services/auth.guard.spec.ts index 68889d22..81d1c094 100644 --- a/src/app/services/auth.guard.spec.ts +++ b/src/app/services/auth.guard.spec.ts @@ -1,12 +1,12 @@ import { TestBed } from '@angular/core/testing'; - +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { AuthGuard } from './auth.guard'; describe('AuthGuard', () => { let guard: AuthGuard; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [AuthGuard] }); guard = TestBed.inject(AuthGuard); }); diff --git a/src/app/services/auth.service.spec.ts b/src/app/services/auth.service.spec.ts index f1251cac..3acf0447 100644 --- a/src/app/services/auth.service.spec.ts +++ b/src/app/services/auth.service.spec.ts @@ -1,12 +1,12 @@ import { TestBed } from '@angular/core/testing'; - +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { AuthService } from './auth.service'; describe('AuthService', () => { let service: AuthService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [AuthService] }); service = TestBed.inject(AuthService); }); diff --git a/src/app/services/user.service.spec.ts b/src/app/services/user.service.spec.ts index 3f804c9f..bd802828 100644 --- a/src/app/services/user.service.spec.ts +++ b/src/app/services/user.service.spec.ts @@ -1,12 +1,12 @@ import { TestBed } from '@angular/core/testing'; - import { UserService } from './user.service'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('UserService', () => { let service: UserService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [UserService] }); service = TestBed.inject(UserService); }); From 5e655e231599335a9c628d9f6dcd4ed6615e5e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Mon, 6 Nov 2023 21:22:19 -0300 Subject: [PATCH 12/16] Adiciona testes --- src/app/pages/login/login.component.spec.ts | 17 ++-- src/app/services/auth.guard.spec.ts | 21 ++++- src/app/services/user.service.spec.ts | 94 ++++++++++++++++++++- src/app/services/user.service.ts | 2 +- 4 files changed, 122 insertions(+), 12 deletions(-) diff --git a/src/app/pages/login/login.component.spec.ts b/src/app/pages/login/login.component.spec.ts index e03216cb..381ddc62 100644 --- a/src/app/pages/login/login.component.spec.ts +++ b/src/app/pages/login/login.component.spec.ts @@ -50,13 +50,14 @@ describe('LoginComponent', () => { expect(component.navigator).toHaveBeenCalledWith('/sendCodeResetPassword'); - it('should call navigator method when "Cadastre-se" is clicked', () => { - spyOn(component, 'navigator'); - const registerLink = - fixture.nativeElement.querySelector('.text-blue-brand'); - registerLink.click(); - - expect(component.navigator).toHaveBeenCalledWith('/register'); - }); + }); + + it('should call navigator method when "Cadastre-se" is clicked', () => { + spyOn(component, 'navigator'); + const registerLink = + fixture.nativeElement.querySelector('.text-blue-brand'); + registerLink.click(); + + expect(component.navigator).toHaveBeenCalledWith('/register'); }); }); diff --git a/src/app/services/auth.guard.spec.ts b/src/app/services/auth.guard.spec.ts index 81d1c094..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({ imports: [HttpClientTestingModule], providers: [AuthGuard] }); + 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/user.service.spec.ts b/src/app/services/user.service.spec.ts index bd802828..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 } from '@angular/common/http/testing'; +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({ imports: [HttpClientTestingModule], providers: [UserService] }); + 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) { } From c57671bf2ecb4b32e1940473a685c312f2edc984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 7 Nov 2023 12:21:07 -0300 Subject: [PATCH 13/16] Adiciona teste video, Auth.service e interceptor --- src/app/pages/video/video.component.spec.ts | 260 +++++++++++++++++- src/app/pages/video/video.component.ts | 16 +- src/app/services/auth.service.spec.ts | 108 +++++++- src/app/services/auth.service.ts | 2 +- .../user-token-interceptor.service.spec.ts | 27 +- 5 files changed, 395 insertions(+), 18 deletions(-) diff --git a/src/app/pages/video/video.component.spec.ts b/src/app/pages/video/video.component.spec.ts index f91dc014..e8d64e0b 100644 --- a/src/app/pages/video/video.component.spec.ts +++ b/src/app/pages/video/video.component.spec.ts @@ -2,32 +2,276 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { VideoComponent } from './video.component'; 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..187e13e0 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.service.spec.ts b/src/app/services/auth.service.spec.ts index 3acf0447..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 } from '@angular/common/http/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({ 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..49a7a87d 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', 'testtoken'); + userService.getAllUsers().subscribe((res) => { + expect(res).toBeTruthy(); + }); + const req = httpMock.expectOne(`${userService.apiURL}/users`); + expect(req.request.headers.has('Authorization')).toEqual(true); + }); }); From 93007e9a06fb208d51974ca99123ed8460247d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 7 Nov 2023 15:16:11 -0300 Subject: [PATCH 14/16] Adiciona teste de resetar senha --- .../reset-password.component.spec.ts | 67 +++++++++++++------ src/app/pages/video/video.component.ts | 2 +- .../user-token-interceptor.service.spec.ts | 2 +- 3 files changed, 49 insertions(+), 22 deletions(-) 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 82aa9d5f..9eea9864 100644 --- a/src/app/pages/reset-password/reset-password.component.spec.ts +++ b/src/app/pages/reset-password/reset-password.component.spec.ts @@ -1,32 +1,45 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ResetPasswordComponent } from './reset-password.component'; -import { FormBuilder, FormGroup } from '@angular/forms'; +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: jasmine.SpyObj; - let formBuilder: FormBuilder; + let authService: AuthService; beforeEach(() => { - authService = jasmine.createSpyObj('AuthService', ['updatePassword']); - formBuilder = new FormBuilder(); TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, ReactiveFormsModule], declarations: [ResetPasswordComponent], providers: [ - { provide: AuthService, useValue: authService }, - { provide: FormBuilder, useValue: formBuilder }, + { provide: AuthService, useValue: new AuthServiceMock() }, + { provide: FormBuilder }, ], }); fixture = TestBed.createComponent(ResetPasswordComponent); + authService = TestBed.inject(AuthService); component = fixture.componentInstance; }); + it('should create', () => { + fixture.detectChanges(); + expect(component).toBeTruthy(); + }); + it('should create the userForm correctly', () => { - component.ngOnInit(); + fixture.detectChanges(); expect(component.userForm).toBeTruthy(); expect(component.userForm.controls['email']).toBeTruthy(); @@ -35,25 +48,39 @@ describe('ResetPasswordComponent', () => { expect(component.userForm.controls['confirmPassword']).toBeTruthy(); }); - it('should call authService.updatePassword and navigate on success', () => { - const response = { /* mock your response here */ }; - authService.updatePassword.and.callFake(() => of(response)); - const navigateSpy = spyOn(component, 'navigator'); + it('should call changePassword', () => { + fixture.detectChanges(); + spyOn(component, 'changePassword').and.callThrough(); + const form = component.userForm; + form.setValue({ email: 'test@example.com', code: '123456', password: 'password', confirmPassword: 'password' }); + fixture.detectChanges(); - component.changePassword(); + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); + submitButton.click(); - expect(authService.updatePassword).toHaveBeenCalledWith(component.userForm.value); - expect(navigateSpy).toHaveBeenCalledWith('/login'); + expect(component.changePassword).toHaveBeenCalled(); }); it('should show an alert on error', () => { - authService.updatePassword.and.callFake(() => throwError('Error')); - const consoleErrorSpy = spyOn(console, 'error'); const alertSpy = spyOn(window, 'alert'); + spyOn(component, 'changePassword').and.callThrough(); + fixture.detectChanges(); - component.changePassword(); - - expect(consoleErrorSpy).toHaveBeenCalled(); + 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/video/video.component.ts b/src/app/pages/video/video.component.ts index 187e13e0..1815a6df 100644 --- a/src/app/pages/video/video.component.ts +++ b/src/app/pages/video/video.component.ts @@ -22,7 +22,7 @@ export class VideoComponent implements OnInit { findAll(): void { this.videoService.findAll().subscribe({ next: (data) => { - console.log(data); + // console.log(data); this.videosEduplay = data.body?.videoList || []; }, error: (error) => { diff --git a/src/app/services/user-token-interceptor.service.spec.ts b/src/app/services/user-token-interceptor.service.spec.ts index 49a7a87d..085d5581 100644 --- a/src/app/services/user-token-interceptor.service.spec.ts +++ b/src/app/services/user-token-interceptor.service.spec.ts @@ -29,7 +29,7 @@ describe('TokenInterceptorService', () => { }); it('should add header Authorization to request', () => { - localStorage.setItem('token', 'testtoken'); + localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJqb2FvMTV2aWN0b3IwOEBnbWFpbC5jb20iLCJleHAiOjE2OTkzMTI5MzV9.1B9qBJt8rErwBKyD5JCdsPozsw86oQ38tdfDuMM2HFI'); userService.getAllUsers().subscribe((res) => { expect(res).toBeTruthy(); }); From 29576e73e0ffe813a95467315c25f01fcb824467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 7 Nov 2023 16:07:20 -0300 Subject: [PATCH 15/16] Adiciona testes de login --- src/app/pages/login/login.component.spec.ts | 63 ++++++++++++++++--- src/app/pages/login/login.component.ts | 27 ++++---- .../reset-password.component.spec.ts | 5 +- .../reset-password.component.ts | 3 +- 4 files changed, 72 insertions(+), 26 deletions(-) diff --git a/src/app/pages/login/login.component.spec.ts b/src/app/pages/login/login.component.spec.ts index 381ddc62..ca4a87dd 100644 --- a/src/app/pages/login/login.component.spec.ts +++ b/src/app/pages/login/login.component.spec.ts @@ -3,21 +3,45 @@ 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 () => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, ReactiveFormsModule], - providers: [FormBuilder, AuthService], + 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', () => { @@ -25,14 +49,15 @@ describe('LoginComponent', () => { }); it('should have a valid form on initialization', () => { - expect(component.userForm.valid).toBeFalse(); + fixture.detectChanges(); + expect(component.userForm).toBeTruthy(); }); it('should call login method when the form is submitted', () => { - spyOn(component, 'login'); + fixture.detectChanges(); + spyOn(component, 'login').and.callThrough(); const form = component.userForm; form.setValue({ email: 'test@example.com', password: 'password' }); - fixture.detectChanges(); const submitButton = fixture.nativeElement.querySelector( 'button[type="submit"]' @@ -42,8 +67,21 @@ describe('LoginComponent', () => { 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'); + spyOn(component, 'navigator').and.callThrough(); const forgotPasswordLink = fixture.nativeElement.querySelector('.text-gray-400'); forgotPasswordLink.click(); @@ -53,11 +91,20 @@ describe('LoginComponent', () => { }); it('should call navigator method when "Cadastre-se" is clicked', () => { - spyOn(component, 'navigator'); + 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/reset-password/reset-password.component.spec.ts b/src/app/pages/reset-password/reset-password.component.spec.ts index 9eea9864..6563e9f0 100644 --- a/src/app/pages/reset-password/reset-password.component.spec.ts +++ b/src/app/pages/reset-password/reset-password.component.spec.ts @@ -51,6 +51,7 @@ describe('ResetPasswordComponent', () => { 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(); @@ -75,12 +76,12 @@ describe('ResetPasswordComponent', () => { expect(alertSpy).toHaveBeenCalledWith('Preencha todos os campos corretamente!'); }); - (it('should call changePassword and return an error', () => { + 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'); }, From 4b72eec957212ff079fead21a3a8ecc147f2eafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 7 Nov 2023 17:35:03 -0300 Subject: [PATCH 16/16] Adiciona testes --- .../active-account.component.spec.ts | 67 ++++++++++- .../check-code-rest-password.component.html | 37 +++++- ...check-code-rest-password.component.spec.ts | 108 +++++++++++++++++- .../edit-user/edit-user.component.spec.ts | 67 ++++++++++- .../pages/profile/profile.component.spec.ts | 58 +++++++++- src/app/pages/profile/profile.component.ts | 10 +- .../pages/register/register.component.spec.ts | 74 +++++++++++- src/app/pages/video/video.component.spec.ts | 4 +- 8 files changed, 395 insertions(+), 30 deletions(-) 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 2d16512e..2a087ab8 100644 --- a/src/app/pages/active-account/active-account.component.spec.ts +++ b/src/app/pages/active-account/active-account.component.spec.ts @@ -2,16 +2,39 @@ 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 { ReactiveFormsModule } from '@angular/forms'; +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({ - imports: [HttpClientTestingModule, ReactiveFormsModule], - providers: [AuthService], + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [ + { path: 'login', component: LoginComponent }, + ] + ), ReactiveFormsModule], + providers: [{ provide: AuthService, useValue: new AuthServiceMock() }, FormBuilder], declarations: [ActiveAccountComponent] }) .compileComponents(); @@ -19,9 +42,47 @@ describe('ActiveAccountComponent', () => { 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 86a55a1c..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 @@ -2,16 +2,39 @@ 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 { ReactiveFormsModule } from '@angular/forms'; +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({ - imports: [HttpClientTestingModule, ReactiveFormsModule], - providers: [AuthService], + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [ + { path: 'changePassword', component: ResetPasswordComponent }, + ] + ), ReactiveFormsModule], + providers: [{ provide: AuthService, useValue: new AuthServiceMock() }, FormBuilder], declarations: [CheckCodeRestPasswordComponent] }) .compileComponents(); @@ -19,9 +42,88 @@ describe('CheckCodeRestPasswordComponent', () => { 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 c1728898..41d290bb 100644 --- a/src/app/pages/edit-user/edit-user.component.spec.ts +++ b/src/app/pages/edit-user/edit-user.component.spec.ts @@ -2,25 +2,86 @@ 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 { ReactiveFormsModule } from '@angular/forms'; +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({ - imports: [HttpClientTestingModule, RouterTestingModule, ReactiveFormsModule], - declarations: [EditUserComponent] + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [ + { path: 'profile', component: ProfileComponent }, + ] + ), ReactiveFormsModule], + declarations: [EditUserComponent], + providers: [{ provide: UserService, useValue: new UserServiceMock() }, FormBuilder], }) .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/profile/profile.component.spec.ts b/src/app/pages/profile/profile.component.spec.ts index 18ff5af5..9059002a 100644 --- a/src/app/pages/profile/profile.component.spec.ts +++ b/src/app/pages/profile/profile.component.spec.ts @@ -1,24 +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({ - imports: [HttpClientTestingModule], - declarations: [ProfileComponent] + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( + [ + { path: 'editUser', component: EditUserComponent }, + ] + )], + declarations: [ProfileComponent], + providers: [{ provide: UserService, useValue: new UserServiceMock() }] }) .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 21c94378..af78b8bd 100644 --- a/src/app/pages/register/register.component.spec.ts +++ b/src/app/pages/register/register.component.spec.ts @@ -2,26 +2,94 @@ 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 { ReactiveFormsModule } from '@angular/forms'; +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({ - imports: [HttpClientTestingModule, ReactiveFormsModule], - providers: [AuthService], + imports: [HttpClientTestingModule, ReactiveFormsModule, + RouterTestingModule.withRoutes( + [ + { path: 'activateAccount', component: ActiveAccountComponent }, + ] + ) + ], + providers: [{ provide: AuthService, useValue: new AuthServiceMock() }, FormBuilder], declarations: [RegisterComponent] }) .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/video/video.component.spec.ts b/src/app/pages/video/video.component.spec.ts index e8d64e0b..a879e27c 100644 --- a/src/app/pages/video/video.component.spec.ts +++ b/src/app/pages/video/video.component.spec.ts @@ -269,9 +269,9 @@ describe('VideoComponent', () => { expect(mySpy).toHaveBeenCalled(); }); - (it('should call findAll and return an error', () => { + it('should call findAll and return an error', () => { const mySpy = spyOn(videoService, 'findAll').and.returnValue(throwError(() => new Error('Erro'))); component.findAll(); expect(mySpy).toHaveBeenCalled(); - })); + }); });