Browse Source

Migrate Angular tests from Karma/Jasmine to Vitest

Replaces Karma and Jasmine with Vitest for unit testing in the Angular template.
pull/24725/head
Fahri Gedik 2 months ago
parent
commit
1a2dee7129
  1. 12
      templates/app/angular/angular.json
  2. 44
      templates/app/angular/karma.conf.js
  3. 11
      templates/app/angular/package.json
  4. 1
      templates/app/angular/src/app/home/home.component.html
  5. 114
      templates/app/angular/src/app/home/home.component.spec.ts
  6. 79
      templates/app/angular/src/app/home/home.component.ts
  7. 13
      templates/app/angular/src/test.ts
  8. 13
      templates/app/angular/tsconfig.spec.json

12
templates/app/angular/angular.json

@ -163,17 +163,7 @@
}
},
"test": {
"builder": "@angular/build:karma",
"options": {
"browser": "src/test.ts",
"polyfills": ["src/polyfills.ts"],
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": []
}
"builder": "@angular/build:unit-test"
},
"lint": {
"builder": "@angular-eslint/builder:lint",

44
templates/app/angular/karma.conf.js

@ -1,44 +0,0 @@
// 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({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
],
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/MyProjectName'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

11
templates/app/angular/package.json

@ -47,17 +47,12 @@
"@angular/cli": "~21.0.0",
"@angular/compiler-cli": "~21.0.0",
"@angular/language-service": "~21.0.0",
"@types/jasmine": "~3.6.0",
"@types/node": "~20.11.0",
"@typescript-eslint/eslint-plugin": "7.16.0",
"@typescript-eslint/parser": "7.16.0",
"eslint": "^8.0.0",
"jasmine-core": "~4.0.0",
"karma": "~6.4.4",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.1.0",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.7.0",
"typescript": "~5.9.3"
"jsdom": "^27.1.0",
"typescript": "~5.9.3",
"vitest": "^4.0.8"
}
}

1
templates/app/angular/src/app/home/home.component.html

@ -1,5 +1,4 @@
<div class="row mb-3">
<abp-dynamic-form [fields]="formFields" />
<div class="col-xl-6 col-12 d-flex">
<div class="card h-lg-100 w-100 overflow-hidden">
<div class="card-body">

114
templates/app/angular/src/app/home/home.component.spec.ts

@ -1,39 +1,43 @@
import { CoreTestingModule } from "@abp/ng.core/testing";
import { ThemeSharedTestingModule } from "@abp/ng.theme.shared/testing";
import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { NgxValidateCoreModule } from "@ngx-validate/core";
import { HomeComponent } from "./home.component";
import { OAuthService } from 'angular-oauth2-oidc';
import { AuthService } from '@abp/ng.core';
import { vi } from 'vitest';
describe("HomeComponent", () => {
let fixture: ComponentFixture<HomeComponent>;
const mockOAuthService = jasmine.createSpyObj('OAuthService', ['hasValidAccessToken'])
const mockAuthService = jasmine.createSpyObj('AuthService', ['navigateToLogin'])
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [],
imports: [
CoreTestingModule.withConfig(),
ThemeSharedTestingModule.withConfig(),
NgxValidateCoreModule,
HomeComponent
],
providers: [
/* mock providers here */
{
provide: OAuthService,
useValue: mockOAuthService
},
{
provide: AuthService,
useValue: mockAuthService
}
],
}).compileComponents();
})
);
const mockOAuthService = {
hasValidAccessToken: vi.fn()
};
const mockAuthService = {
navigateToLogin: vi.fn()
};
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [],
imports: [
CoreTestingModule.withConfig(),
ThemeSharedTestingModule.withConfig(),
NgxValidateCoreModule,
HomeComponent
],
providers: [
/* mock providers here */
{
provide: OAuthService,
useValue: mockOAuthService
},
{
provide: AuthService,
useValue: mockAuthService
}
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
@ -44,55 +48,49 @@ describe("HomeComponent", () => {
expect(fixture.componentInstance).toBeTruthy();
});
describe('when login state is true', () => {
beforeAll(() => {
mockOAuthService.hasValidAccessToken.and.returnValue(true)
mockOAuthService.hasValidAccessToken.mockReturnValue(true);
});
it("hasLoggedIn should be true", () => {
expect(fixture.componentInstance.hasLoggedIn).toBeTrue();
expect(mockOAuthService.hasValidAccessToken).toHaveBeenCalled()
})
expect(fixture.componentInstance.hasLoggedIn).toBe(true);
expect(mockOAuthService.hasValidAccessToken).toHaveBeenCalled();
});
it("button should not be exists", () => {
const element = fixture.nativeElement
const button = element.querySelector('[role="button"]')
expect(button).toBeNull()
})
})
const element = fixture.nativeElement;
const button = element.querySelector('[role="button"]');
expect(button).toBeNull();
});
});
describe('when login state is false', () => {
beforeAll(() => {
mockOAuthService.hasValidAccessToken.and.returnValue(false)
mockOAuthService.hasValidAccessToken.mockReturnValue(false);
});
it("hasLoggedIn should be false", () => {
expect(fixture.componentInstance.hasLoggedIn).toBeFalse();
expect(mockOAuthService.hasValidAccessToken).toHaveBeenCalled()
})
expect(fixture.componentInstance.hasLoggedIn).toBe(false);
expect(mockOAuthService.hasValidAccessToken).toHaveBeenCalled();
});
it("button should be exists", () => {
const element = fixture.nativeElement
const button = element.querySelector('[role="button"]')
expect(button).toBeDefined()
})
describe('when button clicked', () => {
const element = fixture.nativeElement;
const button = element.querySelector('[role="button"]');
expect(button).toBeDefined();
});
describe('when button clicked', () => {
beforeEach(() => {
const element = fixture.nativeElement
const button = element.querySelector('[role="button"]')
button.click()
const element = fixture.nativeElement;
const button = element.querySelector('[role="button"]');
button.click();
});
it("navigateToLogin have been called", () => {
expect(mockAuthService.navigateToLogin).toHaveBeenCalled()
})
})
})
expect(mockAuthService.navigateToLogin).toHaveBeenCalled();
});
});
});
});

