From b1d4c4d13d7b06f3edc9b96e80d3faf852571e45 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Tue, 25 Aug 2026 11:26:56 -0300 Subject: [PATCH 1/3] fix(reset-password): implement password visibility toggle for input fields --- .../reset-password-page.component.html | 8 +++- .../reset-password-page.component.spec.ts | 37 +++++++++++++++++++ .../reset-password-page.component.ts | 6 +++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/app/features/store/pages/reset-password-page/reset-password-page.component.html b/src/app/features/store/pages/reset-password-page/reset-password-page.component.html index 582aaf6..647d450 100644 --- a/src/app/features/store/pages/reset-password-page/reset-password-page.component.html +++ b/src/app/features/store/pages/reset-password-page/reset-password-page.component.html @@ -13,11 +13,13 @@ @if (getControlError('password'); as errorMessage) { {{ errorMessage }} @@ -30,11 +32,13 @@ @if (getControlError('password_confirmation'); as errorMessage) { {{ errorMessage }} diff --git a/src/app/features/store/pages/reset-password-page/reset-password-page.component.spec.ts b/src/app/features/store/pages/reset-password-page/reset-password-page.component.spec.ts index fec20c9..636af77 100644 --- a/src/app/features/store/pages/reset-password-page/reset-password-page.component.spec.ts +++ b/src/app/features/store/pages/reset-password-page/reset-password-page.component.spec.ts @@ -50,6 +50,43 @@ describe('ResetPasswordPageComponent', () => { ]; } + it('shows and hides both password fields with either visibility control', async () => { + const modalService = { + openSimple: vi.fn(), + }; + + await TestBed.configureTestingModule({ + imports: [ResetPasswordPageComponent], + providers: resetProviders(modalService), + }).compileComponents(); + + const fixture = TestBed.createComponent(ResetPasswordPageComponent); + fixture.detectChanges(); + + const getPasswordInputs = () => + Array.from(fixture.nativeElement.querySelectorAll('input')) as HTMLInputElement[]; + const getVisibilityButtons = () => + Array.from( + fixture.nativeElement.querySelectorAll('button[aria-label]'), + ) as HTMLButtonElement[]; + + expect(getPasswordInputs().map((input) => input.type)).toEqual(['password', 'password']); + + getVisibilityButtons()[0].click(); + fixture.detectChanges(); + + expect(getPasswordInputs().map((input) => input.type)).toEqual(['text', 'text']); + expect(getVisibilityButtons().map((button) => button.getAttribute('aria-label'))).toEqual([ + 'Ocultar contraseña', + 'Ocultar contraseña', + ]); + + getVisibilityButtons()[1].click(); + fixture.detectChanges(); + + expect(getPasswordInputs().map((input) => input.type)).toEqual(['password', 'password']); + }); + it('rejects passwords that do not match', async () => { const modalService = { openSimple: vi.fn(), diff --git a/src/app/features/store/pages/reset-password-page/reset-password-page.component.ts b/src/app/features/store/pages/reset-password-page/reset-password-page.component.ts index eb1fb04..b320af8 100644 --- a/src/app/features/store/pages/reset-password-page/reset-password-page.component.ts +++ b/src/app/features/store/pages/reset-password-page/reset-password-page.component.ts @@ -49,6 +49,7 @@ export class ResetPasswordPageComponent { private readonly submittedState = signal(false); private readonly isSubmittingState = signal(false); private readonly serverErrorState = signal(null); + private readonly passwordVisibleState = signal(false); private readonly email = this.route.snapshot.queryParamMap.get('email') ?? ''; private readonly code = this.route.snapshot.queryParamMap.get('code') ?? ''; @@ -72,6 +73,11 @@ export class ResetPasswordPageComponent { protected readonly submitted = this.submittedState.asReadonly(); protected readonly isSubmitting = this.isSubmittingState.asReadonly(); protected readonly serverError = this.serverErrorState.asReadonly(); + protected readonly passwordVisible = this.passwordVisibleState.asReadonly(); + + protected setPasswordVisibility(visible: boolean): void { + this.passwordVisibleState.set(visible); + } protected updatePassword(controlName: PasswordControlName, value: string | number): void { this.form.controls[controlName].setValue(String(value)); -- 2.39.5 From 4a952af784803a6d254f7e10819208a6c0a3e639 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Tue, 25 Aug 2026 11:44:24 -0300 Subject: [PATCH 2/3] fix(favicon): update favicon handling to use a default SVG icon --- src/app/app.spec.ts | 2 +- src/app/app.ts | 4 +++- src/index.html | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/app.spec.ts b/src/app/app.spec.ts index e7a979b..a446d61 100644 --- a/src/app/app.spec.ts +++ b/src/app/app.spec.ts @@ -115,6 +115,6 @@ describe('App', () => { expect(fixture.nativeElement.textContent).toContain('No encontramos una tienda para este dominio'); expect(document.title).toBe('ShopitFront'); expect(document.head.querySelector('link[rel~="icon"]')?.getAttribute('href')) - .toBe('favicon.ico'); + .toBe("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E"); }); }); diff --git a/src/app/app.ts b/src/app/app.ts index c5631f8..e69b2e4 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -9,6 +9,8 @@ import { GlobalLoadingComponent } from './shared/components/global-loading/globa import { ModalHostComponent } from './shared/components/modal-host/modal-host.component'; import { ToastContainerComponent } from './shared/components/toast-container/toast-container.component'; +const EMPTY_FAVICON = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E"; + function hexToRgb(hex: string): string { const cleanHex = hex.replace('#', '').trim(); let r = 0, g = 0, b = 0; @@ -66,7 +68,7 @@ export class App { effect(() => { const tenant = this.tenantService.tenant(); const siteTitle = tenant?.site_title?.trim() || 'ShopitFront'; - const faviconHref = tenant?.favicon || 'favicon.ico'; + const faviconHref = tenant?.favicon || EMPTY_FAVICON; this.title.setTitle(siteTitle); diff --git a/src/index.html b/src/index.html index c7d13ff..cc30d1b 100644 --- a/src/index.html +++ b/src/index.html @@ -5,7 +5,7 @@ ShopitFront - + -- 2.39.5 From 85529f40f2c5278adc01e279e0c732461200672b Mon Sep 17 00:00:00 2001 From: ncoronel Date: Wed, 26 Aug 2026 10:02:56 -0300 Subject: [PATCH 3/3] fix(register-page): implement password visibility toggle for registration fields --- .../register-page.component.html | 8 +++- .../register-page.component.spec.ts | 42 +++++++++++++++++++ .../register-page/register-page.component.ts | 6 +++ src/environments/environment.homo.ts | 2 +- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/app/features/store/pages/register-page/register-page.component.html b/src/app/features/store/pages/register-page/register-page.component.html index 93606df..da3cfc4 100644 --- a/src/app/features/store/pages/register-page/register-page.component.html +++ b/src/app/features/store/pages/register-page/register-page.component.html @@ -38,11 +38,13 @@ @if (getControlError('password'); as errorMessage) { {{ errorMessage }} @@ -51,11 +53,13 @@ @if (getControlError('password_confirmation'); as errorMessage) { {{ errorMessage }} diff --git a/src/app/features/store/pages/register-page/register-page.component.spec.ts b/src/app/features/store/pages/register-page/register-page.component.spec.ts index c190fb7..1376ffb 100644 --- a/src/app/features/store/pages/register-page/register-page.component.spec.ts +++ b/src/app/features/store/pages/register-page/register-page.component.spec.ts @@ -12,6 +12,48 @@ describe('RegisterPageComponent', () => { TestBed.resetTestingModule(); }); + it('shows and hides both password fields with either visibility control', async () => { + await TestBed.configureTestingModule({ + imports: [RegisterPageComponent], + providers: [ + provideRouter([]), + { provide: AuthService, useValue: { register: vi.fn() } }, + { provide: ModalService, useValue: { openSimple: vi.fn() } }, + { provide: ToastService, useValue: { danger: vi.fn() } } + ] + }).compileComponents(); + + const fixture = TestBed.createComponent(RegisterPageComponent); + fixture.detectChanges(); + + const getPasswordInputs = () => + Array.from( + fixture.nativeElement.querySelectorAll( + 'input#register-password, input#register-password-repeat' + ) + ) as HTMLInputElement[]; + const getVisibilityButtons = () => + Array.from( + fixture.nativeElement.querySelectorAll('button[aria-label]') + ) as HTMLButtonElement[]; + + expect(getPasswordInputs().map((input) => input.type)).toEqual(['password', 'password']); + + getVisibilityButtons()[0].click(); + fixture.detectChanges(); + + expect(getPasswordInputs().map((input) => input.type)).toEqual(['text', 'text']); + expect(getVisibilityButtons().map((button) => button.getAttribute('aria-label'))).toEqual([ + 'Ocultar contraseña', + 'Ocultar contraseña' + ]); + + getVisibilityButtons()[1].click(); + fixture.detectChanges(); + + expect(getPasswordInputs().map((input) => input.type)).toEqual(['password', 'password']); + }); + it('submits registration data and redirects to /login on success', async () => { const authService = { register: vi.fn().mockReturnValue( diff --git a/src/app/features/store/pages/register-page/register-page.component.ts b/src/app/features/store/pages/register-page/register-page.component.ts index de6d3e6..11d689a 100644 --- a/src/app/features/store/pages/register-page/register-page.component.ts +++ b/src/app/features/store/pages/register-page/register-page.component.ts @@ -48,6 +48,7 @@ export class RegisterPageComponent { private readonly submittedState = signal(false); private readonly serverErrorState = signal(null); private readonly isSubmittingState = signal(false); + private readonly passwordVisibleState = signal(false); protected readonly form = this.formBuilder.nonNullable.group({ nombre_apellido: ['', [Validators.required, Validators.maxLength(TEXT_MAX_LENGTH)]], @@ -67,6 +68,11 @@ export class RegisterPageComponent { protected readonly submitted = this.submittedState.asReadonly(); protected readonly serverError = this.serverErrorState.asReadonly(); protected readonly isSubmitting = this.isSubmittingState.asReadonly(); + protected readonly passwordVisible = this.passwordVisibleState.asReadonly(); + + protected setPasswordVisibility(visible: boolean): void { + this.passwordVisibleState.set(visible); + } goToLogin(): void { void this.router.navigate(['/login']); diff --git a/src/environments/environment.homo.ts b/src/environments/environment.homo.ts index 6b23224..5bd05cb 100644 --- a/src/environments/environment.homo.ts +++ b/src/environments/environment.homo.ts @@ -1,5 +1,5 @@ export const environment = { - production: false, + production: true, nombre:"Homologación - activo", url:"https://backend.qa.shopit.com.ar/api/", urlDescarga:"url/" -- 2.39.5