2018-02-28 12:45:11 -05:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2018-06-18 05:29:29 -04:00
|
|
|
import {Component, DoBootstrap, EventEmitter, Injector, Input, NgModule, Output, destroyPlatform} from '@angular/core';
|
2018-02-28 12:45:11 -05:00
|
|
|
import {BrowserModule} from '@angular/platform-browser';
|
|
|
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
2018-07-12 18:58:13 -04:00
|
|
|
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
|
2018-02-27 17:06:06 -05:00
|
|
|
import {Subject} from 'rxjs';
|
2018-02-28 12:45:11 -05:00
|
|
|
|
2018-03-14 16:24:27 -04:00
|
|
|
import {NgElementConstructor, createCustomElement} from '../src/create-custom-element';
|
2018-03-14 18:58:12 -04:00
|
|
|
import {NgElementStrategy, NgElementStrategyEvent, NgElementStrategyFactory} from '../src/element-strategy';
|
2018-02-28 12:45:11 -05:00
|
|
|
|
|
|
|
type WithFooBar = {
|
|
|
|
fooFoo: string,
|
|
|
|
barBar: string
|
|
|
|
};
|
|
|
|
|
2018-07-12 18:58:13 -04:00
|
|
|
if (browserDetection.supportsCustomElements) {
|
2018-03-14 16:24:27 -04:00
|
|
|
describe('createCustomElement', () => {
|
2018-02-28 12:45:11 -05:00
|
|
|
let NgElementCtor: NgElementConstructor<WithFooBar>;
|
|
|
|
let strategy: TestStrategy;
|
|
|
|
let strategyFactory: TestStrategyFactory;
|
2018-03-02 01:34:21 -05:00
|
|
|
let injector: Injector;
|
2018-02-28 12:45:11 -05:00
|
|
|
|
|
|
|
beforeAll(done => {
|
|
|
|
destroyPlatform();
|
|
|
|
platformBrowserDynamic()
|
|
|
|
.bootstrapModule(TestModule)
|
|
|
|
.then(ref => {
|
2018-03-02 01:34:21 -05:00
|
|
|
injector = ref.injector;
|
2018-02-28 12:45:11 -05:00
|
|
|
strategyFactory = new TestStrategyFactory();
|
|
|
|
strategy = strategyFactory.testStrategy;
|
|
|
|
|
2018-03-14 16:24:27 -04:00
|
|
|
NgElementCtor = createCustomElement(TestComponent, {injector, strategyFactory});
|
2018-02-28 12:45:11 -05:00
|
|
|
|
|
|
|
// The `@webcomponents/custom-elements/src/native-shim.js` polyfill allows us to create
|
|
|
|
// new instances of the NgElement which extends HTMLElement, as long as we define it.
|
|
|
|
customElements.define('test-element', NgElementCtor);
|
|
|
|
})
|
|
|
|
.then(done, done.fail);
|
|
|
|
});
|
|
|
|
|
|
|
|
afterAll(() => destroyPlatform());
|
|
|
|
|
|
|
|
it('should use a default strategy for converting component inputs', () => {
|
|
|
|
expect(NgElementCtor.observedAttributes).toEqual(['foo-foo', 'barbar']);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should send input values from attributes when connected', () => {
|
2018-03-06 17:02:25 -05:00
|
|
|
const element = new NgElementCtor(injector);
|
2018-02-28 12:45:11 -05:00
|
|
|
element.setAttribute('foo-foo', 'value-foo-foo');
|
|
|
|
element.setAttribute('barbar', 'value-barbar');
|
|
|
|
element.connectedCallback();
|
|
|
|
expect(strategy.connectedElement).toBe(element);
|
|
|
|
|
2018-03-02 13:08:16 -05:00
|
|
|
expect(strategy.getInputValue('fooFoo')).toBe('value-foo-foo');
|
|
|
|
expect(strategy.getInputValue('barBar')).toBe('value-barbar');
|
2018-02-28 12:45:11 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should listen to output events after connected', () => {
|
2018-03-06 17:02:25 -05:00
|
|
|
const element = new NgElementCtor(injector);
|
2018-02-28 12:45:11 -05:00
|
|
|
element.connectedCallback();
|
|
|
|
|
|
|
|
let eventValue: any = null;
|
|
|
|
element.addEventListener('some-event', (e: CustomEvent) => eventValue = e.detail);
|
|
|
|
strategy.events.next({name: 'some-event', value: 'event-value'});
|
|
|
|
|
|
|
|
expect(eventValue).toEqual('event-value');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not listen to output events after disconnected', () => {
|
2018-03-06 17:02:25 -05:00
|
|
|
const element = new NgElementCtor(injector);
|
2018-02-28 12:45:11 -05:00
|
|
|
element.connectedCallback();
|
|
|
|
element.disconnectedCallback();
|
|
|
|
expect(strategy.disconnectCalled).toBe(true);
|
|
|
|
|
|
|
|
let eventValue: any = null;
|
|
|
|
element.addEventListener('some-event', (e: CustomEvent) => eventValue = e.detail);
|
|
|
|
strategy.events.next({name: 'some-event', value: 'event-value'});
|
|
|
|
|
|
|
|
expect(eventValue).toEqual(null);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should properly set getters/setters on the element', () => {
|
2018-03-06 17:02:25 -05:00
|
|
|
const element = new NgElementCtor(injector);
|
2018-02-28 12:45:11 -05:00
|
|
|
element.fooFoo = 'foo-foo-value';
|
|
|
|
element.barBar = 'barBar-value';
|
|
|
|
|
|
|
|
expect(strategy.inputs.get('fooFoo')).toBe('foo-foo-value');
|
|
|
|
expect(strategy.inputs.get('barBar')).toBe('barBar-value');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helpers
|
|
|
|
@Component({
|
|
|
|
selector: 'test-component',
|
|
|
|
template: 'TestComponent|foo({{ fooFoo }})|bar({{ barBar }})',
|
|
|
|
})
|
|
|
|
class TestComponent {
|
|
|
|
@Input() fooFoo: string = 'foo';
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
@Input('barbar') barBar !: string;
|
2018-02-28 12:45:11 -05:00
|
|
|
|
|
|
|
@Output() bazBaz = new EventEmitter<boolean>();
|
|
|
|
@Output('quxqux') quxQux = new EventEmitter<Object>();
|
|
|
|
}
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [TestComponent],
|
|
|
|
entryComponents: [TestComponent],
|
|
|
|
})
|
2018-06-18 05:29:29 -04:00
|
|
|
class TestModule implements DoBootstrap {
|
2018-02-28 12:45:11 -05:00
|
|
|
ngDoBootstrap() {}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class TestStrategy implements NgElementStrategy {
|
|
|
|
connectedElement: HTMLElement|null = null;
|
|
|
|
disconnectCalled = false;
|
|
|
|
inputs = new Map<string, any>();
|
|
|
|
|
|
|
|
events = new Subject<NgElementStrategyEvent>();
|
|
|
|
|
|
|
|
connect(element: HTMLElement): void { this.connectedElement = element; }
|
|
|
|
|
|
|
|
disconnect(): void { this.disconnectCalled = true; }
|
|
|
|
|
2018-03-02 13:08:16 -05:00
|
|
|
getInputValue(propName: string): any { return this.inputs.get(propName); }
|
2018-02-28 12:45:11 -05:00
|
|
|
|
2018-03-02 13:08:16 -05:00
|
|
|
setInputValue(propName: string, value: string): void { this.inputs.set(propName, value); }
|
2018-02-28 12:45:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
export class TestStrategyFactory implements NgElementStrategyFactory {
|
|
|
|
testStrategy = new TestStrategy();
|
|
|
|
|
|
|
|
create(): NgElementStrategy { return this.testStrategy; }
|
|
|
|
}
|