feat: Add newProjectConfigureHttpApiModule for API configuration and localization
feat: Implement SampleAppServiceTests for application service testing feat: Create newProjectConfigureApplicationTests project for application layer tests feat: Establish newProjectConfigureDomainTests project for domain layer tests feat: Introduce Entity Framework Core tests for the newProjectConfigure application feat: Develop console test application for HTTP API client demonstration feat: Implement FakeCurrentPrincipalAccessor for testing purposes chore: Set up test base classes and data seed contributors for consistent testing environment
This commit is contained in:
1 parent
7909179fca
commit
b6f2213ccf
279 files changed
+39466
No files matched your search
@@ -0,0 +1,281 @@
|
||||
<div class="row mb-3">
|
||||
<div class="col-xl-6 col-12 d-flex">
|
||||
<div class="card h-lg-100 w-100 overflow-hidden">
|
||||
<div class="card-body">
|
||||
<div class="starting-content">
|
||||
@if(!hasLoggedIn){
|
||||
<h3>Welcome</h3>
|
||||
<p class="lead text-muted mb-2">
|
||||
{{ '::LongWelcomeMessage' | abpLocalization }}
|
||||
</p>
|
||||
<a (click)="login()" class="px-4 btn btn-primary mb-4" role="button"><i class="fa fa-sign-in"></i> {{
|
||||
'AbpAccount::Login' | abpLocalization }}</a>
|
||||
}
|
||||
|
||||
<h3>Getting Started</h3>
|
||||
<ng-container *ngTemplateOutlet="
|
||||
startingContentTemplate;
|
||||
context: {
|
||||
$implicit: {
|
||||
description:
|
||||
'Learn how to create and run a new web application using the application startup template.',
|
||||
href: 'https://abp.io/docs/latest/get-started',
|
||||
label: 'Getting Started'
|
||||
}
|
||||
}
|
||||
"></ng-container>
|
||||
|
||||
<h4>Web Application Development Tutorial</h4>
|
||||
<ng-container *ngTemplateOutlet="
|
||||
startingContentTemplate;
|
||||
context: {
|
||||
$implicit: {
|
||||
description:
|
||||
'Learn how to build an ABP based web application named Acme.BookStore.',
|
||||
href: 'https://abp.io/docs/latest/tutorials/book-store/part-01?UI=NG&DB=EF',
|
||||
label: 'Explore Tutorial'
|
||||
}
|
||||
}
|
||||
"></ng-container>
|
||||
|
||||
<h4>Customize Lepton Theme</h4>
|
||||
<p class="text-muted mb-2">Learn how to customize LeptonX Theme as you wish.</p>
|
||||
<a href="https://abp.io/docs/latest/framework/ui/angular/theming" class="btn btn-primary soft mb-5 mb-xl-0"
|
||||
target="_blank">Customize Lepton</a>
|
||||
</div>
|
||||
|
||||
<img class="card-bg-image" src="assets/images/getting-started/bg-01.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 d-flex">
|
||||
<div class="row">
|
||||
<ng-container *ngTemplateOutlet="
|
||||
visitLinkTemplate;
|
||||
context: {
|
||||
$implicit: {
|
||||
label: 'Visit Support',
|
||||
description:
|
||||
'You can check for similar problems and solutions, or open a new topic to discuss your specific issue.',
|
||||
href: 'https://abp.io/support/questions',
|
||||
card: 'card overflow-hidden',
|
||||
imgClass: '',
|
||||
imgSrc: 'assets/images/getting-started/img-support.png',
|
||||
logo: 'abp-support abp-logo mb-2'
|
||||
}
|
||||
}
|
||||
"></ng-container>
|
||||
<ng-container *ngTemplateOutlet="
|
||||
visitLinkTemplate;
|
||||
context: {
|
||||
$implicit: {
|
||||
label: 'Visit Blog',
|
||||
description:
|
||||
'You can find content on .NET development, cross-platform, ASP.NET application templates, ABP-related news, and more.',
|
||||
href: 'https://abp.io/blog',
|
||||
card: 'card h-md-100 overflow-hidden',
|
||||
imgClass: 'margin-bottom: -24px',
|
||||
imgSrc: 'assets/images/getting-started/img-blog.png',
|
||||
logo: 'abp-blog abp-logo mb-2'
|
||||
}
|
||||
}
|
||||
"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 d-flex">
|
||||
<div class="card h-100 overflow-hidden">
|
||||
<div class="card-body">
|
||||
<div class="abp-community abp-logo mb-2"></div>
|
||||
<p class="lead text-muted">
|
||||
A unique community platform for
|
||||
<span class="fw-bold">ABP Lovers!</span>
|
||||
</p>
|
||||
<p class="text-muted mb-2">
|
||||
Explore all ABP users' experiences with the ABP Framework, discover articles and videos on
|
||||
how to use ABP, and join raffles for a chance to win surprise gifts!
|
||||
</p>
|
||||
<a class="btn btn-brand soft mb-3" href="https://abp.io/community/" target="_blank">Join ABP Community</a>
|
||||
</div>
|
||||
<img class="mt-3" src="assets/images/getting-started/img-community.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<ng-container *ngTemplateOutlet="
|
||||
tableTemplate;
|
||||
context: {
|
||||
$implicit: {
|
||||
title: 'More from ABP.IO',
|
||||
items: [
|
||||
{
|
||||
iconType: 'icon',
|
||||
style: true,
|
||||
icon: 'bi bi-check fs-4',
|
||||
iconClass: 'rounded-circle me-2 flex-center bg-brand soft',
|
||||
title: 'Latest Release Logs',
|
||||
link: 'https://github.com/abpframework/abp/releases',
|
||||
},
|
||||
{
|
||||
iconType: 'icon',
|
||||
style: true,
|
||||
icon: 'bi bi-check fs-4',
|
||||
iconClass: 'rounded-circle me-2 flex-center bg-brand soft',
|
||||
title: 'Video Courses',
|
||||
link: 'https://abp.io/video-courses/essentials',
|
||||
},
|
||||
{
|
||||
iconType: 'icon',
|
||||
style: true,
|
||||
icon: 'bi bi-check fs-4',
|
||||
iconClass: 'rounded-circle me-2 flex-center bg-brand soft',
|
||||
title: 'Samples',
|
||||
link: 'https://abp.io/docs/latest/Samples/Index',
|
||||
},
|
||||
{
|
||||
iconType: 'icon',
|
||||
style: true,
|
||||
icon: 'bi bi-check fs-4',
|
||||
iconClass: 'rounded-circle me-2 flex-center bg-brand soft',
|
||||
title: 'Books',
|
||||
link: 'https://abp.io/books',
|
||||
},
|
||||
{
|
||||
iconType: 'icon',
|
||||
style: true,
|
||||
icon: 'bi bi-check fs-4',
|
||||
iconClass: 'rounded-circle me-2 flex-center bg-brand soft',
|
||||
title: 'FAQ',
|
||||
link: 'https://abp.io/faq',
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
"></ng-container>
|
||||
<div class="col-xl-6 col-lg-8 d-flex">
|
||||
<div class="card">
|
||||
<div class="card-body d-flex">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-8">
|
||||
<p class="text-muted mb-0">THE OFFICIAL GUIDE</p>
|
||||
<h3>Mastering ABP Framework</h3>
|
||||
<p class="text-muted">
|
||||
Written by the creator of the ABP Framework, this book will help you gain a complete
|
||||
understanding of the framework and modern web application development techniques.
|
||||
</p>
|
||||
<div class="d-md-flex mb-2 mb-md-0">
|
||||
<a href="https://www.amazon.com/gp/product/B097Z2DM8Q" target="_blank"
|
||||
class="btn btn-primary soft me-md-2 mb-2 mb-md-0">
|
||||
Buy on Amazon US
|
||||
</a>
|
||||
<a href="https://www.packtpub.com/product/mastering-abp-framework/9781801079242" target="_blank"
|
||||
class="btn btn-primary soft mb-2 mb-md-0">
|
||||
Buy on PACKT
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<img src="assets/images/getting-started/book.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngTemplateOutlet="
|
||||
tableTemplate;
|
||||
context: {
|
||||
$implicit: {
|
||||
title: 'Follow us on Social Media',
|
||||
items: [
|
||||
{
|
||||
title: 'X.com',
|
||||
icon: 'logo-x me-2',
|
||||
link: 'https://twitter.com/abpframework'
|
||||
},
|
||||
{
|
||||
iconType: 'image',
|
||||
icon: 'assets/images/getting-started/discord.svg',
|
||||
title: 'Discord',
|
||||
link: 'https://abp.io/community/discord'
|
||||
},
|
||||
{
|
||||
iconType: 'image',
|
||||
icon: 'assets/images/getting-started/stack-overflow.svg',
|
||||
title: 'Stack Overflow',
|
||||
link: 'https://stackoverflow.com/questions/tagged/abp'
|
||||
},
|
||||
{
|
||||
iconType: 'image',
|
||||
icon: 'assets/images/getting-started/youtube.svg',
|
||||
title: 'YouTube',
|
||||
link: 'https://www.youtube.com/@Volosoft'
|
||||
},
|
||||
{
|
||||
iconType: 'image',
|
||||
icon: 'assets/images/getting-started/instagram.svg',
|
||||
title: 'Instagram',
|
||||
link: 'https://www.instagram.com/abpframework/'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"></ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template #startingContentTemplate let-context>
|
||||
<p class="lead text-muted mb-2" [innerHTML]="context.description"></p>
|
||||
<a [href]="context.href" class="btn btn-brand mb-4" target="_blank">{{ context.label }}</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #visitLinkTemplate let-context>
|
||||
<div class="col-12 d-flex">
|
||||
<div [class]="context.card">
|
||||
<div class="card-body pb-0">
|
||||
<div [class]="context.logo"></div>
|
||||
<p class="text-muted mb-2" [innerHTML]="context.description"></p>
|
||||
<a [href]="context.href" class="btn btn-brand soft" target="_blank">{{ context.label }}</a>
|
||||
<img [class]="context.imgClass" [src]="context.imgSrc" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #tableTemplate let-context>
|
||||
<div class="col-xl-3 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title text-body">{{ context.title }}</div>
|
||||
</div>
|
||||
<div class="card-body pt-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table mb-0">
|
||||
<tbody>
|
||||
@for(item of context.items; track $index; let last = $last){
|
||||
<tr>
|
||||
<td class="ps-0 d-flex" [class.border-bottom-0]="last">
|
||||
@if(item.icon){
|
||||
<div [class]="item.iconClass" [style]="item.style ? 'width: 20px; height: 20px' : ''">
|
||||
@if(item.iconType === 'image'){
|
||||
<img [src]="item.icon" class="me-2" />
|
||||
}@else if(item.iconType === 'icon'){
|
||||
<i [class]="item.icon"></i>
|
||||
}@else{
|
||||
<div [class]="item.icon"></div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div>{{ item.title }}</div>
|
||||
</td>
|
||||
<td class="text-end pe-0 opacity-25" [class.border-bottom-0]="last">
|
||||
<a [href]="item.link" target="_blank">
|
||||
<i class="bi bi-box-arrow-up-right text-dark"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Styles for the home component */
|
||||
.card-bg-image {
|
||||
position: absolute;
|
||||
width: 630px;
|
||||
height: auto;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.starting-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@media (min-width: 1199px) {
|
||||
.h-lg-100 {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.h-md-100 {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { CoreTestingModule } from "@abp/ng.core/testing";
|
||||
import { ThemeSharedTestingModule } from "@abp/ng.theme.shared/testing";
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { NgxValidateCoreModule } from "@ngx-validate/core";
|
||||
import { HomeComponent } from "./home.component";
|
||||
import { AuthService } from '@abp/ng.core';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
describe("HomeComponent", () => {
|
||||
let fixture: ComponentFixture<HomeComponent>;
|
||||
let mockAuthService: { isAuthenticated: boolean; navigateToLogin: ReturnType<typeof vi.fn> };
|
||||
|
||||
beforeEach(async () => {
|
||||
mockAuthService = {
|
||||
isAuthenticated: false,
|
||||
navigateToLogin: vi.fn()
|
||||
};
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CoreTestingModule.withConfig(),
|
||||
ThemeSharedTestingModule.withConfig(),
|
||||
NgxValidateCoreModule,
|
||||
HomeComponent
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: AuthService,
|
||||
useValue: mockAuthService
|
||||
}
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it("should be initiated", () => {
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when login state is true', () => {
|
||||
beforeEach(() => {
|
||||
mockAuthService.isAuthenticated = true;
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("hasLoggedIn should be true", () => {
|
||||
expect(fixture.componentInstance.hasLoggedIn).toBe(true);
|
||||
});
|
||||
|
||||
it("button should not be exists", () => {
|
||||
const element = fixture.nativeElement;
|
||||
const button = element.querySelector('[role="button"]');
|
||||
expect(button).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when login state is false', () => {
|
||||
beforeEach(() => {
|
||||
mockAuthService.isAuthenticated = false;
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("hasLoggedIn should be false", () => {
|
||||
expect(fixture.componentInstance.hasLoggedIn).toBe(false);
|
||||
});
|
||||
|
||||
it("button should be exists", () => {
|
||||
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();
|
||||
});
|
||||
|
||||
it("navigateToLogin have been called", () => {
|
||||
expect(mockAuthService.navigateToLogin).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
import { AuthService, LocalizationPipe } from '@abp/ng.core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { NgTemplateOutlet } from '@angular/common';
|
||||
import { FormFieldConfig } from '@abp/ng.components/dynamic-form';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss'],
|
||||
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: {
|
||||
defaultValues: [
|
||||
{ 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;
|
||||
}
|
||||
|
||||
login() {
|
||||
this.authService.navigateToLogin();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const homeRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
loadComponent: () => import('./home.component').then(m => m.HomeComponent),
|
||||
},
|
||||
];
|
||||
Reference in new issue
Block a user