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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@
"typescript": "5.9.3",
"zone.js": "^0.15.1"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { kcpRootOrgsPath } from '../models/constants';
import { PortalNodeContext } from '../models/luigi-context';
import { PortalLuigiNode } from '../models/luigi-node';
import { inject } from '@angular/core';
import { CustomGlobalNodesService, I18nService } from '@openmfp/portal-ui-lib';
import {
CustomGlobalNodesService,
EntityType,
I18nService,
NodeContext,
} from '@openmfp/portal-ui-lib';

export class CustomGlobalNodesServiceImpl implements CustomGlobalNodesService {
private i18nService = inject(I18nService);
Expand All @@ -25,6 +30,28 @@ export class CustomGlobalNodesServiceImpl implements CustomGlobalNodesService {
kcpPath: kcpRootOrgsPath,
} as PortalNodeContext,
},
{
pathSegment: 'error',
order: '1000',
hideFromNav: true,
context: {} as PortalNodeContext,
children: [
{
pathSegment: ':id',
entityType: EntityType.ENTITY_ERROR,
hideFromNav: true,
hideSideNav: true,
viewUrl: '/assets/platform-mesh-portal-ui-wc.js#error-component',
context: {
id: ':id',
translationTable: this.i18nService.translationTable,
} as any as NodeContext,
webcomponent: {
selfRegistered: true,
},
},
],
},
{
pathSegment: 'users',
showBreadcrumbs: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('CustomRoutingConfigServiceImpl', () => {
const result = await config.pageNotFoundHandler();

expect(result).toEqual({
redirectTo: 'welcome',
redirectTo: 'error/404',
keepURL: true,
});
});
Expand All @@ -111,7 +111,7 @@ describe('CustomRoutingConfigServiceImpl', () => {
const result = await config.pageNotFoundHandler();

expect(result).toEqual({
redirectTo: 'error/404',
redirectTo: 'welcome',
keepURL: true,
});
});
Expand Down Expand Up @@ -171,7 +171,7 @@ describe('CustomRoutingConfigServiceImpl', () => {
const result = await config.pageNotFoundHandler();

expect(result).toEqual({
redirectTo: 'error/404',
redirectTo: 'welcome',
keepURL: true,
});
});
Expand All @@ -190,7 +190,7 @@ describe('CustomRoutingConfigServiceImpl', () => {
const result = await config.pageNotFoundHandler();

expect(result).toEqual({
redirectTo: 'welcome',
redirectTo: 'error/404',
keepURL: true,
});
});
Expand All @@ -215,7 +215,7 @@ describe('CustomRoutingConfigServiceImpl', () => {
const result = await config.pageNotFoundHandler();

expect(result).toEqual({
redirectTo: 'error/404',
redirectTo: 'welcome',
keepURL: true,
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ export class CustomRoutingConfigServiceImpl implements RoutingConfigService {

getRoutingConfig(): any {
return {
pageNotFoundHandler: async () => {
pageNotFoundHandler: () => {
if (!this.envConfig?.baseDomain) {
return this.redirectTo('error/404');
}

if (window.location.hostname !== this.envConfig.baseDomain) {
if (window.location.hostname === this.envConfig.baseDomain) {
return this.redirectTo('welcome');
}

Expand Down
24 changes: 23 additions & 1 deletion projects/wc/_mocks_/ui5-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,26 @@ jest.mock('@ui5/webcomponents-icons/dist/hide.js', () => ({}), {
});
jest.mock('@ui5/webcomponents-icons/dist/show.js', () => ({}), {
virtual: true,
});
});

jest.mock(
'@ui5/webcomponents-fiori/dist/illustrations/NoEntries.js',
() => ({}),
{
virtual: true,
},
);
jest.mock(
'@ui5/webcomponents-fiori/dist/illustrations/UnableToLoad.js',
() => ({}),
{
virtual: true,
},
);
jest.mock(
'@ui5/webcomponents-fiori/dist/illustrations/tnt/UnsuccessfulAuth.js',
() => ({}),
{
virtual: true,
},
);
23 changes: 23 additions & 0 deletions projects/wc/src/app/components/error/error.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@let configVM = config();
@if (configVM.scene) {
<div class="page">
<div class="container">
<ui5-illustrated-message [name]="configVM.scene">
<ui5-title slot="title" level="H1">{{configVM.illustratedMessageTitle}}</ui5-title>
<div slot="subtitle">
<p>{{configVM.illustratedMessageText}}</p>
</div>
<div>
@for (button of configVM.buttons; track button.url) {
<ui5-button
design="Transparent"
(click)="goTo(button)"
>
{{ button.label }}
</ui5-button>
}
</div>
</ui5-illustrated-message>
</div>
</div>
}
119 changes: 119 additions & 0 deletions projects/wc/src/app/components/error/error.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { ErrorComponent } from './error.component';
import { ButtonConfig } from './models/error.model';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { I18nService, LuigiCoreService } from '@openmfp/portal-ui-lib';
import {
ButtonComponent,
IllustratedMessageComponent,
TitleComponent,
} from '@ui5/webcomponents-ngx';

describe('ErrorComponent', () => {
let component: ErrorComponent;
let fixture: ComponentFixture<ErrorComponent>;
let i18nServiceMock: jest.Mocked<I18nService>;
let luigiCoreServiceMock: jest.Mocked<LuigiCoreService>;

beforeEach(async () => {
i18nServiceMock = {
getTranslationAsync: jest.fn().mockResolvedValue('translated text'),
translationTable: {},
} as any;

luigiCoreServiceMock = {
navigation: jest.fn().mockReturnValue({
navigate: jest.fn(),
}),
showAlert: jest.fn(),
} as any;

await TestBed.configureTestingModule({
imports: [
ErrorComponent,
IllustratedMessageComponent,
ButtonComponent,
TitleComponent,
],
providers: [
{ provide: I18nService, useValue: i18nServiceMock },
{ provide: LuigiCoreService, useValue: luigiCoreServiceMock },
],
}).compileComponents();

fixture = TestBed.createComponent(ErrorComponent);
component = fixture.componentInstance;
});

it('should create', () => {
expect(component).toBeTruthy();
});

describe('goTo', () => {
it('should open URL in new tab', () => {
const windowSpy = jest.spyOn(window, 'open').mockImplementation();
const button: ButtonConfig = { url: 'https://test.com' };

component.goTo(button);
expect(windowSpy).toHaveBeenCalledWith('https://test.com', '_blank');
});

it('should navigate using LuigiCore when route is provided', () => {
const button: ButtonConfig = { route: { context: 'test-route' } };
const navigateSpy = jest.fn();
jest
.spyOn(luigiCoreServiceMock, 'navigation')
.mockReturnValue({ navigate: navigateSpy });

component.goTo(button);
expect(navigateSpy).toHaveBeenCalledWith('/test-route');
});
});

describe('setSceneConfig', () => {
it('should set 404 config', async () => {
const testContext = {
id: '404',
translationTable: {},
};

fixture.componentRef.setInput('context', testContext);

await component.ngOnInit();
expect(component.config().scene).toBe('NoEntries');
expect(component.config().illustratedMessageTitle).toBe('translated text');
expect(component.config().illustratedMessageText).toBe('translated text');
expect(component.config().buttons).toBeDefined();
});

it('should set 403 config', async () => {
const testContext = {
id: '403',
translationTable: {},
};

fixture.componentRef.setInput('context', testContext);

await component.ngOnInit();
expect(component.config().scene).toBe('tnt/UnsuccessfulAuth');
expect(component.config().illustratedMessageTitle).toBe('');
expect(component.config().illustratedMessageText).toBe('translated text');
expect(component.config().buttons).toBeDefined();
expect(component.config().buttons?.length).toBe(2);
});

it('should set default error config for unknown error code', async () => {
const testContext = {
id: '500',
translationTable: {},
};

fixture.componentRef.setInput('context', testContext);

await component.ngOnInit();
expect(component.config().scene).toBe('UnableToLoad');
expect(component.config().illustratedMessageTitle).toBe('translated text');
expect(component.config().illustratedMessageText).toBe('');
expect(component.config().buttons).toBeDefined();
});
});
});
Loading