Compare commits
15 Commits
2b342ec235
...
fix/cart-e
| Author | SHA1 | Date | |
|---|---|---|---|
| eb3a084d18 | |||
| e4f9d38e82 | |||
| b0e2efbdd1 | |||
| 1ad7bccf16 | |||
| a1d6585b54 | |||
| 591f53c102 | |||
| 38d03588b1 | |||
| 8e987d0ce6 | |||
| e171b9a233 | |||
| 643d43adea | |||
| d89d01b511 | |||
| eeb245209b | |||
| 9d3754c2d8 | |||
| 85529f40f2 | |||
| 6f4aa3b1bd |
@@ -5,6 +5,7 @@ import {
|
|||||||
CheckoutService,
|
CheckoutService,
|
||||||
PurchaseDetailResponse,
|
PurchaseDetailResponse,
|
||||||
} from '../../../../core/services/checkout.service';
|
} from '../../../../core/services/checkout.service';
|
||||||
|
import { CartService } from '../../../../core/services/cart/cart.service';
|
||||||
import { Tenant } from '../../../../core/services/tenant.interface';
|
import { Tenant } from '../../../../core/services/tenant.interface';
|
||||||
import { TenantService } from '../../../../core/services/tenant.service';
|
import { TenantService } from '../../../../core/services/tenant.service';
|
||||||
import { PurchaseStatusPageComponent } from './purchase-status-page.component';
|
import { PurchaseStatusPageComponent } from './purchase-status-page.component';
|
||||||
@@ -77,11 +78,13 @@ describe('PurchaseStatusPageComponent', () => {
|
|||||||
navigate: vi.fn().mockResolvedValue(true),
|
navigate: vi.fn().mockResolvedValue(true),
|
||||||
navigateByUrl: vi.fn().mockResolvedValue(true),
|
navigateByUrl: vi.fn().mockResolvedValue(true),
|
||||||
};
|
};
|
||||||
|
const cartService = { clearCart: vi.fn() };
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [PurchaseStatusPageComponent],
|
imports: [PurchaseStatusPageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: CheckoutService, useValue: checkoutService },
|
{ provide: CheckoutService, useValue: checkoutService },
|
||||||
|
{ provide: CartService, useValue: cartService },
|
||||||
{ provide: TenantService, useValue: { tenant: () => tenant } },
|
{ provide: TenantService, useValue: { tenant: () => tenant } },
|
||||||
{
|
{
|
||||||
provide: ActivatedRoute,
|
provide: ActivatedRoute,
|
||||||
@@ -107,15 +110,22 @@ describe('PurchaseStatusPageComponent', () => {
|
|||||||
fixture,
|
fixture,
|
||||||
element: fixture.nativeElement as HTMLElement,
|
element: fixture.nativeElement as HTMLElement,
|
||||||
checkoutService,
|
checkoutService,
|
||||||
|
cartService,
|
||||||
router,
|
router,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('clears the cart when the purchase is approved', async () => {
|
||||||
|
const { cartService } = await render(true);
|
||||||
|
|
||||||
|
expect(cartService.clearCart).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
it('shows the tickets action when this purchase generated tickets', async () => {
|
it('shows the tickets action when this purchase generated tickets', async () => {
|
||||||
const { element, checkoutService, router } = await render(true);
|
const { element, checkoutService, router } = await render(true);
|
||||||
|
|
||||||
expect(checkoutService.getPurchase).toHaveBeenCalledOnce();
|
expect(checkoutService.getPurchase).toHaveBeenCalledOnce();
|
||||||
expect(element.textContent).toContain('Ver mis tickets');
|
expect(element.textContent).toContain('Mis tickets');
|
||||||
expect(element.textContent).not.toContain('WhatsApp');
|
expect(element.textContent).not.toContain('WhatsApp');
|
||||||
|
|
||||||
element.querySelector<HTMLButtonElement>('app-button button')?.click();
|
element.querySelector<HTMLButtonElement>('app-button button')?.click();
|
||||||
@@ -162,11 +172,13 @@ describe('PurchaseStatusPageComponent', () => {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const cartService = { clearCart: vi.fn() };
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [PurchaseStatusPageComponent],
|
imports: [PurchaseStatusPageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: CheckoutService, useValue: checkoutService },
|
{ provide: CheckoutService, useValue: checkoutService },
|
||||||
|
{ provide: CartService, useValue: cartService },
|
||||||
{ provide: TenantService, useValue: { tenant: () => tenant } },
|
{ provide: TenantService, useValue: { tenant: () => tenant } },
|
||||||
{
|
{
|
||||||
provide: ActivatedRoute,
|
provide: ActivatedRoute,
|
||||||
@@ -189,6 +201,7 @@ describe('PurchaseStatusPageComponent', () => {
|
|||||||
|
|
||||||
expect(checkoutService.getPurchase).toHaveBeenCalledTimes(2);
|
expect(checkoutService.getPurchase).toHaveBeenCalledTimes(2);
|
||||||
expect(fixture.nativeElement.textContent).toContain('COMPRA REALIZADA!');
|
expect(fixture.nativeElement.textContent).toContain('COMPRA REALIZADA!');
|
||||||
|
expect(cartService.clearCart).toHaveBeenCalledOnce();
|
||||||
|
|
||||||
await vi.advanceTimersByTimeAsync(10_000);
|
await vi.advanceTimersByTimeAsync(10_000);
|
||||||
expect(checkoutService.getPurchase).toHaveBeenCalledTimes(2);
|
expect(checkoutService.getPurchase).toHaveBeenCalledTimes(2);
|
||||||
@@ -212,6 +225,7 @@ describe('PurchaseStatusPageComponent', () => {
|
|||||||
imports: [PurchaseStatusPageComponent],
|
imports: [PurchaseStatusPageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: CheckoutService, useValue: checkoutService },
|
{ provide: CheckoutService, useValue: checkoutService },
|
||||||
|
{ provide: CartService, useValue: { clearCart: vi.fn() } },
|
||||||
{ provide: TenantService, useValue: { tenant: () => tenant } },
|
{ provide: TenantService, useValue: { tenant: () => tenant } },
|
||||||
{
|
{
|
||||||
provide: ActivatedRoute,
|
provide: ActivatedRoute,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
CheckoutService,
|
CheckoutService,
|
||||||
PurchaseStatusResponse,
|
PurchaseStatusResponse,
|
||||||
} from '../../../../core/services/checkout.service';
|
} from '../../../../core/services/checkout.service';
|
||||||
|
import { CartService } from '../../../../core/services/cart/cart.service';
|
||||||
import { findMenu } from '../../../../core/services/menu.utils';
|
import { findMenu } from '../../../../core/services/menu.utils';
|
||||||
import { TenantService } from '../../../../core/services/tenant.service';
|
import { TenantService } from '../../../../core/services/tenant.service';
|
||||||
import { ButtonComponent } from '../../../../shared/components/button/button.component';
|
import { ButtonComponent } from '../../../../shared/components/button/button.component';
|
||||||
@@ -35,6 +36,7 @@ export class PurchaseStatusPageComponent implements OnInit, OnDestroy {
|
|||||||
private readonly route = inject(ActivatedRoute);
|
private readonly route = inject(ActivatedRoute);
|
||||||
private readonly router = inject(Router);
|
private readonly router = inject(Router);
|
||||||
private readonly checkoutService = inject(CheckoutService);
|
private readonly checkoutService = inject(CheckoutService);
|
||||||
|
private readonly cartService = inject(CartService);
|
||||||
private readonly tenantService = inject(TenantService);
|
private readonly tenantService = inject(TenantService);
|
||||||
private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
||||||
|
|
||||||
@@ -104,6 +106,10 @@ export class PurchaseStatusPageComponent implements OnInit, OnDestroy {
|
|||||||
this.status.set(status);
|
this.status.set(status);
|
||||||
this.hasGeneratedTickets.set(purchase.has_generated_tickets === true);
|
this.hasGeneratedTickets.set(purchase.has_generated_tickets === true);
|
||||||
|
|
||||||
|
if (status === 'approved') {
|
||||||
|
this.cartService.clearCart();
|
||||||
|
}
|
||||||
|
|
||||||
if (status === 'pending') {
|
if (status === 'pending') {
|
||||||
this.schedulePolling();
|
this.schedulePolling();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -38,11 +38,13 @@
|
|||||||
<label class="visually-hidden" for="register-password">Contraseña</label>
|
<label class="visually-hidden" for="register-password">Contraseña</label>
|
||||||
<app-input
|
<app-input
|
||||||
id="register-password"
|
id="register-password"
|
||||||
type="password"
|
type="password-toggle"
|
||||||
placeholder="Contraseña"
|
placeholder="Contraseña"
|
||||||
[value]="form.controls.password.value"
|
[value]="form.controls.password.value"
|
||||||
[invalid]="showControlError('password')"
|
[invalid]="showControlError('password')"
|
||||||
|
[visible]="passwordVisible()"
|
||||||
(valueChange)="updateField('password', $event)"
|
(valueChange)="updateField('password', $event)"
|
||||||
|
(visibleChange)="setPasswordVisibility($event)"
|
||||||
/>
|
/>
|
||||||
@if (getControlError('password'); as errorMessage) {
|
@if (getControlError('password'); as errorMessage) {
|
||||||
<small class="text-danger">{{ errorMessage }}</small>
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
@@ -51,11 +53,13 @@
|
|||||||
<label class="visually-hidden" for="register-password-repeat">Repetir Contraseña</label>
|
<label class="visually-hidden" for="register-password-repeat">Repetir Contraseña</label>
|
||||||
<app-input
|
<app-input
|
||||||
id="register-password-repeat"
|
id="register-password-repeat"
|
||||||
type="password"
|
type="password-toggle"
|
||||||
placeholder="Repetir Contraseña"
|
placeholder="Repetir Contraseña"
|
||||||
[value]="form.controls.password_confirmation.value"
|
[value]="form.controls.password_confirmation.value"
|
||||||
[invalid]="showControlError('password_confirmation')"
|
[invalid]="showControlError('password_confirmation')"
|
||||||
|
[visible]="passwordVisible()"
|
||||||
(valueChange)="updateField('password_confirmation', $event)"
|
(valueChange)="updateField('password_confirmation', $event)"
|
||||||
|
(visibleChange)="setPasswordVisibility($event)"
|
||||||
/>
|
/>
|
||||||
@if (getControlError('password_confirmation'); as errorMessage) {
|
@if (getControlError('password_confirmation'); as errorMessage) {
|
||||||
<small class="text-danger">{{ errorMessage }}</small>
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
|
|||||||
@@ -12,6 +12,48 @@ describe('RegisterPageComponent', () => {
|
|||||||
TestBed.resetTestingModule();
|
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 () => {
|
it('submits registration data and redirects to /login on success', async () => {
|
||||||
const authService = {
|
const authService = {
|
||||||
register: vi.fn().mockReturnValue(
|
register: vi.fn().mockReturnValue(
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export class RegisterPageComponent {
|
|||||||
private readonly submittedState = signal(false);
|
private readonly submittedState = signal(false);
|
||||||
private readonly serverErrorState = signal<string | null>(null);
|
private readonly serverErrorState = signal<string | null>(null);
|
||||||
private readonly isSubmittingState = signal(false);
|
private readonly isSubmittingState = signal(false);
|
||||||
|
private readonly passwordVisibleState = signal(false);
|
||||||
|
|
||||||
protected readonly form = this.formBuilder.nonNullable.group({
|
protected readonly form = this.formBuilder.nonNullable.group({
|
||||||
nombre_apellido: ['', [Validators.required, Validators.maxLength(TEXT_MAX_LENGTH)]],
|
nombre_apellido: ['', [Validators.required, Validators.maxLength(TEXT_MAX_LENGTH)]],
|
||||||
@@ -67,6 +68,11 @@ export class RegisterPageComponent {
|
|||||||
protected readonly submitted = this.submittedState.asReadonly();
|
protected readonly submitted = this.submittedState.asReadonly();
|
||||||
protected readonly serverError = this.serverErrorState.asReadonly();
|
protected readonly serverError = this.serverErrorState.asReadonly();
|
||||||
protected readonly isSubmitting = this.isSubmittingState.asReadonly();
|
protected readonly isSubmitting = this.isSubmittingState.asReadonly();
|
||||||
|
protected readonly passwordVisible = this.passwordVisibleState.asReadonly();
|
||||||
|
|
||||||
|
protected setPasswordVisibility(visible: boolean): void {
|
||||||
|
this.passwordVisibleState.set(visible);
|
||||||
|
}
|
||||||
|
|
||||||
goToLogin(): void {
|
goToLogin(): void {
|
||||||
void this.router.navigate(['/login']);
|
void this.router.navigate(['/login']);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: true,
|
||||||
nombre:"Homologación - activo",
|
nombre:"Homologación - activo",
|
||||||
url:"https://backend.qa.shopit.com.ar/api/",
|
url:"https://backend.qa.shopit.com.ar/api/",
|
||||||
urlDescarga:"url/"
|
urlDescarga:"url/"
|
||||||
|
|||||||
Reference in New Issue
Block a user