Skip to content
55 changes: 44 additions & 11 deletions packages/dockview-angular/src/__tests__/angular-renderer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EnvironmentInjector,
inject,
Injector,
TemplateRef,
ViewChild,
} from '@angular/core';
import { AngularRenderer } from '../lib/utils/angular-renderer';

Expand Down Expand Up @@ -36,18 +37,33 @@ class TestUpdateComponent {
}
}

@Component({
selector: 'test-template-holder-component',
template: `
<ng-template #template>
<test-update-component />
</ng-template>
`,
})
class TemplateHolderComponent {
@ViewChild('template', { static: true })
public template?: TemplateRef<any>;
}

describe('AngularRenderer', () => {
let injector: Injector;
let environmentInjector: EnvironmentInjector;
let application: ApplicationRef;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TestComponent],
declarations: [
TestComponent,
TestUpdateComponent,
TemplateHolderComponent,
],
}).compileComponents();

injector = TestBed.inject(Injector);
environmentInjector = TestBed.inject(EnvironmentInjector);
application = TestBed.inject(ApplicationRef);
});

Expand All @@ -63,7 +79,6 @@ describe('AngularRenderer', () => {
const renderer = new AngularRenderer({
component: TestComponent,
injector,
environmentInjector,
});

renderer.init({ title: 'Updated Title', value: 'test-value' });
Expand All @@ -80,7 +95,6 @@ describe('AngularRenderer', () => {
const renderer = new AngularRenderer({
component: TestComponent,
injector,
environmentInjector,
});

renderer.init({ title: 'Initial Title' });
Expand All @@ -97,7 +111,6 @@ describe('AngularRenderer', () => {
const renderer = new AngularRenderer({
component: TestComponent,
injector,
environmentInjector,
});

renderer.init({ title: 'Test Title' });
Expand All @@ -118,7 +131,6 @@ describe('AngularRenderer', () => {
const renderer = new AngularRenderer({
component: null as any,
injector,
environmentInjector,
});

expect(() => {
Expand All @@ -130,7 +142,6 @@ describe('AngularRenderer', () => {
const renderer = new AngularRenderer({
component: TestComponent,
injector,
environmentInjector,
});

renderer.init({ title: 'Test Title' });
Expand All @@ -145,7 +156,6 @@ describe('AngularRenderer', () => {
const renderer = new AngularRenderer({
component: TestComponent,
injector,
environmentInjector,
});

renderer.init({ title: 'Test Title' });
Expand All @@ -161,7 +171,6 @@ describe('AngularRenderer', () => {
const renderer = new AngularRenderer({
component: TestUpdateComponent,
injector,
environmentInjector,
});

renderer.init({});
Expand All @@ -172,4 +181,28 @@ describe('AngularRenderer', () => {
application.tick();
expect(renderer.element.innerHTML).toContain('Counter: 1');
});

it('should render view from template', () => {
// Create component with template
const templateRenderer = new AngularRenderer({
component: TemplateHolderComponent,
injector,
});
templateRenderer.init({});
const template = (
templateRenderer.component.instance as TemplateHolderComponent
).template;

expect(template).toBeDefined();

// Create view from template
const renderer = new AngularRenderer({
component: template,
injector: templateRenderer.component.injector, // use container injector to ensure we have a view
});
renderer.init({});
application.tick();

expect(renderer.element.innerHTML).toContain('Counter: 0');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing';
import { Component, Injector, EnvironmentInjector } from '@angular/core';
import { AngularFrameworkComponentFactory } from '../lib/utils/component-factory';
import { CreateComponentOptions } from 'dockview-core';
import { ComponentRegistryService } from '../lib/utils/component-registry.service';

@Component({
selector: 'test-dockview-component',
Expand Down Expand Up @@ -53,6 +54,7 @@ describe('AngularFrameworkComponentFactory', () => {
let injector: Injector;
let environmentInjector: EnvironmentInjector;
let factory: AngularFrameworkComponentFactory;
let resolver: ComponentRegistryService;

const components = {
'dockview-test': TestDockviewComponent,
Expand Down Expand Up @@ -84,9 +86,11 @@ describe('AngularFrameworkComponentFactory', () => {

injector = TestBed.inject(Injector);
environmentInjector = TestBed.inject(EnvironmentInjector);
resolver = TestBed.inject(ComponentRegistryService);
resolver.registerComponents(components);

factory = new AngularFrameworkComponentFactory(
components,
resolver,
injector,
environmentInjector,
tabComponents,
Expand Down Expand Up @@ -237,7 +241,7 @@ describe('AngularFrameworkComponentFactory', () => {

it('should return undefined when no component and no default', () => {
const factoryWithoutDefault = new AngularFrameworkComponentFactory(
components,
resolver,
injector,
environmentInjector,
{}
Expand Down Expand Up @@ -266,7 +270,7 @@ describe('AngularFrameworkComponentFactory', () => {
it('should throw error when no watermark component provided', () => {
const factoryWithoutWatermark =
new AngularFrameworkComponentFactory(
components,
resolver,
injector,
environmentInjector
);
Expand Down Expand Up @@ -299,7 +303,7 @@ describe('AngularFrameworkComponentFactory', () => {
it('should return undefined when no header actions components provided', () => {
const factoryWithoutHeaderActions =
new AngularFrameworkComponentFactory(
components,
resolver,
injector,
environmentInjector
);
Expand Down
121 changes: 121 additions & 0 deletions packages/dockview-angular/src/__tests__/component-registry.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { TestBed } from '@angular/core/testing';
import { Type } from '@angular/core';
import {
ComponentRegistryService,
ComponentResolver,
} from '../lib/utils/component-registry.service';

describe('ComponentRegistryService', () => {
let service: ComponentRegistryService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [ComponentRegistryService],
});
service = TestBed.inject(ComponentRegistryService);
});

describe('registerComponent', () => {
it('should register a component with a valid name and reference', () => {
const mockComponent = {} as Type<unknown>;
service.registerComponent('testComponent', mockComponent);

expect(service.resolveComponent('testComponent')).toBe(
mockComponent
);
});

it('should throw an error if component name or reference is not provided', () => {
expect(() =>
service.registerComponent('', {} as Type<unknown>)
).toThrow('Component and reference must be provided');
});
});

describe('registerComponents', () => {
it('should register multiple components from a record', () => {
const components = {
componentA: {} as Type<unknown>,
componentB: {} as Type<unknown>,
};

service.registerComponents(components);

expect(service.resolveComponent('componentA')).toBe(
components.componentA
);
expect(service.resolveComponent('componentB')).toBe(
components.componentB
);
});
});

describe('resolveComponent', () => {
it('should return a registered component reference', () => {
const mockComponent = {} as Type<unknown>;
service.registerComponent('testComponent', mockComponent);

const resolved = service.resolveComponent('testComponent');
expect(resolved).toBe(mockComponent);
});

it('should throw an error if component name is not provided', () => {
expect(() => service.resolveComponent('')).toThrow(
'Component must be provided'
);
});

it('should resolve a component dynamically through a resolver', () => {
const dynamicComponent = {} as Type<unknown>;
const resolver: ComponentResolver = (component) =>
component === 'dynamicComponent' ? dynamicComponent : undefined;

service.registerResolver(resolver);

expect(service.resolveComponent('dynamicComponent')).toBe(
dynamicComponent
);
});

it('should fallback to static registration if no resolver matches', () => {
const staticComponent = {} as Type<unknown>;
service.registerComponent('staticComponent', staticComponent);

const resolver: ComponentResolver = () => undefined;
service.registerResolver(resolver);

expect(service.resolveComponent('staticComponent')).toBe(
staticComponent
);
});
});

describe('registerResolver', () => {
it('should register a new resolver for dynamic component resolution', () => {
const dynamicComponent = {} as Type<unknown>;
const resolver: ComponentResolver = (component) =>
component === 'dynamicComponent' ? dynamicComponent : undefined;

service.registerResolver(resolver);

expect(service.resolveComponent('dynamicComponent')).toBe(
dynamicComponent
);
});
});

describe('unregisterResolver', () => {
it('should unregister a resolver', () => {
const dynamicComponent = {} as Type<unknown>;
const resolver: ComponentResolver = (component) =>
component === 'dynamicComponent' ? dynamicComponent : undefined;

service.registerResolver(resolver);
service.unregisterResolver(resolver);

expect(
service.resolveComponent('dynamicComponent')
).toBeUndefined();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ describe('DockviewAngularComponent', () => {
expect(component).toBeTruthy();
});

it('should throw error if components input is not provided', () => {
component.components = undefined as any;

expect(() => {
component.ngOnInit();
}).toThrow('DockviewAngularComponent: components input is required');
});

it('should initialize dockview api on ngOnInit', () => {
component.ngOnInit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ describe('GridviewAngularComponent', () => {
expect(component).toBeTruthy();
});

it('should throw error if components input is not provided', () => {
component.components = undefined as any;

expect(() => {
component.ngOnInit();
}).toThrow('GridviewAngularComponent: components input is required');
});

it('should initialize gridview api on ngOnInit', () => {
component.ngOnInit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ describe('PaneviewAngularComponent', () => {
expect(component).toBeTruthy();
});

it('should throw error if components input is not provided', () => {
component.components = undefined as any;

expect(() => {
component.ngOnInit();
}).toThrow('PaneviewAngularComponent: components input is required');
});

it('should initialize paneview api on ngOnInit', () => {
component.ngOnInit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ describe('SplitviewAngularComponent', () => {
expect(component).toBeTruthy();
});

it('should throw error if components input is not provided', () => {
component.components = undefined as any;

expect(() => {
component.ngOnInit();
}).toThrow('SplitviewAngularComponent: components input is required');
});

it('should initialize splitview api on ngOnInit', () => {
component.ngOnInit();

Expand Down
Loading
Loading