79
templates/app/angular/src/app/home/home.component.ts

@ -1,89 +1,16 @@
import {AuthService, LocalizationPipe} from '@abp/ng.core';
import { AuthService, LocalizationPipe } from '@abp/ng.core';
import { Component, inject } from '@angular/core';
import {NgTemplateOutlet} from "@angular/common";
import {DynamicFormComponent, FormFieldConfig} from "@abp/ng.components/dynamic-form";
import { NgTemplateOutlet } from "@angular/common";
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
imports: [NgTemplateOutlet, LocalizationPipe, DynamicFormComponent]
imports: [NgTemplateOutlet, LocalizationPipe]
})
export class HomeComponent {
private authService = inject(AuthService);
formFields: FormFieldConfig[] = [
{
key: 'firstName',
type: 'text',
label: 'First Name',
placeholder: 'Enter first name',
value: 'erdemc',
required: true,
validators: [
{ type: 'required', message: 'First name is required' },
{ type: 'minLength', value: 2, message: 'Minimum 2 characters required' }
],
gridSize: 6,
order: 1
},
{
key: 'lastName',
type: 'text',
label: 'Last Name',
placeholder: 'Enter last name',
required: true,
validators: [
{ type: 'required', message: 'Last name is required' }
],
gridSize: 12,
order: 3
},
{
key: 'email',
type: 'email',
label: 'Email Address',
placeholder: 'Enter email',
required: true,
validators: [
{ type: 'required', message: 'Email is required' },
{ type: 'email', message: 'Please enter a valid email' }
],
gridSize: 6,
order: 2
},
{
key: 'userType',
type: 'select',
label: 'User Type',
required: true,
options: [
{ key: 'admin', value: 'Administrator' },
{ key: 'user', value: 'Regular User' },
{ key: 'guest', value: 'Guest User' }
],
validators: [
{ type: 'required', message: 'Please select user type' }
],
order: 4
},
{
key: 'adminNotes',
type: 'textarea',
label: 'Admin Notes',
placeholder: 'Enter admin-specific notes',
conditionalLogic: [
{
dependsOn: 'userType',
condition: 'equals',
value: 'admin',
action: 'show'
}
],
order: 5
}
];
get hasLoggedIn(): boolean {
return this.authService.isAuthenticated;
}

13
templates/app/angular/src/test.ts

@ -1,13 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);

13
templates/app/angular/tsconfig.spec.json

@ -1,18 +1,15 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
"vitest/globals"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
"src/**/*.d.ts",
"src/**/*.spec.ts"
]
}

Loading…
Cancel
Save