configuration pocketbase terminé (#5)

# Conflicts:
#	.gitignore
This commit is contained in:
Styve Lioumba
2025-08-21 18:41:52 +02:00
committed by styve Lioumba
parent 1dc1109482
commit 4fb600b0cb
179 changed files with 23970 additions and 15135 deletions

View File

@@ -0,0 +1,58 @@
import {TestBed} from '@angular/core/testing';
import {authGuard} from './auth.guard';
import {AuthService} from "@app/core/services/authentication/auth.service";
import {signal} from "@angular/core";
import {Auth} from "@app/shared/models/auth";
import {CanActivateFn, Router} from '@angular/router';
describe('authGuard', () => {
let mockAuthService: Partial<AuthService>;
let mockRouter: Partial<Router>;
const executeGuard: CanActivateFn = (...guardParameters) =>
TestBed.runInInjectionContext(() => authGuard(...guardParameters));
beforeEach(() => {
mockAuthService= {
user: signal<Auth | undefined>({ isValid: true, token: 'mockToken', record: null })
}
mockRouter ={
parseUrl: jest.fn()
}
TestBed.configureTestingModule({
providers: [
{ provide: AuthService, useValue: mockAuthService },
{ provide: Router, useValue: mockRouter }
]
});
});
it('should be created', () => {
expect(executeGuard).toBeTruthy();
});
it('should allow access if user is valid', () => {
const mockRoute = {} as any;
const mockState = {} as any;
const result =TestBed.runInInjectionContext(() => executeGuard(mockRoute, mockState));
expect(result).toEqual(true);
});
it('should redirect to /auth if user is not valid', () => {
mockAuthService.user!.set({ isValid: false, token: '', record: null });
const mockRoute = {} as any;
const mockState = {} as any;
(mockRouter.parseUrl as jest.Mock).mockReturnValue('/auth');
const result = TestBed.runInInjectionContext(() => executeGuard(mockRoute, mockState));
expect(result).toEqual("/auth" as any);
expect(mockRouter.parseUrl).toHaveBeenCalledWith("/auth");
});
});

View File

@@ -0,0 +1,13 @@
import {CanActivateFn, Router} from '@angular/router';
import {inject} from "@angular/core";
import {AuthService} from "@app/core/services/authentication/auth.service";
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
if (!authService.user()!.isValid) {
return router.parseUrl("/auth")
}
return true;
};

View File

