mirror of
https://github.com/block-core/angor-hub-old.git
synced 2025-12-17 01:44:19 +01:00
Replace custom Webpack builder with Angular DevKit builder, remove webpack.config.js, and add LoginComponent unit tests
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
"prefix": "app",
|
"prefix": "app",
|
||||||
"architect": {
|
"architect": {
|
||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular-builders/custom-webpack:browser",
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
"options": {
|
"options": {
|
||||||
"outputPath": "dist",
|
"outputPath": "dist",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
"defaultConfiguration": "production"
|
"defaultConfiguration": "production"
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-builders/custom-webpack:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
"buildTarget": "angor:build:production"
|
"buildTarget": "angor:build:production"
|
||||||
@@ -101,10 +101,10 @@
|
|||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular-builders/custom-webpack:extract-i18n"
|
"builder": "@angular-devkit/build-angular:extract-i18n"
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"builder": "@angular-builders/custom-webpack:karma",
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
"options": {
|
"options": {
|
||||||
"polyfills": ["zone.js", "zone.js/testing"],
|
"polyfills": ["zone.js", "zone.js/testing"],
|
||||||
"tsConfig": "tsconfig.spec.json",
|
"tsConfig": "tsconfig.spec.json",
|
||||||
|
|||||||
2926
package-lock.json
generated
2926
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -26,10 +26,10 @@
|
|||||||
class="mt-8"
|
class="mt-8"
|
||||||
[appearance]="'outline'"
|
[appearance]="'outline'"
|
||||||
[showIcon]="false"
|
[showIcon]="false"
|
||||||
[type]="secAlert.type"
|
[type]="secAlert().type"
|
||||||
[@shake]="secAlert.type === 'error'"
|
[@shake]="secAlert().type === 'error'"
|
||||||
>
|
>
|
||||||
{{ secAlert.message }}
|
{{ secAlert().message }}
|
||||||
</angor-alert>
|
</angor-alert>
|
||||||
|
|
||||||
|
|
||||||
@@ -154,10 +154,10 @@
|
|||||||
class="mt-8"
|
class="mt-8"
|
||||||
[appearance]="'outline'"
|
[appearance]="'outline'"
|
||||||
[showIcon]="false"
|
[showIcon]="false"
|
||||||
[type]="menemonicAlert.type"
|
[type]="menemonicAlert().type"
|
||||||
[@shake]="menemonicAlert.type === 'error'"
|
[@shake]="menemonicAlert().type === 'error'"
|
||||||
>
|
>
|
||||||
{{ menemonicAlert.message }}
|
{{ menemonicAlert().message }}
|
||||||
</angor-alert>
|
</angor-alert>
|
||||||
<!-- Login form with Menemonic -->
|
<!-- Login form with Menemonic -->
|
||||||
<form
|
<form
|
||||||
|
|||||||
109
src/app/components/auth/login/login.component.spec.ts
Normal file
109
src/app/components/auth/login/login.component.spec.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { SignerService } from 'app/services/signer.service';
|
||||||
|
import { StateService } from 'app/services/state.service';
|
||||||
|
import { NostrLoginService } from 'app/services/nostr-login.service';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { of, throwError } from 'rxjs';
|
||||||
|
|
||||||
|
describe('LoginComponent', () => {
|
||||||
|
let component: LoginComponent;
|
||||||
|
let fixture: ComponentFixture<LoginComponent>;
|
||||||
|
let mockRouter: jasmine.SpyObj<Router>;
|
||||||
|
let mockSignerService: jasmine.SpyObj<SignerService>;
|
||||||
|
let mockStateService: jasmine.SpyObj<StateService>;
|
||||||
|
let mockNostrLoginService: jasmine.SpyObj<NostrLoginService>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
mockRouter = jasmine.createSpyObj('Router', ['navigateByUrl']);
|
||||||
|
mockSignerService = jasmine.createSpyObj('SignerService', ['handleLoginWithKey', 'handleLoginWithMnemonic', 'handleLoginWithExtension', 'getPublicKey', 'setPublicKey']);
|
||||||
|
mockStateService = jasmine.createSpyObj('StateService', ['loadUserProfile']);
|
||||||
|
mockNostrLoginService = jasmine.createSpyObj('NostrLoginService', ['getPublicKeyObservable', 'launchLoginScreen', 'launchSignupScreen']);
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [LoginComponent],
|
||||||
|
imports: [ReactiveFormsModule],
|
||||||
|
providers: [
|
||||||
|
{ provide: Router, useValue: mockRouter },
|
||||||
|
{ provide: SignerService, useValue: mockSignerService },
|
||||||
|
{ provide: StateService, useValue: mockStateService },
|
||||||
|
{ provide: NostrLoginService, useValue: mockNostrLoginService },
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
mockNostrLoginService.getPublicKeyObservable.and.returnValue(of('mockPublicKey'));
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize forms on init', () => {
|
||||||
|
expect(component.SecretKeyLoginForm).toBeDefined();
|
||||||
|
expect(component.MenemonicLoginForm).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle login with secret key', () => {
|
||||||
|
component.SecretKeyLoginForm.setValue({ secretKey: 'mockSecretKey', password: 'mockPassword' });
|
||||||
|
mockSignerService.handleLoginWithKey.and.returnValue(true);
|
||||||
|
|
||||||
|
component.loginWithSecretKey();
|
||||||
|
|
||||||
|
expect(mockSignerService.handleLoginWithKey).toHaveBeenCalledWith('mockSecretKey', 'mockPassword');
|
||||||
|
expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('/home');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle login with mnemonic', () => {
|
||||||
|
component.MenemonicLoginForm.setValue({ menemonic: 'mockMnemonic', passphrase: 'mockPassphrase', password: 'mockPassword' });
|
||||||
|
mockSignerService.handleLoginWithMnemonic.and.returnValue(true);
|
||||||
|
|
||||||
|
component.loginWithMenemonic();
|
||||||
|
|
||||||
|
expect(mockSignerService.handleLoginWithMnemonic).toHaveBeenCalledWith('mockMnemonic', 'mockPassphrase', 'mockPassword');
|
||||||
|
expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('/home');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle login with Nostr extension', async () => {
|
||||||
|
mockSignerService.handleLoginWithExtension.and.returnValue(Promise.resolve(true));
|
||||||
|
|
||||||
|
await component.loginWithNostrExtension();
|
||||||
|
|
||||||
|
expect(mockSignerService.handleLoginWithExtension).toHaveBeenCalled();
|
||||||
|
expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('/home');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle login failure with secret key', () => {
|
||||||
|
component.SecretKeyLoginForm.setValue({ secretKey: 'mockSecretKey', password: 'mockPassword' });
|
||||||
|
mockSignerService.handleLoginWithKey.and.returnValue(false);
|
||||||
|
|
||||||
|
component.loginWithSecretKey();
|
||||||
|
|
||||||
|
expect(component.loading()).toBeFalse();
|
||||||
|
expect(component.showSecAlert()).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle login failure with mnemonic', () => {
|
||||||
|
component.MenemonicLoginForm.setValue({ menemonic: 'mockMnemonic', passphrase: 'mockPassphrase', password: 'mockPassword' });
|
||||||
|
mockSignerService.handleLoginWithMnemonic.and.returnValue(false);
|
||||||
|
|
||||||
|
component.loginWithMenemonic();
|
||||||
|
|
||||||
|
expect(component.loading()).toBeFalse();
|
||||||
|
expect(component.showMenemonicAlert()).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error during login with Nostr extension', async () => {
|
||||||
|
mockSignerService.handleLoginWithExtension.and.returnValue(Promise.reject('error'));
|
||||||
|
|
||||||
|
await component.loginWithNostrExtension();
|
||||||
|
|
||||||
|
expect(component.loading()).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { AngorAlertComponent, AngorAlertType } from '@angor/components/alert';
|
import { AngorAlertComponent, AngorAlertType } from '@angor/components/alert';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, inject, signal } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
@@ -40,37 +40,33 @@ import { Subscription } from 'rxjs';
|
|||||||
export class LoginComponent implements OnInit {
|
export class LoginComponent implements OnInit {
|
||||||
SecretKeyLoginForm: FormGroup;
|
SecretKeyLoginForm: FormGroup;
|
||||||
MenemonicLoginForm: FormGroup;
|
MenemonicLoginForm: FormGroup;
|
||||||
secAlert = { type: 'error' as AngorAlertType, message: '' };
|
secAlert = signal({ type: 'error' as AngorAlertType, message: '' });
|
||||||
showSecAlert = false;
|
showSecAlert = signal(false);
|
||||||
|
|
||||||
menemonicAlert = { type: 'error' as AngorAlertType, message: '' };
|
menemonicAlert = signal({ type: 'error' as AngorAlertType, message: '' });
|
||||||
showMenemonicAlert = false;
|
showMenemonicAlert = signal(false);
|
||||||
|
|
||||||
loading = false;
|
loading = signal(false);
|
||||||
isInstalledExtension = false;
|
isInstalledExtension = signal(false);
|
||||||
privateKey: Uint8Array = new Uint8Array();
|
privateKey: Uint8Array = new Uint8Array();
|
||||||
publicKey: string = '';
|
publicKey = signal('');
|
||||||
npub: string = '';
|
npub = signal('');
|
||||||
nsec: string = '';
|
nsec = signal('');
|
||||||
|
|
||||||
useNostrLogin = true;
|
|
||||||
|
|
||||||
|
useNostrLogin = signal(true);
|
||||||
|
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
private _formBuilder = inject(FormBuilder);
|
||||||
private _formBuilder: FormBuilder,
|
private _router = inject(Router);
|
||||||
private _router: Router,
|
private _signerService = inject(SignerService);
|
||||||
private _signerService: SignerService,
|
private _stateService = inject(StateService);
|
||||||
private _stateService: StateService,
|
private _nostrLoginService = inject(NostrLoginService);
|
||||||
private _nostrLoginService: NostrLoginService
|
|
||||||
) { }
|
|
||||||
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.subscription = this._nostrLoginService.getPublicKeyObservable().subscribe({
|
this.subscription = this._nostrLoginService.getPublicKeyObservable().subscribe({
|
||||||
next: (pubkey: string) => {
|
next: (pubkey: string) => {
|
||||||
this.publicKey = pubkey;
|
this.publicKey.set(pubkey);
|
||||||
this._signerService.setPublicKey(pubkey);
|
this._signerService.setPublicKey(pubkey);
|
||||||
this.initializeAppState();
|
this.initializeAppState();
|
||||||
this._router.navigateByUrl('/home');
|
this._router.navigateByUrl('/home');
|
||||||
@@ -106,7 +102,7 @@ export class LoginComponent implements OnInit {
|
|||||||
|
|
||||||
this.MenemonicLoginForm = this._formBuilder.group({
|
this.MenemonicLoginForm = this._formBuilder.group({
|
||||||
menemonic: ['', [Validators.required, Validators.minLength(3)]],
|
menemonic: ['', [Validators.required, Validators.minLength(3)]],
|
||||||
passphrase: [''],
|
passphrase: [''],
|
||||||
password: [''],
|
password: [''],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -120,9 +116,9 @@ export class LoginComponent implements OnInit {
|
|||||||
globalContext.nostr &&
|
globalContext.nostr &&
|
||||||
typeof globalContext.nostr.signEvent === 'function'
|
typeof globalContext.nostr.signEvent === 'function'
|
||||||
) {
|
) {
|
||||||
this.isInstalledExtension = true;
|
this.isInstalledExtension.set(true);
|
||||||
} else {
|
} else {
|
||||||
this.isInstalledExtension = false;
|
this.isInstalledExtension.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,8 +130,8 @@ export class LoginComponent implements OnInit {
|
|||||||
const secretKey = this.SecretKeyLoginForm.get('secretKey')?.value;
|
const secretKey = this.SecretKeyLoginForm.get('secretKey')?.value;
|
||||||
const password = this.SecretKeyLoginForm.get('password')?.value;
|
const password = this.SecretKeyLoginForm.get('password')?.value;
|
||||||
|
|
||||||
this.loading = true;
|
this.loading.set(true);
|
||||||
this.showSecAlert = false;
|
this.showSecAlert.set(false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const success = this._signerService.handleLoginWithKey(
|
const success = this._signerService.handleLoginWithKey(
|
||||||
@@ -152,12 +148,9 @@ export class LoginComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Handle login failure
|
// Handle login failure
|
||||||
this.loading = false;
|
this.loading.set(false);
|
||||||
this.secAlert.message =
|
this.secAlert.update(alert => ({ ...alert, message: error instanceof Error ? error.message : 'An unexpected error occurred.' }));
|
||||||
error instanceof Error
|
this.showSecAlert.set(true);
|
||||||
? error.message
|
|
||||||
: 'An unexpected error occurred.';
|
|
||||||
this.showSecAlert = true;
|
|
||||||
console.error('Login error: ', error);
|
console.error('Login error: ', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,8 +165,8 @@ export class LoginComponent implements OnInit {
|
|||||||
this.MenemonicLoginForm.get('passphrase')?.value || ''; // Optional passphrase
|
this.MenemonicLoginForm.get('passphrase')?.value || ''; // Optional passphrase
|
||||||
const password = this.MenemonicLoginForm.get('password')?.value;
|
const password = this.MenemonicLoginForm.get('password')?.value;
|
||||||
|
|
||||||
this.loading = true;
|
this.loading.set(true);
|
||||||
this.showMenemonicAlert = false;
|
this.showMenemonicAlert.set(false);
|
||||||
|
|
||||||
const success = this._signerService.handleLoginWithMnemonic(
|
const success = this._signerService.handleLoginWithMnemonic(
|
||||||
menemonic,
|
menemonic,
|
||||||
@@ -185,9 +178,9 @@ export class LoginComponent implements OnInit {
|
|||||||
this.initializeAppState();
|
this.initializeAppState();
|
||||||
this._router.navigateByUrl('/home');
|
this._router.navigateByUrl('/home');
|
||||||
} else {
|
} else {
|
||||||
this.loading = false;
|
this.loading.set(false);
|
||||||
this.menemonicAlert.message = 'Menemonic is missing or invalid.';
|
this.menemonicAlert.update(alert => ({ ...alert, message: 'Menemonic is missing or invalid.' }));
|
||||||
this.showMenemonicAlert = true;
|
this.showMenemonicAlert.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
const webpack = require('webpack');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
resolve: {
|
|
||||||
fallback: {
|
|
||||||
crypto: require.resolve('crypto-browserify'),
|
|
||||||
stream: require.resolve('stream-browserify'),
|
|
||||||
// url: require.resolve('url/'),
|
|
||||||
url: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new webpack.ProvidePlugin({
|
|
||||||
process: 'process/browser',
|
|
||||||
}),
|
|
||||||
new webpack.NormalModuleReplacementPlugin(
|
|
||||||
/node:crypto/,
|
|
||||||
require.resolve('crypto-browserify')
|
|
||||||
),
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
global: 'globalThis',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user