Skip to content

Commit

Permalink
feat(directive): Add support for bound inputs and outputs
Browse files Browse the repository at this point in the history
Also fix a bug when inputs/outputs where comletely reassigned and changes were not picked up

#102
  • Loading branch information
gund committed Mar 23, 2018
1 parent edf1077 commit a962612
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 20 deletions.
131 changes: 131 additions & 0 deletions src/dynamic/dynamic.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import {
InjectedComponent,
MockedInjectedComponent,
TestComponent,
InjectedBoundComponent,
TestModule,
} from '../test/index';
import { COMPONENT_INJECTOR } from './component-injector';
import { DynamicDirective } from './dynamic.directive';

const getComponentInjectorFrom = getByPredicate<ComponentInjectorComponent>(By.directive(ComponentInjectorComponent));
const getInjectedComponentFrom = getByPredicate<InjectedComponent>(By.directive(InjectedComponent));
const getInjectedBoundComponentFrom = getByPredicate<InjectedBoundComponent>(By.directive(InjectedBoundComponent));

describe('Directive: Dynamic', () => {
beforeEach(() => {
Expand Down Expand Up @@ -50,6 +52,23 @@ describe('Directive: Dynamic', () => {
expect(injectedComp['prop2']).toBe(2);
});

it('should be reassigned when replaced', () => {
fixture.detectChanges();
fixture.componentInstance['inputs'] = { otherProp: 'set' };
fixture.detectChanges();

expect(injectedComp['otherProp']).toBe('set');
});

it('should be reassigned from `null|undefined` when replaced', () => {
fixture.componentInstance['inputs'] = null;
fixture.detectChanges();
fixture.componentInstance['inputs'] = { otherProp: 'set' };
fixture.detectChanges();

expect(injectedComp['otherProp']).toBe('set');
});

it('should trigger initially `OnChanges` life-cycle hook', () => {
injectedComp.ngOnChanges.and.callFake((changes: SimpleChanges) => {
expect(changes.prop1).toBeDefined();
Expand Down Expand Up @@ -175,6 +194,66 @@ describe('Directive: Dynamic', () => {
});
});

describe('bound inputs', () => {
let fixture: ComponentFixture<TestComponent>;
let testComp: any;
let injectedComp: InjectedBoundComponent;
let onChangesMock: jest.Mock;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TestModule],
declarations: [DynamicDirective, TestComponent],
});

const template = `<ng-container [ngComponentOutlet]="comp" [ndcDynamicInputs]="inputs"></ng-container>`;
TestBed
.overrideComponent(TestComponent, { set: { template } })
.compileComponents();
fixture = TestBed.createComponent(TestComponent);

testComp = fixture.componentInstance;
testComp.comp = InjectedBoundComponent;
testComp.inputs = null;

fixture.detectChanges();
injectedComp = getInjectedBoundComponentFrom(fixture).component;
injectedComp['ngOnChanges'] = onChangesMock = jest.fn();
}));

it('should correctly be passed to dynamic component', () => {
testComp.inputs = { outerProp: '123' };
fixture.detectChanges();

expect(injectedComp.innerProp).toBe('123');
});

it('should trigger `OnChanges` life-cycle hook with correct names', () => {
onChangesMock.mockImplementation((changes: SimpleChanges) => {
expect(changes.innerProp).toBeDefined();
expect(changes.innerProp.currentValue).toBe('123');
expect(changes.innerProp.isFirstChange()).toBeTruthy();
});

testComp.inputs = { outerProp: '123' };
fixture.detectChanges();

expect(onChangesMock).toHaveBeenCalledTimes(1);

onChangesMock.mockImplementation((changes: SimpleChanges) => {
expect(changes.innerProp).toBeDefined();
expect(changes.innerProp.currentValue).toBe('456');
expect(changes.innerProp.previousValue).toBe('123');
expect(changes.innerProp.isFirstChange()).toBeFalsy();
});

testComp.inputs = { outerProp: '456' };
fixture.detectChanges();

expect(onChangesMock).toHaveBeenCalledTimes(2);
});
});

describe('outputs', () => {
let fixture: ComponentFixture<TestComponent>
, injectorComp: ComponentInjectorComponent
Expand All @@ -201,6 +280,18 @@ describe('Directive: Dynamic', () => {
expect(outputSpy).toHaveBeenCalledWith('data');
}));

it('should re-bind outputs after `null|undefiined` to component and receive events', async(() => {
fixture.componentInstance['outputs'] = null;
fixture.detectChanges();
fixture.componentInstance['outputs'] = { onEvent: outputSpy };
fixture.detectChanges();

injectedComp.onEvent.next('data');

expect(outputSpy).toHaveBeenCalledTimes(1);
expect(outputSpy).toHaveBeenCalledWith('data');
}));

it('should NOT bind outputs to component when outputs undefined', async(() => {
fixture.componentInstance['outputs'] = undefined;

Expand Down Expand Up @@ -306,4 +397,44 @@ describe('Directive: Dynamic', () => {
expect(outputSpy).toHaveBeenCalledWith('data');
});
});

describe('bound outputs', () => {
let fixture: ComponentFixture<TestComponent>;
let testComp: any;
let injectedComp: InjectedBoundComponent;
let outputHandler: jest.Mock;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TestModule],
declarations: [DynamicDirective, TestComponent],
});

const template = `<ng-container [ngComponentOutlet]="comp" [ndcDynamicOutputs]="outputs"></ng-container>`;
TestBed
.overrideComponent(TestComponent, { set: { template } })
.compileComponents();
fixture = TestBed.createComponent(TestComponent);

testComp = fixture.componentInstance;
testComp.comp = InjectedBoundComponent;
testComp.outputs = null;

fixture.detectChanges();
injectedComp = getInjectedBoundComponentFrom(fixture).component;

outputHandler = jest.fn();
}));

it('should correctly be passed to dynamic component', () => {
testComp.outputs = { outerEvt: outputHandler };
fixture.detectChanges();

injectedComp.innerEvt.emit('data');

expect(outputHandler).toHaveBeenCalledTimes(1);
expect(outputHandler).toHaveBeenCalledWith('data');
});

});
});
Loading

0 comments on commit a962612

Please sign in to comment.