| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library entered_left_view_test; |
| |
| import 'dart:async'; |
| import 'dart:html'; |
| import 'dart:js' as js; |
| import 'package:unittest/html_individual_config.dart'; |
| import 'package:unittest/unittest.dart'; |
| import '../utils.dart'; |
| |
| var invocations = []; |
| class Foo extends HtmlElement { |
| factory Foo() => null; |
| Foo.created() : super.created(); |
| |
| void createdCallback() { |
| invocations.add('created'); |
| } |
| |
| void enteredView() { |
| invocations.add('entered'); |
| } |
| |
| void leftView() { |
| invocations.add('left'); |
| } |
| |
| void attributeChanged(String name, String oldValue, String newValue) { |
| invocations.add('attribute changed'); |
| } |
| } |
| |
| // Pump custom events polyfill events. |
| void customElementsTakeRecords() { |
| if (js.context.hasProperty('CustomElements')) { |
| js.context['CustomElements'].callMethod('takeRecords'); |
| } |
| } |
| |
| main() { |
| useHtmlIndividualConfiguration(); |
| |
| // Adapted from Blink's |
| // fast/dom/custom/entered-left-document.html test. |
| |
| var docA = document; |
| var docB = document.implementation.createHtmlDocument(''); |
| |
| var nullSanitizer = new NullTreeSanitizer(); |
| |
| var registeredTypes = false; |
| setUp(() { |
| return loadPolyfills().then((_) { |
| if (registeredTypes) { |
| return; |
| } |
| registeredTypes = true; |
| document.register('x-a', Foo); |
| }); |
| }); |
| |
| group('standard_events', () { |
| var a; |
| setUp(() { |
| invocations = []; |
| }); |
| |
| test('Created', () { |
| a = new Element.tag('x-a'); |
| expect(invocations, ['created']); |
| }); |
| |
| test('entered', () { |
| document.body.append(a); |
| customElementsTakeRecords(); |
| expect(invocations, ['entered']); |
| }); |
| |
| test('left', () { |
| a.remove(); |
| customElementsTakeRecords(); |
| expect(invocations, ['left']); |
| }); |
| |
| var div = new DivElement(); |
| test('nesting does not trigger entered', () { |
| div.append(a); |
| customElementsTakeRecords(); |
| expect(invocations, []); |
| }); |
| |
| test('nested entering triggers entered', () { |
| document.body.append(div); |
| customElementsTakeRecords(); |
| expect(invocations, ['entered']); |
| }); |
| |
| test('nested leaving triggers left', () { |
| div.remove(); |
| customElementsTakeRecords(); |
| expect(invocations, ['left']); |
| }); |
| }); |
| |
| group('viewless_document', () { |
| var a; |
| setUp(() { |
| invocations = []; |
| }); |
| |
| test('Created, owned by a document without a view', () { |
| a = docB.createElement('x-a'); |
| expect(a.document, docB, |
| reason:'new instance should be owned by the document the definition ' |
| 'was registered with'); |
| expect(invocations, ['created'], |
| reason: 'calling the constructor should invoke the created callback'); |
| }); |
| |
| test('Entered document without a view', () { |
| docB.body.append(a); |
| expect(invocations, [], |
| reason: 'entered callback should not be invoked when entering a ' |
| 'document without a view'); |
| }); |
| |
| /* |
| test('Attribute changed in document without a view', () { |
| a.setAttribute('data-foo', 'bar'); |
| expect(invocations, ['attribute changed'], |
| reason: 'changing an attribute should invoke the callback, even in a ' |
| 'document without a view'); |
| }); |
| */ |
| test('Entered document with a view', () { |
| document.body.append(a); |
| customElementsTakeRecords(); |
| expect(invocations, ['entered'], |
| reason: 'entered callback should be invoked when entering a document ' |
| 'with a view'); |
| }); |
| |
| test('Left document with a view', () { |
| a.remove(); |
| customElementsTakeRecords(); |
| expect(invocations, ['left'], |
| reason: 'left callback should be invoked when leaving a document ' |
| 'with a view'); |
| }); |
| |
| test('Created in a document without a view', () { |
| docB.body.setInnerHtml('<x-a></x-a>', treeSanitizer: nullSanitizer); |
| Platform.upgradeCustomElements(docB.body); |
| |
| expect(invocations, ['created'], |
| reason: 'only created callback should be invoked when parsing a ' |
| 'custom element in a document without a view'); |
| }); |
| }); |
| |
| group('shadow_dom', () { |
| var div; |
| var s; |
| setUp(() { |
| invocations = []; |
| div = new DivElement(); |
| s = div.createShadowRoot(); |
| }); |
| |
| tearDown(() { |
| customElementsTakeRecords(); |
| }); |
| |
| test('Created in Shadow DOM that is not in a document', () { |
| s.setInnerHtml('<x-a></x-a>', treeSanitizer: nullSanitizer); |
| Platform.upgradeCustomElements(s); |
| |
| expect(invocations, ['created'], |
| reason: 'the entered callback should not be invoked when entering a ' |
| 'Shadow DOM subtree not in the document'); |
| }); |
| |
| test('Leaves Shadow DOM that is not in a document', () { |
| s.innerHtml = ''; |
| expect(invocations, [], |
| reason: 'the left callback should not be invoked when leaving a ' |
| 'Shadow DOM subtree not in the document'); |
| }); |
| |
| test('Enters a document with a view as a constituent of Shadow DOM', () { |
| s.setInnerHtml('<x-a></x-a>', treeSanitizer: nullSanitizer); |
| Platform.upgradeCustomElements(s); |
| |
| document.body.append(div); |
| customElementsTakeRecords(); |
| expect(invocations, ['created', 'entered'], |
| reason: 'the entered callback should be invoked when inserted into ' |
| 'a document with a view as part of Shadow DOM'); |
| |
| div.remove(); |
| customElementsTakeRecords(); |
| |
| expect(invocations, ['created', 'entered', 'left'], |
| reason: 'the left callback should be invoked when removed from a ' |
| 'document with a view as part of Shadow DOM'); |
| }); |
| }); |
| |
| |
| group('disconnected_subtree', () { |
| var div = new DivElement(); |
| |
| setUp(() { |
| invocations = []; |
| }); |
| |
| test('Enters a disconnected subtree of DOM', () { |
| div.setInnerHtml('<x-a></x-a>', treeSanitizer: nullSanitizer); |
| Platform.upgradeCustomElements(div); |
| |
| expect(invocations, ['created'], |
| reason: 'the entered callback should not be invoked when inserted ' |
| 'into a disconnected subtree'); |
| }); |
| |
| test('Leaves a disconnected subtree of DOM', () { |
| div.innerHtml = ''; |
| expect(invocations, [], |
| reason: 'the left callback should not be invoked when removed from a ' |
| 'disconnected subtree'); |
| }); |
| |
| test('Enters a document with a view as a constituent of a subtree', () { |
| div.setInnerHtml('<x-a></x-a>', treeSanitizer: nullSanitizer); |
| Platform.upgradeCustomElements(div); |
| invocations = []; |
| document.body.append(div); |
| customElementsTakeRecords(); |
| expect(invocations, ['entered'], |
| reason: 'the entered callback should be invoked when inserted into a ' |
| 'document with a view as part of a subtree'); |
| }); |
| }); |
| } |