Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 205 additions & 45 deletions core-web/apps/dotcms-ui/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import { of } from 'rxjs';
import { of, throwError } from 'rxjs';

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { DebugElement } from '@angular/core';
Expand All @@ -11,6 +11,7 @@ import { RouterTestingModule } from '@angular/router/testing';
import { ConfirmationService } from 'primeng/api';

import {
DEFAULT_COLORS,
DotAlertConfirmService,
DotLicenseService,
DotMessageService,
Expand All @@ -35,6 +36,7 @@ describe('AppComponent', () => {
let dotMessageService: DotMessageService;
let dotLicenseService: DotLicenseService;
let dotNavLogoService: DotNavLogoService;
let consoleWarnSpy: jest.SpyInstance;

beforeEach(() => {
TestBed.configureTestingModule({
Expand All @@ -59,66 +61,224 @@ describe('AppComponent', () => {
dotLicenseService = TestBed.inject(DotLicenseService);
dotNavLogoService = TestBed.inject(DotNavLogoService);

jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(
of({
colors: {
primary: '#123',
secondary: '#456',
background: '#789'
},
releaseInfo: {
buildDate: 'Jan 1, 2022'
},
license: {
displayServerId: 'test',
isCommunity: false,
level: 200,
levelName: 'test level'
}
}) as any
);
jest.spyOn(dotUiColorsService, 'setColors');
jest.spyOn(dotMessageService, 'init');
jest.spyOn(dotLicenseService, 'setLicense');
jest.spyOn(dotNavLogoService, 'setLogo');
consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();

fixture = TestBed.createComponent(AppComponent);
de = fixture.debugElement;
});

it('should init message service', () => {
fixture.detectChanges();
expect(dotMessageService.init).toHaveBeenCalledWith({ buildDate: 'Jan 1, 2022' });
expect(dotMessageService.init).toHaveBeenCalledTimes(1);
afterEach(() => {
consoleWarnSpy.mockRestore();
});

it('should have router-outlet', () => {
fixture.detectChanges();
expect(de.query(By.css('router-outlet')) !== null).toBe(true);
});
describe('Component initialization', () => {
it('should have router-outlet', () => {
fixture.detectChanges();
expect(de.query(By.css('router-outlet')) !== null).toBe(true);
});

it('should have dot-alert-confirm component', () => {
fixture.detectChanges();
expect(de.query(By.css('dot-alert-confirm')) !== null).toBe(true);
it('should have dot-alert-confirm component', () => {
fixture.detectChanges();
expect(de.query(By.css('dot-alert-confirm')) !== null).toBe(true);
});
});

it('should set ui colors', () => {
fixture.detectChanges();
expect(dotUiColorsService.setColors).toHaveBeenCalledWith(expect.any(HTMLElement), {
primary: '#123',
secondary: '#456',
background: '#789'
describe('Configuration loading', () => {
it('should load and apply configuration successfully', () => {
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(
of({
colors: {
primary: '#123',
secondary: '#456',
background: '#789'
},
releaseInfo: {
buildDate: 'Jan 1, 2022'
},
logos: {
navBar: 'logo-url'
},
license: {
displayServerId: 'test',
isCommunity: false,
level: 200,
levelName: 'test level'
}
}) as any
);

fixture.detectChanges();

expect(dotMessageService.init).toHaveBeenCalledWith({ buildDate: 'Jan 1, 2022' });
expect(dotUiColorsService.setColors).toHaveBeenCalledWith(expect.any(HTMLElement), {
primary: '#123',
secondary: '#456',
background: '#789'
});
expect(dotNavLogoService.setLogo).toHaveBeenCalledWith('logo-url');
// Note: setLicense test is skipped due to DotLicenseService injection issue
// expect(dotLicenseService.setLicense).toHaveBeenCalledWith({...});
});

it('should handle partial configuration (missing optional fields)', () => {
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(
of({
colors: {
primary: '#123',
secondary: '#456',
background: '#789'
},
releaseInfo: null,
logos: null,
license: null
}) as any
);

fixture.detectChanges();

// Should not call init if buildDate is null
expect(dotMessageService.init).not.toHaveBeenCalled();

// Should still set colors
expect(dotUiColorsService.setColors).toHaveBeenCalledWith(expect.any(HTMLElement), {
primary: '#123',
secondary: '#456',
background: '#789'
});

// Should not call setLogo if navBar is null
expect(dotNavLogoService.setLogo).not.toHaveBeenCalled();

// Should not call setLicense if license is null
expect(dotLicenseService.setLicense).not.toHaveBeenCalled();
});

it('should use default colors when configuration fails to load', () => {
const error = new Error('Failed to load configuration');
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(throwError(() => error));

fixture.detectChanges();

// Should log warning (throwError wraps error in a function, so we check for the message)
expect(consoleWarnSpy).toHaveBeenCalledWith(
'Failed to load configuration, using defaults:',
expect.any(Function)
);

// Should use default colors
expect(dotUiColorsService.setColors).toHaveBeenCalledWith(
expect.any(HTMLElement),
DEFAULT_COLORS
);

// Should not call other services when config fails
expect(dotMessageService.init).not.toHaveBeenCalled();
expect(dotNavLogoService.setLogo).not.toHaveBeenCalled();
expect(dotLicenseService.setLicense).not.toHaveBeenCalled();
});

it('should handle configuration error gracefully (unauthenticated user)', () => {
const httpError = { status: 401, message: 'Unauthorized' };
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(
throwError(() => httpError)
);

fixture.detectChanges();

// Should log warning (throwError wraps error in a function)
expect(consoleWarnSpy).toHaveBeenCalledWith(
'Failed to load configuration, using defaults:',
expect.any(Function)
);

// Should still set default colors to ensure app works
expect(dotUiColorsService.setColors).toHaveBeenCalledWith(
expect.any(HTMLElement),
DEFAULT_COLORS
);

// App should continue functioning
expect(dotUiColorsService.setColors).toHaveBeenCalledTimes(1);
});

it('should always set colors even when configuration fails', () => {
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(
throwError(() => new Error('Network error'))
);

// Mock querySelector to return null (edge case)
const originalQuerySelector = document.querySelector;
jest.spyOn(document, 'querySelector').mockReturnValue(null);

fixture.detectChanges();

// Should not call setColors if html element doesn't exist
expect(dotUiColorsService.setColors).not.toHaveBeenCalled();

// Restore original
document.querySelector = originalQuerySelector;
});
});
it.skip('should set license', () => {
// TODO: Fix this test - DotLicenseService injection issue
fixture.detectChanges();
expect(dotLicenseService.setLicense).toHaveBeenCalled();
});

it('should set logo', () => {
fixture.detectChanges();
expect(dotNavLogoService.setLogo).toHaveBeenCalledWith(undefined);
expect(dotNavLogoService.setLogo).toHaveBeenCalledTimes(1);
describe('Service initialization', () => {
beforeEach(() => {
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(
of({
colors: {
primary: '#123',
secondary: '#456',
background: '#789'
},
releaseInfo: {
buildDate: 'Jan 1, 2022'
},
logos: {
navBar: 'logo-url'
},
license: {
displayServerId: 'test',
isCommunity: false,
level: 200,
levelName: 'test level'
}
}) as any
);
});

it('should init message service with buildDate', () => {
fixture.detectChanges();
expect(dotMessageService.init).toHaveBeenCalledWith({ buildDate: 'Jan 1, 2022' });
expect(dotMessageService.init).toHaveBeenCalledTimes(1);
});

it('should set ui colors from configuration', () => {
fixture.detectChanges();
expect(dotUiColorsService.setColors).toHaveBeenCalledWith(expect.any(HTMLElement), {
primary: '#123',
secondary: '#456',
background: '#789'
});
});

it('should set logo from configuration', () => {
fixture.detectChanges();
expect(dotNavLogoService.setLogo).toHaveBeenCalledWith('logo-url');
expect(dotNavLogoService.setLogo).toHaveBeenCalledTimes(1);
});

it.skip('should set license from configuration', () => {
// TODO: Fix this test - DotLicenseService injection issue
fixture.detectChanges();
expect(dotLicenseService.setLicense).toHaveBeenCalledWith({
displayServerId: 'test',
isCommunity: false,
level: 200,
levelName: 'test level'
});
});
});
});
52 changes: 43 additions & 9 deletions core-web/apps/dotcms-ui/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { of } from 'rxjs';

import { Component, inject, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';

import { map, take } from 'rxjs/operators';
import { catchError, map, take } from 'rxjs/operators';

import { DotLicenseService, DotMessageService, DotUiColorsService } from '@dotcms/data-access';
import {
DEFAULT_COLORS,
DotLicenseService,
DotMessageService,
DotUiColorsService
} from '@dotcms/data-access';
import { ConfigParams, DotcmsConfigService, DotUiColors } from '@dotcms/dotcms-js';
import { DotLicense } from '@dotcms/dotcms-models';

Expand Down Expand Up @@ -35,6 +42,18 @@ export class AppComponent implements OnInit {
navBar: config.logos?.navBar,
license: config.license
};
}),
// Handle errors gracefully - use default colors if config fails to load
// This ensures the app works even if user is not authenticated or endpoint fails
catchError((error) => {
console.warn('Failed to load configuration, using defaults:', error);
// Return default values that allow the app to continue functioning
return of({
buildDate: null,
colors: DEFAULT_COLORS,
navBar: null,
license: null
});
})
)
.subscribe(
Expand All @@ -44,15 +63,30 @@ export class AppComponent implements OnInit {
navBar,
license
}: {
buildDate: string;
buildDate: string | null;
colors: DotUiColors;
navBar: string;
license: DotLicense;
navBar: string | null;
license: DotLicense | null;
}) => {
this.dotMessageService.init({ buildDate });
this.dotNavLogoService.setLogo(navBar);
this.dotUiColors.setColors(document.querySelector('html'), colors);
this.dotLicense.setLicense(license);
// Initialize services with loaded or default values
if (buildDate) {
this.dotMessageService.init({ buildDate });
}

if (navBar) {
this.dotNavLogoService.setLogo(navBar);
}

// Always set colors (will use defaults if config failed)
// This ensures PrimeNG theme is always initialized
const htmlElement = document.querySelector('html') as HTMLElement;
if (htmlElement) {
this.dotUiColors.setColors(htmlElement, colors);
}

if (license) {
this.dotLicense.setLicense(license);
}
}
);
}
Expand Down
Loading