Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 3f9d6bf

Browse files
committed
feat(custom-element): patch customElement v1 APIs
1 parent 31fc127 commit 3f9d6bf

File tree

10 files changed

+244
-13
lines changed

10 files changed

+244
-13
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,4 @@ script:
5454
- node_modules/.bin/karma start karma-build-sauce-selenium3-mocha.conf.js --single-run
5555
- node_modules/.bin/gulp test/node
5656
- node_modules/.bin/gulp test/node -no-patch-clock
57+
- node_modules/.bin/karma start karma-dist-sauce-jasmine.es6.conf.js --single-run

karma-build-jasmine.es6.conf.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
module.exports = function (config) {
3+
require('./karma-build-jasmine.conf.js')(config);
4+
config.client.entrypoint = 'browser_es6_entry_point';
5+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
module.exports = function (config) {
3+
require('./karma-dist-jasmine.conf.js')(config);
4+
require('./sauce.es6.conf')(config);
5+
config.client.entrypoint = 'browser_es6_entry_point';
6+
};

lib/browser/browser.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {bindArguments, patchClass, patchMacroTask, patchMethod, patchOnPropertie
1717
import {propertyPatch} from './define-property';
1818
import {eventTargetPatch, patchEvent} from './event-target';
1919
import {propertyDescriptorPatch} from './property-descriptor';
20-
import {registerElementPatch} from './register-element';
20+
import {patchCustomElements, registerElementPatch} from './register-element';
2121

2222
Zone.__load_patch('util', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
2323
api.patchOnProperties = patchOnProperties;
@@ -74,7 +74,11 @@ Zone.__load_patch('EventTarget', (global: any, Zone: ZoneType, api: _ZonePrivate
7474
Zone.__load_patch('on_property', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
7575
propertyDescriptorPatch(api, global);
7676
propertyPatch();
77+
});
78+
79+
Zone.__load_patch('customElements', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
7780
registerElementPatch(global);
81+
patchCustomElements(global);
7882
});
7983

8084
Zone.__load_patch('canvas', (global: any) => {

lib/browser/register-element.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,16 @@ import {attachOriginToPatched, isBrowser, isMix, ObjectGetOwnPropertyDescriptor,
1010

1111
import {_redefineProperty} from './define-property';
1212

13-
export function registerElementPatch(_global: any) {
14-
if ((!isBrowser && !isMix) || !('registerElement' in (<any>_global).document)) {
13+
function patchCallbacks(target: any, targetName: string, method: string, callbacks: string[]) {
14+
const symbol = Zone.__symbol__(method);
15+
if (target[symbol]) {
1516
return;
1617
}
17-
18-
const _registerElement = (<any>document).registerElement;
19-
const callbacks =
20-
['createdCallback', 'attachedCallback', 'detachedCallback', 'attributeChangedCallback'];
21-
22-
(<any>document).registerElement = function(name: any, opts: any) {
18+
const nativeDelegate = target[symbol] = target[method];
19+
target[method] = function(name: any, opts: any, options?: any) {
2320
if (opts && opts.prototype) {
2421
callbacks.forEach(function(callback) {
25-
const source = 'Document.registerElement::' + callback;
22+
const source = `${targetName}.${method}::` + callback;
2623
const prototype = opts.prototype;
2724
if (prototype.hasOwnProperty(callback)) {
2825
const descriptor = ObjectGetOwnPropertyDescriptor(prototype, callback);
@@ -38,8 +35,30 @@ export function registerElementPatch(_global: any) {
3835
});
3936
}
4037

41-
return _registerElement.call(document, name, opts);
38+
return nativeDelegate.call(target, name, opts, options);
4239
};
4340

44-
attachOriginToPatched((<any>document).registerElement, _registerElement);
41+
attachOriginToPatched(target[method], nativeDelegate);
42+
}
43+
44+
export function registerElementPatch(_global: any) {
45+
if ((!isBrowser && !isMix) || !('registerElement' in (<any>_global).document)) {
46+
return;
47+
}
48+
49+
const callbacks =
50+
['createdCallback', 'attachedCallback', 'detachedCallback', 'attributeChangedCallback'];
51+
52+
patchCallbacks(document, 'Document', 'registerElement', callbacks);
53+
}
54+
55+
export function patchCustomElements(_global: any) {
56+
if ((!isBrowser && !isMix) || !('customElements' in _global)) {
57+
return;
58+
}
59+
60+
const callbacks =
61+
['connectedCallback', 'disconnectedCallback', 'adoptedCallback', 'attributeChangedCallback'];
62+
63+
patchCallbacks(_global.customElements, 'customElements', 'define', callbacks);
4564
}

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"closure:test": "scripts/closure/closure_compiler.sh",
2121
"format": "gulp format:enforce",
2222
"karma-jasmine": "karma start karma-build-jasmine.conf.js",
23+
"karma-jasmine:es6": "karma start karma-build-jasmine.es6.conf.js",
2324
"karma-jasmine:phantomjs": "karma start karma-build-jasmine-phantomjs.conf.js --single-run",
2425
"karma-jasmine:single": "karma start karma-build-jasmine.conf.js --single-run",
2526
"karma-jasmine:autoclose": "npm run karma-jasmine:single && npm run ws-client",
@@ -38,6 +39,7 @@
3839
"tsc:w": "tsc -w -p .",
3940
"tslint": "tslint -c tslint.json 'lib/**/*.ts'",
4041
"test": "npm run tsc && concurrently \"npm run tsc:w\" \"npm run ws-server\" \"npm run karma-jasmine\"",
42+
"test:es6": "npm run tsc && concurrently \"npm run tsc:w\" \"npm run ws-server\" \"npm run karma-jasmine:es6\"",
4143
"test:phantomjs": "npm run tsc && concurrently \"npm run tsc:w\" \"npm run ws-server\" \"npm run karma-jasmine:phantomjs\"",
4244
"test:phantomjs-single": "npm run tsc && concurrently \"npm run ws-server\" \"npm run karma-jasmine-phantomjs:autoclose\"",
4345
"test:single": "npm run tsc && concurrently \"npm run ws-server\" \"npm run karma-jasmine:autoclose\"",

sauce.es6.conf.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Sauce configuration
2+
3+
module.exports = function(config, ignoredLaunchers) {
4+
// The WS server is not available with Sauce
5+
config.files.unshift('test/saucelabs.js');
6+
7+
var basicLaunchers = {
8+
'SL_CHROME_66': {base: 'SauceLabs', browserName: 'chrome', version: '66'},
9+
'SL_FIREFOX_63': {base: 'SauceLabs', browserName: 'firefox', version: '63'},
10+
};
11+
12+
var customLaunchers = {};
13+
if (!ignoredLaunchers) {
14+
customLaunchers = basicLaunchers;
15+
} else {
16+
Object.keys(basicLaunchers).forEach(function(key) {
17+
if (ignoredLaunchers
18+
.filter(function(ignore) {
19+
return ignore === key;
20+
})
21+
.length === 0) {
22+
customLaunchers[key] = basicLaunchers[key];
23+
}
24+
});
25+
}
26+
27+
config.set({
28+
captureTimeout: 120000,
29+
browserNoActivityTimeout: 240000,
30+
31+
sauceLabs: {
32+
testName: 'Zone.js',
33+
startConnect: false,
34+
recordVideo: false,
35+
recordScreenshots: false,
36+
options: {
37+
'selenium-version': '2.53.0',
38+
'command-timeout': 600,
39+
'idle-timeout': 600,
40+
'max-duration': 5400
41+
}
42+
},
43+
44+
customLaunchers: customLaunchers,
45+
46+
browsers: Object.keys(customLaunchers),
47+
48+
reporters: ['dots', 'saucelabs'],
49+
50+
singleRun: true,
51+
52+
plugins: ['karma-*']
53+
});
54+
55+
if (process.env.TRAVIS) {
56+
config.sauceLabs.build =
57+
'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
58+
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
59+
60+
process.env.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY.split('').reverse().join('');
61+
}
62+
};
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/*
10+
* check that document.registerElement(name, { prototype: proto });
11+
* is properly patched
12+
*/
13+
14+
import {ifEnvSupports} from '../test-util';
15+
16+
function customElementsSupport() {
17+
return ('registerElement' in document);
18+
}
19+
customElementsSupport.message = 'window.customElements';
20+
21+
describe('customElements', ifEnvSupports(customElementsSupport, function() {
22+
const testZone = Zone.current.fork({name: 'test'});
23+
const callbacks = [
24+
'connectedCallback', 'disconnectedCallback', 'adoptedCallback',
25+
'attributeChangedCallback'
26+
];
27+
const bridge = {
28+
connectedCallback: () => {},
29+
disconnectedCallback: () => {},
30+
adoptedCallback: () => {},
31+
attributeChangedCallback: () => {}
32+
};
33+
34+
class TestCustomElement extends HTMLElement {
35+
constructor() {
36+
super();
37+
}
38+
39+
static get observedAttributes() {
40+
return ['attr1', 'attr2'];
41+
}
42+
43+
connectedCallback() {
44+
return bridge.connectedCallback();
45+
}
46+
47+
disconnectedCallback() {
48+
return bridge.disconnectedCallback();
49+
}
50+
51+
attributeChangedCallback(attrName, oldVal, newVal) {
52+
return bridge.attributeChangedCallback(attrName, oldVal, newVal);
53+
}
54+
55+
adoptedCallback() {
56+
return bridge.adoptedCallback();
57+
}
58+
}
59+
60+
testZone.run(() => {
61+
customElements.define('x-test', TestCustomElement);
62+
});
63+
64+
let elt;
65+
66+
beforeEach(() => {
67+
bridge.connectedCallback = () => {};
68+
bridge.disconnectedCallback = () => {};
69+
bridge.attributeChangedCallback = () => {};
70+
bridge.adoptedCallback = () => {};
71+
});
72+
73+
afterEach(() => {
74+
if (elt) {
75+
document.body.removeChild(elt);
76+
elt = null;
77+
}
78+
});
79+
80+
it('should work with connectedCallback', function(done) {
81+
bridge.connectedCallback = function() {
82+
expect(Zone.current.name).toBe(testZone.name);
83+
done();
84+
};
85+
86+
elt = document.createElement('x-test');
87+
document.body.appendChild(elt);
88+
});
89+
90+
it('should work with disconnectedCallback', function(done) {
91+
bridge.disconnectedCallback = function() {
92+
expect(Zone.current.name).toBe(testZone.name);
93+
done();
94+
};
95+
96+
elt = document.createElement('x-test');
97+
document.body.appendChild(elt);
98+
document.body.removeChild(elt);
99+
elt = null;
100+
});
101+
102+
it('should work with attributeChanged', function(done) {
103+
bridge.attributeChangedCallback = function(
104+
attrName, oldVal, newVal) {
105+
expect(Zone.current.name).toBe(testZone.name);
106+
expect(attrName).toEqual('attr1');
107+
expect(newVal).toEqual('value1');
108+
done();
109+
};
110+
111+
elt = document.createElement('x-test');
112+
document.body.appendChild(elt);
113+
elt.setAttribute('attr1', 'value1');
114+
});
115+
}));

test/browser_es6_entry_point.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import './browser/custom-element.spec';

test/main.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@ declare const __karma__: {
1414

1515
__karma__.loaded = function() {};
1616

17+
let entryPoint = 'browser_entry_point';
18+
1719
if (typeof __karma__ !== 'undefined') {
1820
(window as any)['__Zone_Error_BlacklistedStackFrames_policy'] =
1921
(__karma__ as any).config.errorpolicy;
22+
if ((__karma__ as any).config.entrypoint) {
23+
entryPoint = (__karma__ as any).config.entrypoint;
24+
}
2025
} else if (typeof process !== 'undefined') {
2126
(window as any)['__Zone_Error_BlacklistedStackFrames_policy'] = process.env.errorpolicy;
27+
if (process.env.entrypoint) {
28+
entryPoint = process.env.entrypoint;
29+
}
2230
}
2331

2432
(window as any).global = window;
@@ -54,7 +62,7 @@ browserPatchedPromise.then(() => {
5462
// Setup test environment
5563
System.import(testFrameworkPatch).then(() => {
5664
System.import('/base/build/lib/common/error-rewrite').then(() => {
57-
System.import('/base/build/test/browser_entry_point')
65+
System.import(`/base/build/test/${entryPoint}`)
5866
.then(
5967
() => {
6068
__karma__.start();

0 commit comments

Comments
 (0)