diff --git a/.circleci/config.yml b/.circleci/config.yml index 2dc3023c41..7da0ae3536 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -656,6 +656,18 @@ jobs: - run: yarn tsc -p packages - run: yarn tsc -p modules - run: yarn bazel build //packages/zone.js:npm_package + # Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling + # specific tests which are reliant on such generated fixtures is not an option as SystemJS + # in the Saucelabs legacy job always fetches referenced files, even if the imports would be + # guarded by an check to skip in the Saucelabs legacy job. We should be good running such + # test in all supported browsers on Saucelabs anyway until this job can be removed. + - run: + name: Preparing Bazel-generated fixtures required in legacy tests + command: | + yarn bazel build //packages/core/test:downleveled_es5_fixture + # Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`. + cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \ + dist/all/@angular/core/test/reflection/es5_downleveled_inheritance_fixture.js - run: # Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready. name: Waiting for Saucelabs tunnel to connect diff --git a/karma-js.conf.js b/karma-js.conf.js index bdab6ea463..6f97a611f8 100644 --- a/karma-js.conf.js +++ b/karma-js.conf.js @@ -37,6 +37,9 @@ module.exports = function(config) { 'node_modules/core-js/client/core.js', 'node_modules/jasmine-ajax/lib/mock-ajax.js', + + // Dependencies built by Bazel. See `config.yml` for steps running before + // the legacy Saucelabs tests run. 'dist/bin/packages/zone.js/npm_package/bundles/zone.umd.js', 'dist/bin/packages/zone.js/npm_package/bundles/zone-testing.umd.js', 'dist/bin/packages/zone.js/npm_package/bundles/task-tracking.umd.js', diff --git a/packages/core/src/reflection/reflection_capabilities.ts b/packages/core/src/reflection/reflection_capabilities.ts index cd5a155c40..b4880c4188 100644 --- a/packages/core/src/reflection/reflection_capabilities.ts +++ b/packages/core/src/reflection/reflection_capabilities.ts @@ -17,14 +17,45 @@ import {GetterFn, MethodFn, SetterFn} from './types'; -/** - * Attention: These regex has to hold even if the code is minified! +/* + * ######################### + * Attention: These Regular expressions have to hold even if the code is minified! + * ########################## */ -export const DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*arguments\)/; -export const INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/; -export const INHERITED_CLASS_WITH_CTOR = + +/** + * Regular expression that detects pass-through constructors for ES5 output. This Regex + * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also + * it intends to capture the pattern where existing constructors have been downleveled from + * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g. + * + * * ``` + * function MyClass() { + * var _this = _super.apply(this, arguments) || this; + * ``` + * + * ``` + * function MyClass() { + * var _this = _super.apply(this, __spread(arguments)) || this; + * ``` + * + * More details can be found in: https://github.com/angular/angular/issues/38453. + */ +export const ES5_DELEGATE_CTOR = + /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|[^()]+\(arguments\))\)/; +/** Regular expression that detects ES2015 classes which extend from other classes. */ +export const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/; +/** + * Regular expression that detects ES2015 classes which extend from other classes and + * have an explicit constructor defined. + */ +export const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/; -export const INHERITED_CLASS_WITH_DELEGATE_CTOR = +/** + * Regular expression that detects ES2015 classes which extend from other classes + * and inherit a constructor. + */ +export const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{\s*super\(\.\.\.arguments\)/; /** @@ -36,8 +67,9 @@ export const INHERITED_CLASS_WITH_DELEGATE_CTOR = * an initialized instance property. */ export function isDelegateCtor(typeStr: string): boolean { - return DELEGATE_CTOR.test(typeStr) || INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) || - (INHERITED_CLASS.test(typeStr) && !INHERITED_CLASS_WITH_CTOR.test(typeStr)); + return ES5_DELEGATE_CTOR.test(typeStr) || + ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) || + (ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr)); } export class ReflectionCapabilities implements PlatformReflectionCapabilities { diff --git a/packages/core/test/BUILD.bazel b/packages/core/test/BUILD.bazel index d5fc991815..8827492936 100644 --- a/packages/core/test/BUILD.bazel +++ b/packages/core/test/BUILD.bazel @@ -15,6 +15,23 @@ circular_dependency_test( deps = ["//packages/core/testing"], ) +genrule( + name = "downleveled_es5_fixture", + srcs = ["reflection/es2015_inheritance_fixture.ts"], + outs = ["reflection/es5_downleveled_inheritance_fixture.js"], + cmd = """ + es2015_out_dir="/tmp/downleveled_es5_fixture/" + es2015_out_file="$$es2015_out_dir/es2015_inheritance_fixture.js" + + # Build the ES2015 output and then downlevel it to ES5. + $(execpath @npm//typescript/bin:tsc) $< --outDir $$es2015_out_dir --target ES2015 \ + --types --module umd + $(execpath @npm//typescript/bin:tsc) --outFile $@ $$es2015_out_file --allowJs \ + --types --target ES5 + """, + tools = ["@npm//typescript/bin:tsc"], +) + ts_library( name = "test_lib", testonly = True, @@ -22,6 +39,7 @@ ts_library( ["**/*.ts"], exclude = [ "**/*_node_only_spec.ts", + "reflection/es2015_inheritance_fixture.ts", ], ), # Visible to //:saucelabs_unit_tests_poc target @@ -73,6 +91,9 @@ ts_library( jasmine_node_test( name = "test", bootstrap = ["//tools/testing:node_es5"], + data = [ + ":downleveled_es5_fixture", + ], shard_count = 4, deps = [ ":test_lib", @@ -87,6 +108,7 @@ jasmine_node_test( karma_web_test_suite( name = "test_web", + runtime_deps = [":downleveled_es5_fixture"], deps = [ ":test_lib", ], diff --git a/packages/core/test/reflection/es2015_inheritance_fixture.ts b/packages/core/test/reflection/es2015_inheritance_fixture.ts new file mode 100644 index 0000000000..d9ba165ace --- /dev/null +++ b/packages/core/test/reflection/es2015_inheritance_fixture.ts @@ -0,0 +1,22 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +// AMD module name is required so that this file can be loaded in the Karma tests. +/// + +class Parent {} + +export class ChildNoCtor extends Parent {} +export class ChildWithCtor extends Parent { + constructor() { + super(); + } +} +export class ChildNoCtorPrivateProps extends Parent { + x = 10; +} diff --git a/packages/core/test/reflection/reflector_spec.ts b/packages/core/test/reflection/reflector_spec.ts index 43b732b315..87123b7087 100644 --- a/packages/core/test/reflection/reflector_spec.ts +++ b/packages/core/test/reflection/reflector_spec.ts @@ -201,6 +201,16 @@ class TestObj { expect(isDelegateCtor(ChildWithCtor.toString())).toBe(false); }); + // See: https://github.com/angular/angular/issues/38453 + it('should support ES2015 downleveled classes', () => { + const {ChildNoCtor, ChildNoCtorPrivateProps, ChildWithCtor} = + require('./es5_downleveled_inheritance_fixture'); + + expect(isDelegateCtor(ChildNoCtor.toString())).toBe(true); + expect(isDelegateCtor(ChildNoCtorPrivateProps.toString())).toBe(true); + expect(isDelegateCtor(ChildWithCtor.toString())).toBe(false); + }); + it('should support ES2015 classes when minified', () => { // These classes are ES2015 in minified form const ChildNoCtorMinified = 'class ChildNoCtor extends Parent{}';