@@ -0,0 +1,52 @@
import {TestBed} from '@angular/core/testing';
import {Router} from '@angular/router';
import {myProfileResolver} from './my-profile.resolver';
import {User} from "@app/shared/models/user";
describe('myProfileResolver', () => {
let mockRouter: Partial<Router>;
beforeEach(() => {
mockRouter = {
getCurrentNavigation: jest.fn()
};
TestBed.configureTestingModule({
providers: [
{ provide: Router, useValue: mockRouter }
]
});
});
it('should return the user from navigation extras state', () => {
const user: User = {
id: 'adbc123',
username: "john_doe",
verified: true,
emailVisibility: false,
email: "jd@example.com",
created: new Date().toString(),
updated: new Date().toString(),
name: "john doe",
avatar: ""
};
// Mocke la méthode getCurrentNavigation pour retourner un objet attendu
(mockRouter.getCurrentNavigation as jest.Mock).mockReturnValue({
extras: {
state: {
user
}
}
});
const result = TestBed.runInInjectionContext(() => myProfileResolver(null as any, null as any));
expect(result).toEqual({ user });
expect(mockRouter.getCurrentNavigation).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,9 @@
import {ResolveFn, Router} from '@angular/router';
import {User} from "@app/shared/models/user";
import {inject} from "@angular/core";
export const myProfileResolver: ResolveFn<{ user: User }> = (route, state) => {
const router = inject(Router);
const user: User = router.getCurrentNavigation()!.extras.state!['user'] as User;
return {user}
};

View File

@@ -1,17 +1,67 @@
import { TestBed } from '@angular/core/testing';
import { ResolveFn } from '@angular/router';
import { detailResolver } from './detail.resolver';
describe('detailResolver', () => {
const executeResolver: ResolveFn<boolean> = (...resolverParameters) =>
TestBed.runInInjectionContext(() => detailResolver(...resolverParameters));
beforeEach(() => {
TestBed.configureTestingModule({});
});
it('should be created', () => {
expect(executeResolver).toBeTruthy();
});
});
import {TestBed} from '@angular/core/testing';
import {Router} from '@angular/router';
import {detailResolver} from './detail.resolver';
import {User} from "@app/shared/models/user";
import {Profile} from "@app/shared/models/profile";
describe('detailResolver', () => {
let mockRoute: Partial<Router>;
beforeEach(() => {
mockRoute = {
getCurrentNavigation: jest.fn()
};
TestBed.configureTestingModule({
providers: [
{ provide: Router, useValue: mockRoute }
]
});
});
it('should return user and profile', () => {
const mockUser : User = {
id: 'adbc123',
username: "john_doe",
verified: true,
emailVisibility: false,
email: "jd@example.com",
created: new Date().toString(),
updated: new Date().toString(),
name: "john doe",
avatar: ""
};
const mockProfile : Profile = {
id: "string",
created: "string",
updated: "string",
profession: "string",
utilisateur: "string",
estVerifier: false,
secteur: "string",
reseaux: JSON.parse("{}"),
bio: "string",
cv: "string",
projets: [],
apropos: "string"
};
const fakeState = {} as any;
const fakeParams = { params: { name: 'john_doe'} } as any;
(mockRoute.getCurrentNavigation as jest.Mock).mockReturnValue({
extras: {
state: { user: mockUser, profile: mockProfile }
}
});
const result = TestBed.runInInjectionContext(() => detailResolver( fakeParams, fakeState));
expect(result).toEqual({ user: mockUser, profile: mockProfile });
expect(mockRoute.getCurrentNavigation).toHaveBeenCalled();
});
});

View File

@@ -1,6 +1,10 @@
import { ResolveFn } from '@angular/router';
export const detailResolver: ResolveFn<string> = (route, state) => {
const paramValue = route.params['name'];
return "profile-list-resolver works!, paramValue: " + paramValue || "no query value found!";
};
import {ResolveFn, Router} from '@angular/router';
import {inject} from "@angular/core";
import {User} from "@app/shared/models/user";
import {Profile} from "@app/shared/models/profile";
export const detailResolver: ResolveFn<{ user:User,profile:Profile }> = (route, state) => {
const paramValue = route.params['name'];
const router = inject(Router);
return router.getCurrentNavigation()?.extras.state as { user:User,profile:Profile }
};

View File

@@ -1,17 +1,41 @@
import { TestBed } from '@angular/core/testing';
import { ResolveFn } from '@angular/router';
import { listResolver } from './list.resolver';
describe('listResolver', () => {
const executeResolver: ResolveFn<boolean> = (...resolverParameters) =>
TestBed.runInInjectionContext(() => listResolver(...resolverParameters));
beforeEach(() => {
TestBed.configureTestingModule({});
});
it('should be created', () => {
expect(executeResolver).toBeTruthy();
});
});
import { TestBed } from '@angular/core/testing';
import {ResolveFn, Router} from '@angular/router';
import { listResolver } from './list.resolver';
import {Profile} from "@app/shared/models/profile";
import {myProfileResolver} from "@app/core/resolvers/my-profile/my-profile.resolver";
import {ProfileService} from "@app/core/services/profile/profile.service";
import {Observable, of} from "rxjs";
describe('listResolver', () => {
let mockProfileService: Partial<ProfileService>;
beforeEach(() => {
mockProfileService = {
profiles: of([] as Profile[])
};
TestBed.configureTestingModule({
providers: [
{ provide: ProfileService, useValue: mockProfileService }
]
});
});
it('should return profiles observable from ProfileService', () => {
const fakeRoute ={ queryParams: { search: '' } } as any;
const fakeState = {} as any;
const expectedProfiles = [] as Profile[];
const result$ : Observable<Profile[]> = TestBed.runInInjectionContext(() => listResolver(fakeRoute, fakeState) as Observable<Profile[]>);
result$.subscribe((result:any) => {
expect(result).toEqual(expectedProfiles);
expect(mockProfileService.profiles).toBeDefined();
});
});
});

View File

@@ -1,6 +1,12 @@
import { ResolveFn } from '@angular/router';
export const listResolver: ResolveFn<string> = (route, state) => {
const queryValue = route.queryParams['search'];
return "profile-list-resolver works!, queryValue: " + queryValue || "no query value found!";
};
import { ResolveFn } from '@angular/router';
import {inject} from "@angular/core";
import {ProfileService} from "@app/core/services/profile/profile.service";
import {Observable} from "rxjs";
import {Profile} from "@app/shared/models/profile";
export const listResolver: ResolveFn<Observable<Profile[]>> = (route, state) => {
const queryValue = route.queryParams['search'];
const profileService = inject(ProfileService);
return profileService.profiles;
};

View File

@@ -0,0 +1,76 @@
import {TestBed} from '@angular/core/testing';
import {AuthService} from './auth.service';
import {LoginDto} from "@app/shared/models/login-dto";
import {RegisterDto} from "@app/shared/models/register-dto";
import {User} from "@app/shared/models/user";
describe('AuthService', () => {
let authService: AuthService;
let mockLoginUser : LoginDto = {email: 'john_doe@example.com', password: 'mysecretpassword'};
let mockRegisterUser: RegisterDto = {email:'john_doe@example.com', password: 'mysecretpassword', passwordConfirm: 'mysecretpassword', emailVisibility: false};
const mockAuth = {
isValid: false,
record: {email: mockLoginUser.email, id: '12345', verified: false} as User,
token: 'mockToken12345'
}
const mockAuthStore = {
model: {email: mockLoginUser.email, id: '12345', verified: false} as User,
token: 'abc123',
isValid: true,
clear: jest.fn(),
};
const mockCollection = {
authWithPassword: jest.fn().mockResolvedValue({
record: { verified: true }
}),
create: jest.fn(),
requestPasswordReset: jest.fn(),
requestVerification: jest.fn().mockResolvedValue(true),
};
const mockPocketBase = jest.fn(() => ({
collection: jest.fn(() => mockCollection),
authStore: mockAuthStore,
}));
beforeEach(() => {
TestBed.configureTestingModule({
providers: [],
imports: []
});
authService = TestBed.inject(AuthService);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be created', () => {
expect(authService).toBeTruthy();
});
it('should have user signal initialized to undefined', () => {
expect(authService.user()).toBeUndefined();
});
it('should login correctly and set signal', async () => {
authService.login(mockLoginUser)
.then((response) => {
expect(response).toBeDefined();
}).catch((error) => jest.fn());
});
it('should make success register', () => {
authService.register(mockRegisterUser)
.then((response) => {
expect(response).toBeDefined();
expect(response.email).toEqual(mockRegisterUser.email);
}).catch((error) => jest.fn());
});
});

View File

@@ -0,0 +1,71 @@
import {Injectable, signal} from '@angular/core';
import {LoginDto} from "@app/shared/models/login-dto";
import PocketBase from 'pocketbase';
import {environment} from "@env/environment";
import {Auth} from "@app/shared/models/auth";
import {RegisterDto} from "@app/shared/models/register-dto";
import {User} from "@app/shared/models/user";
@Injectable({
providedIn: 'root'
})
export class AuthService {
user = signal<Auth | undefined>(undefined)
async login(loginDto: LoginDto) {
const {email, password} = loginDto
const pb = new PocketBase(environment.baseUrl);
const response = await pb.collection('users').authWithPassword(email, password);
const isValid = response.record['verified'];
const res = {isValid, record: pb.authStore.model as User, token: pb.authStore.token};
if (isValid){
this.user.set(res);
}
return res;
}
async register(registerDto: RegisterDto) {
const pb = new PocketBase(environment.baseUrl);
return await pb.collection('users').create<User>(registerDto)
}
async logout() {
const pb = new PocketBase(environment.baseUrl);
return pb.authStore.clear();
}
updateUser() {
const pb = new PocketBase(environment.baseUrl);
this.user.set({isValid: pb.authStore.isValid, record: pb.authStore.model as User, token: pb.authStore.token});
}
sendPasswordReset(email: string) {
const pb = new PocketBase(environment.baseUrl);
return pb.collection('users').requestPasswordReset(email);
}
verifyEmail(email: string) {
const pb = new PocketBase(environment.baseUrl);
return pb.collection('users').requestVerification(email)
.then(() => {
return true;
})
.catch((error) => {
console.error('Error sending verification email:', error);
return false;
});
}
isAuthenticated(): boolean {
const pb = new PocketBase(environment.baseUrl);
return pb.authStore.isValid;
}
isEmailVerified(): boolean {
const pb = new PocketBase(environment.baseUrl);
return pb.authStore.model? pb.authStore.model['verified'] : false;
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ProfileService } from './profile.service';
describe('ProfileService', () => {
let service: ProfileService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ProfileService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,40 @@
import {Injectable} from '@angular/core';
import PocketBase from "pocketbase";
import {environment} from "@env/environment";
import {Profile} from "@app/shared/models/profile";
import {from} from "rxjs";
import {ProfileDto} from "@app/shared/models/profile-dto";
@Injectable({
providedIn: 'root'
})
export class ProfileService {
createProfile(profileDto: ProfileDto) {
const pb = new PocketBase(environment.baseUrl);
return from(
pb.collection('profiles').create(profileDto)
);
}
get profiles() {
const pb = new PocketBase(environment.baseUrl);
return from(
pb.collection('profiles').getFullList<Profile>({
sort: 'profession'
}))
}
getProfileByUserId(userId: string) {
const pb = new PocketBase(environment.baseUrl);
return from(
pb.collection<Profile>('profiles').getFirstListItem(`utilisateur="${userId}"`)
)
}
updateProfile(id: string, data: Profile | any) {
const pb = new PocketBase(environment.baseUrl);
return from(pb.collection('profiles').update<Profile>(id, data));
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ProjectService } from './project.service';
describe('ProjectService', () => {
let service: ProjectService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ProjectService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,34 @@
import {Injectable} from '@angular/core';
import PocketBase from "pocketbase";
import {environment} from "@env/environment.development";
import {from} from "rxjs";
import {Project} from "@app/shared/models/project";
import {ProjectDto} from "@app/shared/models/project-dto";
@Injectable({
providedIn: 'root'
})
export class ProjectService {
createProject(projectDto: ProjectDto) {
const pb = new PocketBase(environment.baseUrl);
return from(
pb.collection('projets').create<Project>(projectDto)
);
}
getProjectByUserId(userId: string) {
const pb = new PocketBase(environment.baseUrl);
return from(pb.collection<Project>('projets').getFullList({filter: `utilisateur='${userId}'`}))
}
getProjectById(id: string) {
const pb = new PocketBase(environment.baseUrl);
return from(pb.collection<Project>('projets').getOne<Project>(id))
}
updateProject(id: string, data: Project | any) {
const pb = new PocketBase(environment.baseUrl);
return from(pb.collection('projets').update<Project>(id, data));
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { SectorService } from './sector.service';
describe('SectorService', () => {
let service: SectorService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SectorService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,26 @@
import {Injectable} from '@angular/core';
import PocketBase from "pocketbase";
import {environment} from "@env/environment.development";
import {from} from "rxjs";
import {Sector} from "@app/shared/models/sector";
@Injectable({
providedIn: 'root'
})
export class SectorService {
get sectors() {
const pb = new PocketBase(environment.baseUrl);
return from(
pb.collection<Sector>('secteur').getFullList({
sort: 'nom'
}))
}
getSectorById(id: string) {
const pb = new PocketBase(environment.baseUrl);
return from(pb.collection<Sector>('secteur').getOne<Sector>(id))
}
}

View File

@@ -1,16 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ThemeService } from './theme.service';
describe('ThemeService', () => {
let service: ThemeService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ThemeService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { TestBed } from '@angular/core/testing';
import { ThemeService } from './theme.service';
describe('ThemeService', () => {
let service: ThemeService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ThemeService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -1,14 +1,14 @@
import {Injectable, signal} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ThemeService {
darkModeSignal = signal<string>('null');
updateDarkMode() {
this.darkModeSignal.update((value) => (value === 'dark' ? 'null' : 'dark'));
}
}
import {Injectable, signal} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ThemeService {
darkModeSignal = signal<string>('null');
updateDarkMode() {
this.darkModeSignal.update((value) => (value === 'dark' ? 'null' : 'dark'));
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(UserService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,21 @@
import {Injectable} from '@angular/core';
import PocketBase from "pocketbase";
import {environment} from "@env/environment";
import {from} from "rxjs";
import {User} from "@app/shared/models/user";
@Injectable({
providedIn: 'root'
})
export class UserService {
getUserById(id: string) {
const pb = new PocketBase(environment.baseUrl);
return from(pb.collection('users').getOne<User>(id));
}
updateUser(id: string, data: User|any) {
const pb = new PocketBase(environment.baseUrl);
return from(pb.collection('users').update<User>(id, data));
}
}