Add Sony to domain affiliations on team page
[WebKit-https.git] / Websites / perf.webkit.org / browser-tests / component-base-tests.js
1
2 describe('ComponentBase', function() {
3
4     function createTestToCheckExistenceOfShadowTree(callback, options = {htmlTemplate: false, cssTemplate: true})
5     {
6         const context = new BrowsingContext();
7         return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
8             class SomeComponent extends ComponentBase { }
9             if (options.htmlTemplate)
10                 SomeComponent.htmlTemplate = () => { return '<div id="div" style="height: 10px;"></div>'; };
11             if (options.cssTemplate)
12                 SomeComponent.cssTemplate = () => { return ':host { height: 10px; }'; };
13
14             let instance = new SomeComponent('some-component');
15             instance.element().style.display = 'block';
16             context.document.body.appendChild(instance.element());
17             return callback(instance, () => { return instance.element().offsetHeight == 10; });
18         });
19     }
20
21     it('must enqueue a connected component to render', () => {
22         const context = new BrowsingContext();
23         return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
24             let renderCall = 0;
25             class SomeComponent extends ComponentBase {
26                 render() { renderCall++; }
27             }
28             ComponentBase.defineElement('some-component', SomeComponent);
29
30             let requestAnimationFrameCount = 0;
31             let callback = null;
32             context.global.requestAnimationFrame = (newCallback) => {
33                 callback = newCallback;
34                 requestAnimationFrameCount++;
35             }
36
37             expect(requestAnimationFrameCount).to.be(0);
38             const instance = new SomeComponent;
39             context.document.body.appendChild(instance.element());
40             expect(requestAnimationFrameCount).to.be(1);
41             callback();
42             expect(renderCall).to.be(1);
43             expect(requestAnimationFrameCount).to.be(1);
44         });
45     });
46
47     it('must enqueue a connected component to render upon a resize event if enqueueToRenderOnResize is true', () => {
48         const context = new BrowsingContext();
49         return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
50             class SomeComponent extends ComponentBase {
51                 static get enqueueToRenderOnResize() { return true; }
52             }
53             ComponentBase.defineElement('some-component', SomeComponent);
54
55             let requestAnimationFrameCount = 0;
56             let callback = null;
57             context.global.requestAnimationFrame = (newCallback) => {
58                 callback = newCallback;
59                 requestAnimationFrameCount++;
60             }
61
62             expect(requestAnimationFrameCount).to.be(0);
63             const instance = new SomeComponent;
64             context.global.dispatchEvent(new Event('resize'));
65             context.document.body.appendChild(instance.element());
66             context.global.dispatchEvent(new Event('resize'));
67             expect(requestAnimationFrameCount).to.be(1);
68         });
69     });
70
71     it('must not enqueue a disconnected component to render upon a resize event if enqueueToRenderOnResize is true', () => {
72         const context = new BrowsingContext();
73         return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
74             class SomeComponent extends ComponentBase {
75                 static get enqueueToRenderOnResize() { return true; }
76             }
77             ComponentBase.defineElement('some-component', SomeComponent);
78
79             let requestAnimationFrameCount = 0;
80             let callback = null;
81             context.global.requestAnimationFrame = (newCallback) => {
82                 callback = newCallback;
83                 requestAnimationFrameCount++;
84             }
85
86             const instance = new SomeComponent;
87             expect(requestAnimationFrameCount).to.be(0);
88             context.global.dispatchEvent(new Event('resize'));
89             expect(requestAnimationFrameCount).to.be(0);
90         });
91     });
92
93     describe('constructor', () => {
94         it('is a function', () => {
95             return new BrowsingContext().importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
96                 expect(ComponentBase).to.be.a('function');
97             });
98         });
99
100         it('can be instantiated', () => {
101             return new BrowsingContext().importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
102                 let callCount = 0;
103                 class SomeComponent extends ComponentBase {
104                     constructor() {
105                         super('some-component');
106                         callCount++;
107                     }
108                 }
109                 let instance = new SomeComponent;
110                 expect(instance).to.be.a(ComponentBase);
111                 expect(instance).to.be.a(SomeComponent);
112                 expect(callCount).to.be(1);
113             });
114         });
115
116         it('must not create shadow tree eagerly', () => {
117             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
118                 expect(hasShadowTree()).to.be(false);
119             });
120         });
121     });
122
123     describe('element()', () => {
124         it('must return an element', () => {
125             const context = new BrowsingContext();
126             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
127                 class SomeComponent extends ComponentBase { }
128                 let instance = new SomeComponent('some-component');
129                 expect(instance.element()).to.be.a(context.global.HTMLElement);
130             });
131         });
132
133         it('must return an element whose component() matches the component', () => {
134             return new BrowsingContext().importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
135                 class SomeComponent extends ComponentBase { }
136                 let instance = new SomeComponent('some-component');
137                 expect(instance.element().component()).to.be(instance);
138             });
139         });
140
141         it('must not create shadow tree eagerly', () => {
142             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
143                 instance.element();
144                 expect(hasShadowTree()).to.be(false);
145             });
146         });
147     });
148
149     describe('content()', () => {
150         it('must create shadow tree', () => {
151             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
152                 instance.content();
153                 expect(hasShadowTree()).to.be(true);
154             });
155         });
156
157         it('must return the same shadow tree each time it is called', () => {
158             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
159                 expect(instance.content()).to.be(instance.content());
160             });
161         });
162
163         it('must return the element matching the id if an id is specified', () => {
164             return new BrowsingContext().importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
165                 class SomeComponent extends ComponentBase {
166                     static htmlTemplate() { return '<div id="part1" title="foo"></div><div id="part1"></div>'; }
167                 }
168                 ComponentBase.defineElement('some-component', SomeComponent);
169
170                 const instance = new SomeComponent;
171                 const part1 = instance.content('part1');
172                 expect(part1.localName).to.be('div');
173                 expect(part1.title).to.be('foo');
174                 expect(instance.content('part2')).to.be(null);
175             });
176         });
177     });
178
179     describe('part()', () => {
180         it('must create shadow tree', () => {
181             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
182                 instance.part('foo');
183                 expect(hasShadowTree()).to.be(true);
184             });
185         });
186
187         it('must return the component matching the id if an id is specified', () => {
188             return new BrowsingContext().importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
189                 class SomeComponent extends ComponentBase { }
190                 ComponentBase.defineElement('some-component', SomeComponent);
191
192                 class OtherComponent extends ComponentBase {
193                     static htmlTemplate() { return '<some-component id="foo"></some-component>'; }
194                 }
195                 ComponentBase.defineElement('other-component', OtherComponent);
196
197                 const otherComponent = new OtherComponent;
198                 const someComponent = otherComponent.part('foo');
199                 expect(someComponent).to.be.a(SomeComponent);
200                 expect(someComponent.element().id).to.be('foo');
201                 expect(otherComponent.part('foo')).to.be(someComponent);
202                 expect(otherComponent.part('bar')).to.be(null);
203             });
204         });
205     });
206
207     describe('dispatchAction()', () => {
208         it('must invoke a callback specified in listenToAction', () => {
209             return new BrowsingContext().importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
210                 class SomeComponent extends ComponentBase { }
211                 ComponentBase.defineElement('some-component', SomeComponent);
212
213                 const instance = new SomeComponent;
214
215                 const calls = [];
216                 instance.listenToAction('action', (...args) => {
217                     calls.push(args);
218                 });
219                 const object = {'foo': 1};
220                 instance.dispatchAction('action', 'bar', object, 5);
221
222                 expect(calls.length).to.be(1);
223                 expect(calls[0][0]).to.be('bar');
224                 expect(calls[0][1]).to.be(object);
225                 expect(calls[0][2]).to.be(5);
226             });
227         });
228
229         it('must not do anything when there are no callbacks', () => {
230             return new BrowsingContext().importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
231                 class SomeComponent extends ComponentBase { }
232                 ComponentBase.defineElement('some-component', SomeComponent);
233
234                 const object = {'foo': 1};
235                 (new SomeComponent).dispatchAction('action', 'bar', object, 5);
236             });
237         });
238     });
239
240     describe('enqueueToRender()', () => {
241         it('must not immediately call render()', () => {
242             const context = new BrowsingContext();
243             return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
244                 context.global.requestAnimationFrame = () => {}
245
246                 let renderCallCount = 0;
247                 const SomeComponent = class extends ComponentBase {
248                     render() { renderCallCount++; }
249                 }
250                 ComponentBase.defineElement('some-component', SomeComponent);
251
252                 (new SomeComponent).enqueueToRender();
253                 expect(renderCallCount).to.be(0);
254
255                 (new SomeComponent).enqueueToRender();
256                 expect(renderCallCount).to.be(0);
257             });
258         });
259
260         it('must request an animation frame exactly once', () => {
261             const context = new BrowsingContext();
262             return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
263                 let requestAnimationFrameCount = 0;
264                 context.global.requestAnimationFrame = () => { requestAnimationFrameCount++; }
265
266                 const SomeComponent = class extends ComponentBase { }
267                 ComponentBase.defineElement('some-component', SomeComponent);
268
269                 expect(requestAnimationFrameCount).to.be(0);
270                 let instance = new SomeComponent;
271                 instance.enqueueToRender();
272                 expect(requestAnimationFrameCount).to.be(1);
273
274                 instance.enqueueToRender();
275                 expect(requestAnimationFrameCount).to.be(1);
276
277                 (new SomeComponent).enqueueToRender();
278                 expect(requestAnimationFrameCount).to.be(1);
279
280                 const AnotherComponent = class extends ComponentBase { }
281                 ComponentBase.defineElement('another-component', AnotherComponent);
282                 (new AnotherComponent).enqueueToRender();
283                 expect(requestAnimationFrameCount).to.be(1);
284             });
285         });
286
287         it('must invoke render() when the callback to requestAnimationFrame is called', () => {
288             const context = new BrowsingContext();
289             return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
290                 let callback = null;
291                 context.global.requestAnimationFrame = (newCallback) => {
292                     expect(callback).to.be(null);
293                     expect(newCallback).to.not.be(null);
294                     callback = newCallback;
295                 }
296
297                 let renderCalls = [];
298                 const SomeComponent = class extends ComponentBase {
299                     render() {
300                         renderCalls.push(this);
301                     }
302                 }
303                 ComponentBase.defineElement('some-component', SomeComponent);
304
305                 expect(renderCalls.length).to.be(0);
306                 const instance = new SomeComponent;
307                 instance.enqueueToRender();
308                 instance.enqueueToRender();
309
310                 const anotherInstance = new SomeComponent;
311                 anotherInstance.enqueueToRender();
312                 expect(renderCalls.length).to.be(0);
313
314                 callback();
315
316                 expect(renderCalls.length).to.be(2);
317                 expect(renderCalls[0]).to.be(instance);
318                 expect(renderCalls[1]).to.be(anotherInstance);
319             });
320         });
321
322         it('must immediately invoke render() on a component enqueued inside another render() call', () => {
323             const context = new BrowsingContext();
324             return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
325                 let callback = null;
326                 context.global.requestAnimationFrame = (newCallback) => {
327                     expect(callback).to.be(null);
328                     expect(newCallback).to.not.be(null);
329                     callback = newCallback;
330                 }
331
332                 let renderCalls = [];
333                 let instanceToEnqueue = null;
334                 const SomeComponent = class extends ComponentBase {
335                     render() {
336                         renderCalls.push(this);
337                         if (instanceToEnqueue)
338                             instanceToEnqueue.enqueueToRender();
339                         instanceToEnqueue = null;
340                     }
341                 }
342                 ComponentBase.defineElement('some-component', SomeComponent);
343
344                 expect(renderCalls.length).to.be(0);
345                 const instance = new SomeComponent;
346                 const anotherInstance = new SomeComponent;
347                 instance.enqueueToRender();
348                 instanceToEnqueue = anotherInstance;
349                 callback();
350                 callback = null;
351                 expect(renderCalls.length).to.be(2);
352                 expect(renderCalls[0]).to.be(instance);
353                 expect(renderCalls[1]).to.be(anotherInstance);
354                 renderCalls = [];
355
356                 instance.enqueueToRender();
357                 anotherInstance.enqueueToRender();
358                 instanceToEnqueue = instance;
359                 callback();
360                 expect(renderCalls.length).to.be(3);
361                 expect(renderCalls[0]).to.be(instance);
362                 expect(renderCalls[1]).to.be(anotherInstance);
363                 expect(renderCalls[2]).to.be(instance);
364             });
365         });
366
367         it('must request a new animation frame once it exited the callback from requestAnimationFrame', () => {
368             const context = new BrowsingContext();
369             return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
370                 let requestAnimationFrameCount = 0;
371                 let callback = null;
372                 context.global.requestAnimationFrame = (newCallback) => {
373                     expect(callback).to.be(null);
374                     expect(newCallback).to.not.be(null);
375                     callback = newCallback;
376                     requestAnimationFrameCount++;
377                 }
378
379                 let renderCalls = [];
380                 const SomeComponent = class extends ComponentBase {
381                     render() { renderCalls.push(this); }
382                 }
383                 ComponentBase.defineElement('some-component', SomeComponent);
384
385                 const instance = new SomeComponent;
386                 const anotherInstance = new SomeComponent;
387                 expect(requestAnimationFrameCount).to.be(0);
388
389                 instance.enqueueToRender();
390                 expect(requestAnimationFrameCount).to.be(1);
391                 anotherInstance.enqueueToRender();
392                 expect(requestAnimationFrameCount).to.be(1);
393
394                 expect(renderCalls.length).to.be(0);
395                 callback();
396                 callback = null;
397                 expect(renderCalls.length).to.be(2);
398                 expect(renderCalls[0]).to.be(instance);
399                 expect(renderCalls[1]).to.be(anotherInstance);
400                 expect(requestAnimationFrameCount).to.be(1);
401
402                 anotherInstance.enqueueToRender();
403                 expect(requestAnimationFrameCount).to.be(2);
404                 instance.enqueueToRender();
405                 expect(requestAnimationFrameCount).to.be(2);
406
407                 expect(renderCalls.length).to.be(2);
408                 callback();
409                 callback = null;
410                 expect(renderCalls.length).to.be(4);
411                 expect(renderCalls[0]).to.be(instance);
412                 expect(renderCalls[1]).to.be(anotherInstance);
413                 expect(renderCalls[2]).to.be(anotherInstance);
414                 expect(renderCalls[3]).to.be(instance);
415                 expect(requestAnimationFrameCount).to.be(2);
416             });
417         });
418
419     });
420
421     describe('render()', () => {
422         it('must create shadow tree', () => {
423             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
424                 instance.render();
425                 expect(hasShadowTree()).to.be(true);
426             });
427         });
428
429         it('must not create shadow tree when neither htmlTemplate nor cssTemplate are present', () => {
430             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
431                 instance.render();
432                 expect(hasShadowTree()).to.be(false);
433             }, {htmlTemplate: false, cssTemplate: false});
434         });
435
436         it('must create shadow tree when htmlTemplate is present and cssTemplate is not', () => {
437             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
438                 instance.render();
439                 expect(hasShadowTree()).to.be(true);
440             }, {htmlTemplate: true, cssTemplate: false});
441         });
442
443         it('must create shadow tree when cssTemplate is present and htmlTemplate is not', () => {
444             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
445                 instance.render();
446                 expect(hasShadowTree()).to.be(true);
447             }, {htmlTemplate: false, cssTemplate: true});
448         });
449
450         it('must invoke didConstructShadowTree after creating the shadow tree', () => {
451             const context = new BrowsingContext();
452             return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
453                 let didConstructShadowTreeCount = 0;
454                 let htmlTemplateCount = 0;
455
456                 class SomeComponent extends ComponentBase {
457                     didConstructShadowTree()
458                     {
459                         expect(this.content()).to.be.a(context.global.ShadowRoot);
460                         didConstructShadowTreeCount++;
461                     }
462
463                     static htmlTemplate()
464                     {
465                         htmlTemplateCount++;
466                         return '';
467                     }
468                 }
469                 ComponentBase.defineElement('some-component', SomeComponent);
470
471                 const instance = new SomeComponent;
472                 expect(didConstructShadowTreeCount).to.be(0);
473                 expect(htmlTemplateCount).to.be(0);
474                 instance.render();
475                 expect(didConstructShadowTreeCount).to.be(1);
476                 expect(htmlTemplateCount).to.be(1);
477             });
478         });
479     });
480
481     describe('createElement()', () => {
482
483         it('should create an element of the specified name', () => {
484             const context = new BrowsingContext();
485             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
486                 const div = ComponentBase.createElement('div');
487                 expect(div).to.be.a(context.global.HTMLDivElement);
488             });
489         });
490
491         it('should create an element with the specified attributes', () => {
492             const context = new BrowsingContext();
493             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
494                 const input = ComponentBase.createElement('input', {'title': 'hi', 'id': 'foo', 'required': false, 'checked': true});
495                 expect(input).to.be.a(context.global.HTMLInputElement);
496                 expect(input.attributes.length).to.be(3);
497                 expect(input.attributes[0].localName).to.be('title');
498                 expect(input.attributes[0].value).to.be('hi');
499                 expect(input.attributes[1].localName).to.be('id');
500                 expect(input.attributes[1].value).to.be('foo');
501                 expect(input.attributes[2].localName).to.be('checked');
502                 expect(input.attributes[2].value).to.be('checked');
503             });
504         });
505
506         it('should create an element with the specified event handlers and attributes', () => {
507             const context = new BrowsingContext();
508             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
509                 let clickCount = 0;
510                 const div = ComponentBase.createElement('div', {'title': 'hi', 'onclick': () => clickCount++});
511                 expect(div).to.be.a(context.global.HTMLDivElement);
512                 expect(div.attributes.length).to.be(1);
513                 expect(div.attributes[0].localName).to.be('title');
514                 expect(div.attributes[0].value).to.be('hi');
515                 expect(clickCount).to.be(0);
516                 div.click();
517                 expect(clickCount).to.be(1);
518             });
519         });
520
521         it('should create an element with the specified children when there is no attribute specified', () => {
522             const context = new BrowsingContext();
523             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
524                 const element = ComponentBase.createElement;
525                 const span = element('span');
526                 const div = element('div', [span, 'hi']);
527                 expect(div).to.be.a(context.global.HTMLDivElement);
528                 expect(div.attributes.length).to.be(0);
529                 expect(div.childNodes.length).to.be(2);
530                 expect(div.childNodes[0]).to.be(span);
531                 expect(div.childNodes[1]).to.be.a(context.global.Text);
532                 expect(div.childNodes[1].data).to.be('hi');
533             });
534         });
535
536         it('should create an element with the specified children when the second argument is a span', () => {
537             const context = new BrowsingContext();
538             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
539                 const element = ComponentBase.createElement;
540                 const span = element('span');
541                 const div = element('div', span);
542                 expect(div).to.be.a(context.global.HTMLDivElement);
543                 expect(div.attributes.length).to.be(0);
544                 expect(div.childNodes.length).to.be(1);
545                 expect(div.childNodes[0]).to.be(span);
546             });
547         });
548
549         it('should create an element with the specified children when the second argument is a Text node', () => {
550             const context = new BrowsingContext();
551             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
552                 const element = ComponentBase.createElement;
553                 const text = context.document.createTextNode('hi');
554                 const div = element('div', text);
555                 expect(div).to.be.a(context.global.HTMLDivElement);
556                 expect(div.attributes.length).to.be(0);
557                 expect(div.childNodes.length).to.be(1);
558                 expect(div.childNodes[0]).to.be(text);
559             });
560         });
561
562         it('should create an element with the specified children when the second argument is a component', () => {
563             const context = new BrowsingContext();
564             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
565                 class SomeComponent extends ComponentBase { };
566                 ComponentBase.defineElement('some-component', SomeComponent);
567                 const element = ComponentBase.createElement;
568                 const component = new SomeComponent;
569                 const div = element('div', component);
570                 expect(div).to.be.a(context.global.HTMLDivElement);
571                 expect(div.attributes.length).to.be(0);
572                 expect(div.childNodes.length).to.be(1);
573                 expect(div.childNodes[0]).to.be(component.element());
574             });
575         });
576
577         it('should create an element with the specified attributes and children', () => {
578             const context = new BrowsingContext();
579             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
580                 const element = ComponentBase.createElement;
581                 const span = element('span');
582                 const div = element('div', {'lang': 'en'}, [span, 'hi']);
583                 expect(div).to.be.a(context.global.HTMLDivElement);
584                 expect(div.attributes.length).to.be(1);
585                 expect(div.attributes[0].localName).to.be('lang');
586                 expect(div.attributes[0].value).to.be('en');
587                 expect(div.childNodes.length).to.be(2);
588                 expect(div.childNodes[0]).to.be(span);
589                 expect(div.childNodes[1]).to.be.a(context.global.Text);
590                 expect(div.childNodes[1].data).to.be('hi');
591             });
592         });
593
594     });
595
596     describe('defineElement()', () => {
597
598         it('must define a custom element with a class of an appropriate name', () => {
599             const context = new BrowsingContext();
600             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
601                 class SomeComponent extends ComponentBase { }
602                 ComponentBase.defineElement('some-component', SomeComponent);
603
604                 let elementClass = context.global.customElements.get('some-component');
605                 expect(elementClass).to.be.a('function');
606                 expect(elementClass.name).to.be('SomeComponentElement');
607             });
608         });
609
610         it('must define a custom element that can be instantiated via document.createElement', () => {
611             const context = new BrowsingContext();
612             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
613                 let instances = [];
614                 class SomeComponent extends ComponentBase {
615                     constructor() {
616                         super();
617                         instances.push(this);
618                     }
619                 }
620                 ComponentBase.defineElement('some-component', SomeComponent);
621
622                 expect(instances.length).to.be(0);
623                 let element = context.document.createElement('some-component');
624                 expect(instances.length).to.be(1);
625
626                 expect(element).to.be.a(context.global.HTMLElement);
627                 expect(element.component()).to.be(instances[0]);
628                 expect(instances[0].element()).to.be(element);
629                 expect(instances.length).to.be(1);
630             });
631         });
632
633         it('must define a custom element that can be instantiated via new', () => {
634             const context = new BrowsingContext();
635             return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
636                 let instances = [];
637                 class SomeComponent extends ComponentBase {
638                     constructor() {
639                         super();
640                         instances.push(this);
641                     }
642                 }
643                 ComponentBase.defineElement('some-component', SomeComponent);
644
645                 expect(instances.length).to.be(0);
646                 let component = new SomeComponent;
647                 expect(instances.length).to.be(1);
648
649                 expect(component).to.be(instances[0]);
650                 expect(component.element()).to.be.a(context.global.HTMLElement);
651                 expect(component.element().component()).to.be(component);
652                 expect(instances.length).to.be(1);
653             });
654         });
655
656     });
657
658 });