Skip to content

Commit

Permalink
Add integration for shadow AMP v0.js
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkimball committed Feb 10, 2020
1 parent af50ae0 commit 08cb627
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 24 deletions.
8 changes: 6 additions & 2 deletions build-system/externs/amp.extern.js
Expand Up @@ -220,8 +220,12 @@ window.AMP.viewport = {};
window.AMP.viewport.getScrollLeft;
window.AMP.viewport.getScrollWidth;
window.AMP.viewport.getWidth;
window.AMP.attachShadowDoc;
window.AMP.attachShadowDocAsStream;

/** @type {function(!HTMLElement, !Document, !string, Object)} */
window.AMP.attachShadowDoc = function(element, document, url, options) {};

/** @type {function(!HTMLElement, !string, Object)} */
window.AMP.attachShadowDocAsStream = function(element, url, options) {};

/** @constructor */
function AmpConfigType() {}
Expand Down
23 changes: 23 additions & 0 deletions build-system/server/amp4test.js
Expand Up @@ -232,6 +232,29 @@ app.get('/a4a/:bid', (req, res) => {
res.send(doc);
});

/**
* Serves an amp doc for test-shadow-amp.js
*/
app.get('/shadow-amp', (_req, res) =>
res.send(`
<!doctype html>
<html amp lang="en">
<head>
<meta charset="utf-8">
<script async src="https://cdn.ampproject.org/v0.js"></script>
<title>Hello, AMPs</title>
<link rel="canonical" href="https://amp.dev/documentation/guides-and-tutorials/start/create/basic_markup/">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
</head>
<body>
<h1>Shadow AMP document</h1>
<amp-img src="https://placekitten.com/640/480" layout="responsive" width="640" height="480"></amp-img>
</body>
</html>
`)
);

/**
* @param {{body: string, css: string|undefined, extensions: Array<string>|undefined, head: string|undefined, spec: string|undefined}} config
* @return {string}
Expand Down
16 changes: 1 addition & 15 deletions src/polyfills/custom-elements.js
Expand Up @@ -720,7 +720,7 @@ function polyfill(win) {
// Have to patch shadow methods now, since there's no way to find shadow trees
// later.
const elProto = Element.prototype;
const {attachShadow, createShadowRoot} = elProto;
const {attachShadow} = elProto;
if (attachShadow) {
/**
* @param {!{mode: string}} unused
Expand All @@ -736,20 +736,6 @@ function polyfill(win) {
return attachShadow.toString();
};
}
if (createShadowRoot) {
/**
* @return {!ShadowRoot}
*/
elProto.createShadowRoot = function() {
const shadow = createShadowRoot.apply(this, arguments);
registry.observe(shadow);
return shadow;
};
// Necessary for Shadow AMP
elProto.createShadowRoot.toString = function() {
return createShadowRoot.toString();
};
}

/**
* You can't use the real HTMLElement constructor, because you can't subclass
Expand Down
2 changes: 0 additions & 2 deletions src/shadow-embed.js
Expand Up @@ -79,8 +79,6 @@ export function createShadowRoot(hostElement) {
},
});
}
} else if (shadowDomSupported == ShadowDomVersion.V0) {
shadowRoot = hostElement.createShadowRoot();
} else {
shadowRoot = createShadowRootPolyfill(hostElement);
}
Expand Down
76 changes: 76 additions & 0 deletions test/integration/test-shadow-amp.js
@@ -0,0 +1,76 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {BrowserController} from '../../testing/test-helper';

describes.integration(
'AMP shadow v0',
{
amp: false,
body: `
<!-- unminified src for local-tests.js -->
<script async src="/dist/amp-shadow.js"></script>
<!-- minified src for single-pass-tests.js -->
<script async src="/dist/shadow-v0.js"></script>
<div id="host"></div>
<script>
function fetchDocument(url) {
var xhr = new XMLHttpRequest();
return new Promise(function(resolve, reject) {
xhr.open('GET', url, true);
xhr.responseType = 'document';
xhr.setRequestHeader('Accept', 'text/html');
xhr.onload = function() {
// .responseXML contains a ready-to-use Document object
resolve(xhr.responseXML);
};
xhr.send();
});
}
(window.AMP = window.AMP || []).push(() => {
const host = document.getElementById('host');
const testUrl = 'http://localhost:9876/amp4test/shadow-amp';
fetchDocument(testUrl).then(doc => {
AMP.attachShadowDoc(host, doc, testUrl);
});
});
</script>
`,
mockFetch: false,
},
env => {
let docController;
let shadowDoc;

beforeEach(async () => {
docController = new BrowserController(env.win);
await docController.waitForShadowRoot('#host', 25000);
shadowDoc = env.win.document.getElementById('host').shadowRoot;
});

it('should attach shadow AMP document', () => {
return expect(shadowDoc.body.innerText).to.include('Shadow AMP document');
});

it('should layout amp-img component in shadow AMP document', async () => {
const shadowDocController = new BrowserController(env.win, shadowDoc);
await shadowDocController.waitForElementLayout('amp-img');
return expect(
shadowDoc.querySelectorAll('amp-img img[src]')
).to.have.length(1);
});
}
);
28 changes: 23 additions & 5 deletions testing/test-helper.js
Expand Up @@ -210,9 +210,9 @@ export class RequestBank {
}

export class BrowserController {
constructor(win) {
constructor(win, opt_rootNode) {
this.win_ = win;
this.doc_ = this.win_.document;
this.rootNode_ = opt_rootNode || this.win_.document;
}

wait(duration) {
Expand All @@ -221,13 +221,31 @@ export class BrowserController {
});
}

/**
* @param {string} hostSelector
* @param {number=} timeout
* @return {!Promise}
*/
waitForShadowRoot(hostSelector, timeout = 10000) {
const element = this.rootNode_.querySelector(hostSelector);
if (!element) {
throw new Error(`BrowserController query failed: ${hostSelector}`);
}
return poll(
`"${hostSelector}" to host shadow doc`,
() => !!element.shadowRoot,
/* onError */ undefined,
timeout
);
}

/**
* @param {string} selector
* @param {number=} timeout
* @return {!Promise}
*/
waitForElementBuild(selector, timeout = 5000) {
const elements = this.doc_.querySelectorAll(selector);
const elements = this.rootNode_.querySelectorAll(selector);
if (!elements.length) {
throw new Error(`BrowserController query failed: ${selector}`);
}
Expand All @@ -250,7 +268,7 @@ export class BrowserController {
* @return {!Promise}
*/
waitForElementLayout(selector, timeout = 10000) {
const elements = this.doc_.querySelectorAll(selector);
const elements = this.rootNode_.querySelectorAll(selector);
if (!elements.length) {
throw new Error(`BrowserController query failed: ${selector}`);
}
Expand All @@ -271,7 +289,7 @@ export class BrowserController {
}

click(selector) {
const element = this.doc_.querySelector(selector);
const element = this.rootNode_.querySelector(selector);
if (element) {
element.dispatchEvent(new /*OK*/ CustomEvent('click', {bubbles: true}));
}
Expand Down

0 comments on commit 08cb627

Please sign in to comment.