| Index: appengine/config_service/ui/bower_components/test-fixture/test-fixture.html
|
| diff --git a/appengine/config_service/ui/bower_components/test-fixture/test-fixture.html b/appengine/config_service/ui/bower_components/test-fixture/test-fixture.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2194e147904f4177945c14cfa03875153e97fa85
|
| --- /dev/null
|
| +++ b/appengine/config_service/ui/bower_components/test-fixture/test-fixture.html
|
| @@ -0,0 +1,351 @@
|
| +<!--
|
| +@license
|
| +Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
| +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
| +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
| +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
| +Code distributed by Google as part of the polymer project is also
|
| +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
| +-->
|
| +
|
| +<!--
|
| +
|
| +The `<test-fixture>` element can simplify the exercise of consistently
|
| +resetting a test suite's DOM. To use it, wrap the test suite's DOM as a template:
|
| +
|
| +```html
|
| +<test-fixture id="SomeElementFixture">
|
| + <template>
|
| + <some-element id="SomeElementForTesting"></some-element>
|
| + </template>
|
| +</test-fixture>
|
| +```
|
| +
|
| +Now, the `<test-fixture>` element can be used to generate a copy of its
|
| +template:
|
| +
|
| +```html
|
| +<script>
|
| +describe('<some-element>', function () {
|
| + var someElement;
|
| +
|
| + beforeEach(function () {
|
| + document.getElementById('SomeElementFixture').create();
|
| + someElement = document.getElementById('SomeElementForTesting');
|
| + });
|
| +});
|
| +</script>
|
| +```
|
| +
|
| +Fixtured elements can be cleaned up by calling `restore` on the `<test-fixture>`:
|
| +
|
| +```javascript
|
| + afterEach(function () {
|
| + document.getElementById('SomeElementFixture').restore();
|
| + // <some-element id='SomeElementForTesting'> has been removed
|
| + });
|
| +```
|
| +
|
| +`<test-fixture>` will create fixtures from all of its immediate `<template>`
|
| +children. The DOM structure of fixture templates can be as simple or as complex
|
| +as the situation calls for.
|
| +
|
| +## Even simpler usage in Mocha
|
| +
|
| +In Mocha, usage can be simplified even further. Include `test-fixture-mocha.js`
|
| +after Mocha in the `<head>` of your document and then fixture elements like so:
|
| +
|
| +```html
|
| +<script>
|
| +describe('<some-element>', function () {
|
| + var someElement;
|
| +
|
| + beforeEach(function () {
|
| + someElement = fixture('SomeElementFixture');
|
| + });
|
| +});
|
| +</script>
|
| +```
|
| +
|
| +Fixtured elements will be automatically restored in the `afterEach` phase of the
|
| +current Mocha `Suite`.
|
| +
|
| +## Data-bound templates
|
| +
|
| +Data-binding systems are also supported, as long as your (custom) template
|
| +elements define a `stamp(model)` method that returns a document fragment. This
|
| +allows you to stamp out templates w/ custom models for your fixtures.
|
| +
|
| +For example, using Polymer 0.8's `dom-template`:
|
| +
|
| +```html
|
| +<test-fixture id="bound">
|
| + <template is="dom-template">
|
| + <span>{{greeting}}</span>
|
| + </template>
|
| +</test-fixture>
|
| +```
|
| +
|
| +You can pass an optional context argument to `create()` or `fixture()` to pass
|
| +the model:
|
| +
|
| +```js
|
| +var bound = fixture('bound', {greeting: 'ohai thurr'});
|
| +```
|
| +
|
| +## The problem being addressed
|
| +
|
| +Consider the following `web-component-tester` test suite:
|
| +
|
| +```html
|
| +<!doctype html>
|
| +<html>
|
| +<head>
|
| + <title>some-element test suite</title>
|
| +
|
| + <link rel="import" href="../some-element.html">
|
| +</head>
|
| +<body>
|
| + <some-element id="SomeElementForTesting"></some-element>
|
| + <script>
|
| +describe('<some-element>', function () {
|
| + var someElement;
|
| +
|
| + beforeEach(function () {
|
| + someElement = document.getElementById('SomeElementForTesting');
|
| + });
|
| +
|
| + it('can receive property `foo`', function () {
|
| + someElement.foo = 'bar';
|
| + expect(someElement.foo).to.be.equal('bar');
|
| + });
|
| +
|
| + it('has a default `foo` value of `undefined`', function () {
|
| + expect(someElement.foo).to.be.equal(undefined);
|
| + });
|
| +});
|
| + </script>
|
| +</body>
|
| +</html>
|
| +```
|
| +
|
| +In this contrived example, the suite will pass or fail depending on which order
|
| +the tests are run in. It is non-deterministic because `someElement` has
|
| +internal state that is not properly reset at the end of each test.
|
| +
|
| +It would be trivial in the above example to simply reset `someElement.foo` to
|
| +the expected default value of `undefined` in an `afterEach` hook. However, for
|
| +non-contrived test suites that target complex elements, this can result in a
|
| +large quantity of ever-growing set-up and tear-down boilerplate.
|
| +
|
| +@pseudoElement test-fixture
|
| + -->
|
| +
|
| +<script>
|
| +
|
| +(function () {
|
| + var TestFixturePrototype = Object.create(HTMLElement.prototype);
|
| + var TestFixtureExtension = {
|
| + _fixtureTemplates: null,
|
| +
|
| + _elementsFixtured: false,
|
| +
|
| + get elementsFixtured () {
|
| + return this._elementsFixtured;
|
| + },
|
| +
|
| + get fixtureTemplates () {
|
| + if (!this._fixtureTemplates) {
|
| + // Copy fixtures to a true Array for Safari 7. This prevents their
|
| + // `content` property from being improperly garbage collected.
|
| + this._fixtureTemplates = Array.prototype.slice.apply(this.querySelectorAll('template'));
|
| + }
|
| +
|
| + return this._fixtureTemplates;
|
| + },
|
| +
|
| + create: function (model) {
|
| + var generatedDoms = [];
|
| +
|
| + this.restore();
|
| +
|
| + this.removeElements(this.fixtureTemplates);
|
| +
|
| + this.forElements(this.fixtureTemplates, function (fixtureTemplate) {
|
| + generatedDoms.push(
|
| + this.createFrom(fixtureTemplate, model)
|
| + );
|
| + }, this);
|
| +
|
| + this.forcePolyfillAttachedStateSynchrony();
|
| +
|
| + if (generatedDoms.length < 2) {
|
| + return generatedDoms[0];
|
| + }
|
| +
|
| + return generatedDoms;
|
| + },
|
| +
|
| + createFrom: function (fixtureTemplate, model) {
|
| + var fixturedFragment;
|
| + var fixturedElements;
|
| + var fixturedElement;
|
| +
|
| + if (!(fixtureTemplate &&
|
| + fixtureTemplate.tagName === 'TEMPLATE')) {
|
| + return;
|
| + }
|
| +
|
| + try {
|
| + fixturedFragment = this.stamp(fixtureTemplate, model);
|
| + } catch (error) {
|
| + console.error('Error stamping', fixtureTemplate, error);
|
| + throw error;
|
| + }
|
| +
|
| + fixturedElements = this.collectElementChildren(fixturedFragment);
|
| +
|
| + this.appendChild(fixturedFragment);
|
| + this._elementsFixtured = true;
|
| +
|
| + if (fixturedElements.length < 2) {
|
| + return fixturedElements[0];
|
| + }
|
| +
|
| + return fixturedElements;
|
| + },
|
| +
|
| + restore: function () {
|
| + if (!this._elementsFixtured) {
|
| + return;
|
| + }
|
| +
|
| + this.removeElements(this.children);
|
| +
|
| + this.forElements(this.fixtureTemplates, function (fixtureTemplate) {
|
| + this.appendChild(fixtureTemplate);
|
| + }, this);
|
| +
|
| + this.generatedDomStack = [];
|
| +
|
| + this._elementsFixtured = false;
|
| +
|
| + this.forcePolyfillAttachedStateSynchrony();
|
| + },
|
| +
|
| + forcePolyfillAttachedStateSynchrony: function () {
|
| + // Force synchrony in attachedCallback and detachedCallback where
|
| + // implemented, in the event that we are dealing with one of these async
|
| + // polyfills:
|
| + // 1. Web Components CustomElements polyfill (v1 or v0).
|
| + if (window.customElements && window.customElements.flush) {
|
| + window.customElements.flush();
|
| + } else if (window.CustomElements && window.CustomElements.takeRecords) {
|
| + window.CustomElements.takeRecords();
|
| + }
|
| + // 2. ShadyDOM polyfill.
|
| + if (window.ShadyDOM && window.ShadyDOM.flush) {
|
| + window.ShadyDOM.flush();
|
| + }
|
| + },
|
| +
|
| + collectElementChildren: function (parent) {
|
| + // Note: Safari 7.1 does not support `firstElementChild` or
|
| + // `nextElementSibling`, so we do things the old-fashioned way:
|
| + var elements = [];
|
| + var child = parent.firstChild;
|
| +
|
| + while (child) {
|
| + if (child.nodeType === Node.ELEMENT_NODE) {
|
| + elements.push(child);
|
| + }
|
| +
|
| + child = child.nextSibling;
|
| + }
|
| +
|
| + return elements;
|
| + },
|
| +
|
| + removeElements: function (elements) {
|
| + this.forElements(elements, function (element) {
|
| + this.removeChild(element);
|
| + }, this);
|
| + },
|
| +
|
| + forElements: function (elements, iterator, context) {
|
| + Array.prototype.slice.call(elements)
|
| + .forEach(iterator, context);
|
| + },
|
| +
|
| + stamp: function (fixtureTemplate, model) {
|
| + var stamped;
|
| + // Check if we are dealing with a "stampable" `<template>`. This is a
|
| + // vaguely defined special case of a `<template>` that is a custom
|
| + // element with a public `stamp` method that implements some manner of
|
| + // data binding.
|
| + if (fixtureTemplate.stamp) {
|
| + stamped = fixtureTemplate.stamp(model);
|
| + // We leak Polymer specifics a little; if there is an element `root`, we
|
| + // want that to be returned.
|
| + stamped = stamped.root || stamped;
|
| +
|
| + // Otherwise, we fall back to standard HTML templates, which do not have
|
| + // any sort of binding support.
|
| + } else {
|
| + if (model) {
|
| + console.warn(this, 'was given a model to stamp, but the template is not of a bindable type');
|
| + }
|
| +
|
| + stamped = document.importNode(fixtureTemplate.content, true);
|
| +
|
| + // Immediately upgrade the subtree if we are dealing with async
|
| + // Web Components polyfill.
|
| + // https://github.com/Polymer/polymer/blob/0.8-preview/src/features/mini/template.html#L52
|
| + if (window.CustomElements && window.CustomElements.upgradeSubtree) {
|
| + window.CustomElements.upgradeSubtree(stamped);
|
| + }
|
| + }
|
| +
|
| + return stamped;
|
| + }
|
| + };
|
| +
|
| + Object.getOwnPropertyNames(TestFixtureExtension)
|
| + .forEach(function (property) {
|
| + Object.defineProperty(
|
| + TestFixturePrototype,
|
| + property,
|
| + Object.getOwnPropertyDescriptor(TestFixtureExtension, property)
|
| + );
|
| + });
|
| +
|
| + try {
|
| + if (window.customElements) {
|
| + function TestFixture() {
|
| + return ((window.Reflect && Reflect.construct) ?
|
| + Reflect.construct(HTMLElement, [], TestFixture)
|
| + : HTMLElement.call(this)) || this;
|
| + }
|
| + TestFixture.prototype = TestFixturePrototype;
|
| + // `constructor` is not writable on Safari 9, but is configurable.
|
| + Object.defineProperty(TestFixture.prototype, 'constructor', {
|
| + configurable: true,
|
| + enumerable: true,
|
| + writable: true,
|
| + value: TestFixture,
|
| + });
|
| + window.customElements.define('test-fixture', TestFixture);
|
| + } else {
|
| + document.registerElement('test-fixture', {
|
| + prototype: TestFixturePrototype
|
| + });
|
| + }
|
| + } catch (e) {
|
| + if (window.WCT) {
|
| + console.warn('if you are using WCT, you do not need to manually import test-fixture.html');
|
| + } else {
|
| + console.warn('test-fixture has already been registered!');
|
| + }
|
| + }
|
| +})();
|
| +</script>
|
|
|