refactor(event): replace bootstrap notices with claimed notices
This commit is contained in:
@@ -6,7 +6,6 @@ import { provideRouter } from '@angular/router';
|
|||||||
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
import { App } from './app';
|
import { App } from './app';
|
||||||
import { EventDateNoticeService } from './core/services/event-date-notice.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';
|
||||||
|
|
||||||
@@ -117,48 +116,4 @@ describe('App', () => {
|
|||||||
).toBe("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E");
|
).toBe("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows the date notices returned by the tenant bootstrap once', async () => {
|
|
||||||
const show = vi.fn();
|
|
||||||
const dateNotices = [
|
|
||||||
{
|
|
||||||
type: 'suspended' as const,
|
|
||||||
title: 'FECHA CANCELADA!',
|
|
||||||
message: [
|
|
||||||
{ text: 'La fecha del ', bold: false },
|
|
||||||
{ text: '09 de Octubre de 2026', bold: true },
|
|
||||||
{ text: ' ha sido cancelada.', bold: false },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [App],
|
|
||||||
providers: [
|
|
||||||
provideRouter([]),
|
|
||||||
{
|
|
||||||
provide: TenantService,
|
|
||||||
useValue: createTenantServiceStub('ready', {
|
|
||||||
...tenant,
|
|
||||||
event: {
|
|
||||||
title: 'Festival',
|
|
||||||
location: 'Predio',
|
|
||||||
dates: [],
|
|
||||||
date_notices: dateNotices,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: EventDateNoticeService,
|
|
||||||
useValue: { show },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
const fixture = TestBed.createComponent(App);
|
|
||||||
fixture.detectChanges();
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(show).toHaveBeenCalledOnce();
|
|
||||||
expect(show).toHaveBeenCalledWith(dateNotices);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
import { Component, PLATFORM_ID, computed, effect, inject } from '@angular/core';
|
import { Component, computed, effect, inject } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
|
||||||
import { EventDateNoticeService } from './core/services/event-date-notice.service';
|
|
||||||
import { TenantService } from './core/services/tenant.service';
|
import { TenantService } from './core/services/tenant.service';
|
||||||
import { DEFAULT_TENANT_BRANDING } from './core/services/tenant-ssr-cache.store';
|
import { DEFAULT_TENANT_BRANDING } from './core/services/tenant-ssr-cache.store';
|
||||||
import { GlobalLoadingComponent } from './shared/components/global-loading/global-loading.component';
|
import { GlobalLoadingComponent } from './shared/components/global-loading/global-loading.component';
|
||||||
@@ -59,11 +58,8 @@ function hexToRgb(hex: string): string {
|
|||||||
})
|
})
|
||||||
export class App {
|
export class App {
|
||||||
private readonly tenantService = inject(TenantService);
|
private readonly tenantService = inject(TenantService);
|
||||||
private readonly eventDateNoticeService = inject(EventDateNoticeService);
|
|
||||||
private readonly document = inject(DOCUMENT);
|
private readonly document = inject(DOCUMENT);
|
||||||
private readonly title = inject(Title);
|
private readonly title = inject(Title);
|
||||||
private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
||||||
private notifiedTenantCode: string | null = null;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
@@ -82,11 +78,6 @@ export class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
favicon.setAttribute('href', faviconHref);
|
favicon.setAttribute('href', faviconHref);
|
||||||
|
|
||||||
if (this.isBrowser && tenant && this.notifiedTenantCode !== tenant.codigo) {
|
|
||||||
this.notifiedTenantCode = tenant.codigo;
|
|
||||||
this.eventDateNoticeService.show(tenant.event?.date_notices ?? []);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,85 +1,59 @@
|
|||||||
import '@angular/compiler';
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
|
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { getTestBed } from '@angular/core/testing';
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||||
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
|
|
||||||
import { Subject } from 'rxjs';
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
||||||
|
|
||||||
import { EventDateNoticeModalComponent } from '../../shared/components/event-date-notice-modal/event-date-notice-modal.component';
|
import { environment } from '../../../environments/environment';
|
||||||
import { EventDateNoticeService } from './event-date-notice.service';
|
import { EventDateNotice, EventDateNoticeService } from './event-date-notice.service';
|
||||||
import { ModalService } from './modal.service';
|
import { ApiResponse } from './api-response.interface';
|
||||||
import { TenantEventDateNotice } from './tenant.interface';
|
import { TenantService } from './tenant.service';
|
||||||
|
|
||||||
describe('EventDateNoticeService', () => {
|
describe('EventDateNoticeService', () => {
|
||||||
let service: EventDateNoticeService;
|
let service: EventDateNoticeService;
|
||||||
let open: ReturnType<typeof vi.fn>;
|
let httpMock: HttpTestingController;
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
try {
|
|
||||||
getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
|
|
||||||
} catch {
|
|
||||||
// Test environment may already be initialized by another setup entrypoint.
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
open = vi.fn();
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
EventDateNoticeService,
|
EventDateNoticeService,
|
||||||
|
provideHttpClient(),
|
||||||
|
provideHttpClientTesting(),
|
||||||
{
|
{
|
||||||
provide: ModalService,
|
provide: TenantService,
|
||||||
useValue: { open },
|
useValue: { getTenant: () => ({ codigo: 'festival' }) },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
service = TestBed.inject(EventDateNoticeService);
|
service = TestBed.inject(EventDateNoticeService);
|
||||||
|
httpMock = TestBed.inject(HttpTestingController);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => httpMock.verify());
|
||||||
TestBed.resetTestingModule();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows backend notices sequentially', () => {
|
it('claims pending notices for the active tenant', () => {
|
||||||
const firstClosed = new Subject<void | undefined>();
|
const response = {
|
||||||
const secondClosed = new Subject<void | undefined>();
|
data: [
|
||||||
const notices: TenantEventDateNotice[] = [
|
{
|
||||||
{
|
type: 'suspended' as const,
|
||||||
type: 'suspended',
|
change_ids: [10],
|
||||||
title: 'FECHA CANCELADA!',
|
title: 'FECHA CANCELADA!',
|
||||||
message: [{ text: 'La fecha fue cancelada.', bold: false }],
|
message: [{ text: 'La fecha fue cancelada.', bold: false }],
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
type: 'rescheduled',
|
};
|
||||||
title: 'FECHA REPROGRAMADA!',
|
let result: ApiResponse<EventDateNotice[]> | undefined;
|
||||||
message: [{ text: 'La fecha fue reprogramada.', bold: false }],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
open
|
service.claim().subscribe((value) => (result = value));
|
||||||
.mockReturnValueOnce({ afterClosed$: firstClosed.asObservable() })
|
|
||||||
.mockReturnValueOnce({ afterClosed$: secondClosed.asObservable() });
|
|
||||||
|
|
||||||
service.show(notices);
|
const request = httpMock.expectOne(
|
||||||
|
`${environment.url}tenants/festival/event-date-notices/claim`,
|
||||||
|
);
|
||||||
|
expect(request.request.method).toBe('POST');
|
||||||
|
expect(request.request.body).toEqual({});
|
||||||
|
|
||||||
expect(open).toHaveBeenCalledTimes(1);
|
request.flush(response);
|
||||||
expect(open).toHaveBeenNthCalledWith(1, EventDateNoticeModalComponent, {
|
|
||||||
size: 'sm',
|
|
||||||
data: notices[0],
|
|
||||||
});
|
|
||||||
|
|
||||||
firstClosed.next(undefined);
|
expect(result).toEqual(response);
|
||||||
|
|
||||||
expect(open).toHaveBeenCalledTimes(2);
|
|
||||||
expect(open).toHaveBeenNthCalledWith(2, EventDateNoticeModalComponent, {
|
|
||||||
size: 'sm',
|
|
||||||
data: notices[1],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not open a modal without notices', () => {
|
|
||||||
service.show([]);
|
|
||||||
|
|
||||||
expect(open).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -73,34 +73,10 @@ export interface ActiveEventDate {
|
|||||||
isCanceled: boolean;
|
isCanceled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TenantEventDateChangeType = 'rescheduled' | 'suspended';
|
|
||||||
|
|
||||||
export interface TenantEventDateChange {
|
|
||||||
type: TenantEventDateChangeType;
|
|
||||||
source_event_date_id: number | null;
|
|
||||||
destination_event_date_id: number | null;
|
|
||||||
previous_date: string;
|
|
||||||
new_date: string | null;
|
|
||||||
occurred_at: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TenantEventDateNoticeMessagePart {
|
|
||||||
text: string;
|
|
||||||
bold: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TenantEventDateNotice {
|
|
||||||
type: TenantEventDateChangeType;
|
|
||||||
title: string;
|
|
||||||
message: TenantEventDateNoticeMessagePart[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TenantEvent {
|
export interface TenantEvent {
|
||||||
title: string;
|
title: string;
|
||||||
location: string;
|
location: string;
|
||||||
dates: ActiveEventDate[];
|
dates: ActiveEventDate[];
|
||||||
date_changes?: TenantEventDateChange[];
|
|
||||||
date_notices?: TenantEventDateNotice[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WebsiteExtras {
|
export interface WebsiteExtras {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { TestBed, getTestBed } from '@angular/core/testing';
|
|||||||
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
|
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
|
||||||
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { EventDateNotice } from '../../../core/services/event-date-notice.service';
|
||||||
import { MODAL_DATA, ModalRef } from '../../../core/services/modal.service';
|
import { MODAL_DATA, ModalRef } from '../../../core/services/modal.service';
|
||||||
import { TenantEventDateNotice } from '../../../core/services/tenant.interface';
|
|
||||||
import { EventDateNoticeModalComponent } from './event-date-notice-modal.component';
|
import { EventDateNoticeModalComponent } from './event-date-notice-modal.component';
|
||||||
|
|
||||||
describe('EventDateNoticeModalComponent', () => {
|
describe('EventDateNoticeModalComponent', () => {
|
||||||
@@ -22,8 +22,9 @@ describe('EventDateNoticeModalComponent', () => {
|
|||||||
|
|
||||||
it('renders the precalculated message and emphasizes its marked parts', async () => {
|
it('renders the precalculated message and emphasizes its marked parts', async () => {
|
||||||
const close = vi.fn();
|
const close = vi.fn();
|
||||||
const data: TenantEventDateNotice = {
|
const data: EventDateNotice = {
|
||||||
type: 'rescheduled',
|
type: 'rescheduled',
|
||||||
|
change_ids: [1, 2],
|
||||||
title: 'FECHAS REPROGRAMADAS!',
|
title: 'FECHAS REPROGRAMADAS!',
|
||||||
message: [
|
message: [
|
||||||
{ text: 'Las fechas del ', bold: false },
|
{ text: 'Las fechas del ', bold: false },
|
||||||
@@ -48,7 +49,7 @@ describe('EventDateNoticeModalComponent', () => {
|
|||||||
|
|
||||||
expect(element.querySelector('h2')?.textContent).toContain(data.title);
|
expect(element.querySelector('h2')?.textContent).toContain(data.title);
|
||||||
expect(
|
expect(
|
||||||
[...element.querySelectorAll('strong')].map((strong) => strong.textContent?.trim()),
|
[...element.querySelectorAll('b')].map((strong) => strong.textContent?.trim()),
|
||||||
).toEqual(['09 y 10 de Octubre de 2026', 'respectivamente']);
|
).toEqual(['09 y 10 de Octubre de 2026', 'respectivamente']);
|
||||||
|
|
||||||
element.querySelector('button')?.click();
|
element.querySelector('button')?.click();
|
||||||
|
|||||||
Reference in New Issue
Block a user