From 474332b28056541108c6c7b45c89f6b9a086e678 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 8 Oct 2026 11:22:39 -0400 Subject: [PATCH] Deprecate classic classes (RFC 1117) `extend`, `reopen` and `reopenClass` on `CoreObject` now give the `deprecate-classic-classes` deprecation. It is available in 7.5.0 and it is not enabled until the RFC is Ready for Release. Ember still applies framework mixins to its own classes. Those call sites use symbol-keyed methods (`INTERNAL_EXTEND`, `INTERNAL_REOPEN`, `INTERNAL_REOPEN_CLASS`) that do not deprecate. This is the pattern of `INTERNAL_MIXIN_CREATE` from the mixin deprecation. Tests: - Tests of the classic class system use `testUnless(isRemoved)` and the new `expectClassicClassDeprecation()` helper. - Tests of features that stay (router service, query params, locations) now use native classes. - `subclass()` on the application test case replaces a registration with a subclass of it. It replaces `this.router.reopen(...)`. - A quiet expectation in the call tracker adds no assertion on a match. `assert.expect` counts then stay the same in each test run. This continues #21580. Co-Authored-By: Claude Opus 5.5 --- .../@ember/-internals/deprecations/index.ts | 7 + .../-internals/glimmer/lib/component.ts | 8 +- .../components/attrs-lookup-test.js | 4 +- .../classic/curly-components-test.js | 27 +- .../components/target-action-test.js | 15 +- .../integration/components/tracked-test.js | 7 +- .../tests/integration/helpers/tracked-test.js | 13 +- .../-internals/metal/tests/computed_test.js | 15 +- .../metal/tests/native_desc_decorator_test.js | 44 +- .../-internals/runtime/tests/helpers/array.js | 146 +- .../system/object/concatenated_test.js | 248 ++-- .../runtime/tests/mixins/array_test.js | 705 +++++----- .../runtime/tests/mixins/comparable_test.js | 83 +- .../tests/mixins/container_proxy_test.js | 101 +- .../tests/mixins/promise_proxy_test.js | 1209 +++++++++-------- .../mixins/target_action_support_test.js | 96 +- .../tests/system/array_proxy/length_test.js | 5 +- .../runtime/tests/system/core_object_test.js | 9 +- .../tests/system/namespace/base_test.js | 14 +- .../runtime/tests/system/object_proxy_test.js | 9 +- .../utils/lib/internal-classic-class.ts | 14 + .../-internals/views/lib/views/core_view.ts | 3 +- packages/@ember/application/index.ts | 3 +- packages/@ember/array/proxy.ts | 3 +- packages/@ember/controller/index.ts | 3 +- .../controller/tests/controller_test.js | 25 +- packages/@ember/engine/index.ts | 8 +- packages/@ember/engine/instance.ts | 3 +- packages/@ember/object/core.ts | 44 + packages/@ember/object/index.ts | 3 +- .../tests/classic-classes-deprecation-test.js | 127 ++ .../computed/dependent-key-compat-test.js | 21 +- .../computed/reduce_computed_macros_test.js | 83 +- packages/@ember/object/tests/computed_test.js | 56 +- packages/@ember/object/tests/create_test.js | 19 +- packages/@ember/object/tests/destroy_test.js | 23 +- .../object/tests/es-compatibility-test.js | 17 +- packages/@ember/object/tests/evented_test.js | 16 +- packages/@ember/object/tests/events_test.js | 68 +- packages/@ember/object/tests/extend_test.js | 41 +- .../object/tests/mixin/deprecation_test.js | 38 +- .../@ember/object/tests/mixin/reopen_test.js | 12 +- .../@ember/object/tests/observable_test.js | 1126 +++++++-------- packages/@ember/object/tests/observer_test.js | 67 +- .../@ember/object/tests/reopenClass_test.js | 20 +- packages/@ember/object/tests/reopen_test.js | 26 +- .../@ember/routing/lib/routing-service.ts | 3 +- packages/@ember/routing/none-location.ts | 3 +- packages/@ember/routing/route.ts | 8 +- packages/@ember/routing/router.ts | 8 +- .../tests/location/history_location_test.js | 119 +- .../tests/location/none_location_test.js | 48 +- .../@ember/routing/tests/system/dsl_test.js | 30 +- packages/@ember/utils/tests/compare_test.js | 10 +- packages/@ember/utils/tests/type_of_test.js | 2 +- .../ember-testing/lib/adapters/adapter.ts | 3 +- .../tests/routing/decoupled_basic_test.js | 94 +- .../ember/tests/routing/query_params_test.js | 14 +- ..._dependent_state_with_query_params_test.js | 136 +- .../overlapping_query_params_test.js | 26 +- .../routing/router_service_test/basic_test.js | 21 +- .../router_service_test/recognize_test.js | 40 +- .../router_service_test/transitionTo_test.js | 119 +- .../ember/tests/service_injection_test.js | 14 +- packages/internal-test-helpers/index.ts | 1 + .../lib/classic-class-deprecation.ts | 20 + .../lib/ember-dev/deprecation.ts | 27 + .../lib/ember-dev/method-call-tracker.ts | 11 +- .../lib/test-cases/application.ts | 3 +- .../lib/test-cases/autoboot-application.ts | 3 +- .../lib/test-cases/query-param.ts | 5 +- .../lib/test-cases/rendering.ts | 5 +- .../lib/test-cases/router-non-application.ts | 3 +- .../test-cases/test-resolver-application.ts | 13 + 74 files changed, 3154 insertions(+), 2269 deletions(-) create mode 100644 packages/@ember/-internals/utils/lib/internal-classic-class.ts create mode 100644 packages/@ember/object/tests/classic-classes-deprecation-test.js create mode 100644 packages/internal-test-helpers/lib/classic-class-deprecation.ts diff --git a/packages/@ember/-internals/deprecations/index.ts b/packages/@ember/-internals/deprecations/index.ts index b8a83a7054d..b337f841b9c 100644 --- a/packages/@ember/-internals/deprecations/index.ts +++ b/packages/@ember/-internals/deprecations/index.ts @@ -168,6 +168,13 @@ export const DEPRECATIONS = { until: '8.0.0', url: 'https://deprecations.emberjs.com/id/deprecate-ember-utils', }), + DEPRECATE_CLASSIC_CLASSES: deprecation({ + id: 'deprecate-classic-classes', + for: 'ember-source', + since: { available: '7.5.0' }, + until: '8.0.0', + url: 'https://deprecations.emberjs.com/id/deprecate-classic-classes', + }), }; export function deprecateUntil(message: string, deprecation: DeprecationObject) { diff --git a/packages/@ember/-internals/glimmer/lib/component.ts b/packages/@ember/-internals/glimmer/lib/component.ts index 5c0735cd780..969ddb351f9 100644 --- a/packages/@ember/-internals/glimmer/lib/component.ts +++ b/packages/@ember/-internals/glimmer/lib/component.ts @@ -32,6 +32,10 @@ import { getComponentCapturedArgs, } from './component-managers/curly'; import hasDOM from '@ember/-internals/browser-environment/lib/has-dom'; +import { + INTERNAL_EXTEND, + INTERNAL_REOPEN_CLASS, +} from '@ember/-internals/utils/lib/internal-classic-class'; // Keep track of which component classes have already been processed for lazy event setup. let lazyEventsProcessed = new WeakMap>(); @@ -802,7 +806,7 @@ interface Component extends CoreView, TargetActionSupport, ActionSupport, ComponentMethods {} class Component - extends CoreView.extend( + extends CoreView[INTERNAL_EXTEND]( TargetActionSupport, ActionSupport, { @@ -1687,7 +1691,7 @@ class Component } // We continue to use reopenClass here so that positionalParams can be overridden with reopenClass in subclasses. -Component.reopenClass({ +Component[INTERNAL_REOPEN_CLASS]({ positionalParams: [], }); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/attrs-lookup-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/attrs-lookup-test.js index 631b532e336..451f7734ecd 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/attrs-lookup-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/attrs-lookup-test.js @@ -171,9 +171,7 @@ moduleFor( } }; - FooBarComponent.reopenClass({ - positionalParams: ['firstPositional'], - }); + FooBarComponent.positionalParams = ['firstPositional']; this.owner.register('component:foo-bar', FooBarComponent); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/classic/curly-components-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/classic/curly-components-test.js index 54acc8be5a5..3b7675e25ad 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/classic/curly-components-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/classic/curly-components-test.js @@ -7,6 +7,7 @@ import { runLoopSettled, expectDeprecation, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../../../deprecations'; import { action } from '@ember/object'; @@ -1968,7 +1969,11 @@ moduleFor( this.assertText('things'); } - async ['@test didReceiveAttrs fires after .init() but before observers become active'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test didReceiveAttrs fires after .init() but before observers become active`]( + assert + ) { + expectClassicClassDeprecation(); + let barCopyDidChangeCount = 0; this.owner.register( @@ -2042,7 +2047,7 @@ moduleFor( runTask(() => set(this.context, 'foo', 5)); } - [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test triggering an event only attempts to invoke an identically named method, if it actually is a function (GH#15228)`]( + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test triggering an event only attempts to invoke an identically named method, if it actually is a function (GH#15228)`]( assert ) { assert.expect(5); @@ -2051,6 +2056,8 @@ moduleFor( expectDeprecation( () => { + expectClassicClassDeprecation(); + this.owner.register( 'component:evented-component', Component.extend({ @@ -2111,7 +2118,9 @@ moduleFor( this.assertText('hello'); } - ['@test using attrs for positional params is asserted against']() { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test using attrs for positional params is asserted against`]() { + expectClassicClassDeprecation(); + let MyComponent = class extends Component {}; expectAssertion(() => { @@ -2129,16 +2138,20 @@ moduleFor( } // Perhaps change this test to `{{this.attrs.myVar.value}}` when removing the deprecation? - ['@test using this.attrs for positional params']() { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test using this.attrs for positional params`]() { + expectClassicClassDeprecation(); + let MyComponent = class extends Component {}; + MyComponent.reopenClass({ positionalParams: ['myVar'] }); + expectDeprecation(() => { this.owner.register( 'component:foo-bar', template( 'MyVar1: {{this.attrs.myVar}} {{this.myVar}} MyVar2: {{this.myVar2}} {{this.attrs.myVar2}}', { - component: MyComponent.reopenClass({ positionalParams: ['myVar'] }), + component: MyComponent, strictMode: false, } ) @@ -2150,7 +2163,9 @@ moduleFor( this.assertText('MyVar1: 1 1 MyVar2: 2 2'); } - ['@test using named arguments for positional params']() { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test using named arguments for positional params`]() { + expectClassicClassDeprecation(); + let MyComponent = class extends Component {}; this.owner.register( diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/target-action-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/target-action-test.js index 68bad76f0ac..93dd148c79b 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/target-action-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/target-action-test.js @@ -4,6 +4,7 @@ import { RenderingTestCase, runTask, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { action, set } from '@ember/object'; @@ -84,9 +85,9 @@ moduleFor( runTask(() => component.send('foo', 'baz')); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test a handled action can be bubbled to the target for continued processing`]() { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test a handled action can be bubbled to the target for continued processing`]() { + expectClassicClassDeprecation(); + this.assert.expect(3); expectDeprecation( @@ -124,9 +125,11 @@ moduleFor( runTask(() => component.send('poke')); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test action can be handled by a superclass' actions object`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test action can be handled by a superclass' actions object`]( + assert + ) { + expectClassicClassDeprecation(); + this.assert.expect(5); expectDeprecation( diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/tracked-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/tracked-test.js index 2d206d9db05..19a8ca536d8 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/tracked-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/tracked-test.js @@ -14,6 +14,7 @@ import { runTask, expectDeprecation, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { DEPRECATIONS } from '@ember/-internals/deprecations'; import GlimmerishComponent from '../../utils/glimmerish-component'; @@ -62,9 +63,9 @@ moduleFor( this.assertText('max jackson | max jackson'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_ARRAY_PROXY.isRemoved - )} @test creating an array proxy inside a tracking context does not trigger backtracking assertion`]() { + [`${testUnless(DEPRECATIONS.DEPRECATE_ARRAY_PROXY.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test creating an array proxy inside a tracking context does not trigger backtracking assertion`]() { + expectClassicClassDeprecation(); + expectDeprecation(/`ArrayProxy` is deprecated/, DEPRECATIONS.DEPRECATE_ARRAY_PROXY.isEnabled); expectDeprecation( /`PromiseProxyMixin` is deprecated/, diff --git a/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js b/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js index a2a12be40cc..c2f38aa9559 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js @@ -1,9 +1,16 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import EmberObject from '@ember/object'; import { A } from '@ember/array'; import MutableArray from '@ember/array/mutable'; import { tracked, notifyPropertyChange } from '@ember/-internals/metal'; import Service, { service } from '@ember/service'; -import { moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; +import { + moduleFor, + RenderingTestCase, + runTask, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@glimmer/manager'; @@ -172,7 +179,9 @@ moduleFor( this.assertText('1, 2, 3, 4'); } - '@test custom ember array properties rerender when updated'() { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test custom ember array properties rerender when updated`]() { + expectClassicClassDeprecation(); + let CustomArray = class extends EmberObject.extend(MutableArray) { init() { super.init(...arguments); diff --git a/packages/@ember/-internals/metal/tests/computed_test.js b/packages/@ember/-internals/metal/tests/computed_test.js index 1a20fc2166c..8470b98d817 100644 --- a/packages/@ember/-internals/metal/tests/computed_test.js +++ b/packages/@ember/-internals/metal/tests/computed_test.js @@ -1,3 +1,4 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import EmberObject from '@ember/object'; import { meta as metaFor } from '@ember/-internals/meta'; import { @@ -10,7 +11,13 @@ import { addObserver, } from '..'; import { run } from '@ember/runloop'; -import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + runLoopSettled, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; import { destroy } from '@glimmer/destroyable'; let obj, objA, objB, count, func; @@ -730,7 +737,11 @@ moduleFor( }, /Cannot override the computed property `aInt` on <\(unknown\):ember\d*>./); } - ['@test the return value of the setter gets cached'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test the return value of the setter gets cached`]( + assert + ) { + expectClassicClassDeprecation(); + let testObj = EmberObject.extend({ a: '1', sampleCP: computed('a', { diff --git a/packages/@ember/-internals/metal/tests/native_desc_decorator_test.js b/packages/@ember/-internals/metal/tests/native_desc_decorator_test.js index 9089b869f53..cb2401562ce 100644 --- a/packages/@ember/-internals/metal/tests/native_desc_decorator_test.js +++ b/packages/@ember/-internals/metal/tests/native_desc_decorator_test.js @@ -1,7 +1,12 @@ import EmberObject from '@ember/object'; import { defineProperty, nativeDescDecorator } from '..'; import Mixin from '@ember/object/mixin'; -import { moduleFor, AbstractTestCase, expectDeprecation } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + expectDeprecation, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; import { DEPRECATIONS } from '../../deprecations'; let classes = [ @@ -70,6 +75,8 @@ let classes = [ return `${title}: in EmberObject.extend()`; } + static usesClassicClasses = true; + constructor() { this.klass = null; this.props = {}; @@ -98,6 +105,8 @@ let classes = [ return `${title}: in EmberObject.extend() through a mixin`; } + static usesClassicClasses = true; + static usesMixins = true; constructor() { @@ -128,6 +137,8 @@ let classes = [ return `${title}: inherited from another EmberObject super class`; } + static usesClassicClasses = true; + constructor() { this.superklass = null; this.props = {}; @@ -154,6 +165,10 @@ let classes = [ classes .filter((TestClass) => !TestClass.usesMixins || !DEPRECATIONS.DEPRECATE_MIXINS.isRemoved) + .filter( + (TestClass) => + !TestClass.usesClassicClasses || !DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved + ) .forEach((TestClass) => { moduleFor( TestClass.module('@ember/-internals/metal/nativeDescDecorator'), @@ -165,6 +180,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); @@ -192,6 +210,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); factory.install( @@ -227,6 +248,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); factory.install('foo', nativeDescDecorator({ enumerable: true, value: 'bar' }), assert); @@ -247,6 +271,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); factory.install('foo', nativeDescDecorator({ enumerable: false, value: 'bar' }), assert); @@ -267,6 +294,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); factory.install('foo', nativeDescDecorator({ writable: true, value: 'bar' }), assert); @@ -293,6 +323,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); factory.install('foo', nativeDescDecorator({ writable: false, value: 'bar' }), assert); @@ -316,6 +349,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); factory.install( @@ -346,6 +382,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); factory.install( @@ -376,6 +415,9 @@ classes DEPRECATIONS.DEPRECATE_MIXINS.isEnabled ); } + if (TestClass.usesClassicClasses) { + expectClassicClassDeprecation(); + } let factory = new TestClass(assert); factory.install( diff --git a/packages/@ember/-internals/runtime/tests/helpers/array.js b/packages/@ember/-internals/runtime/tests/helpers/array.js index b86937a4dfc..4225c9f9586 100644 --- a/packages/@ember/-internals/runtime/tests/helpers/array.js +++ b/packages/@ember/-internals/runtime/tests/helpers/array.js @@ -115,6 +115,12 @@ const ArrayTestsObserverClass = class extends EmberObject { } }; +// These fixtures are built at import time, so a test cannot expect their +// deprecation. `runArrayTests` skips them after the removal. +function classicFixture(build) { + return DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved ? undefined : ignoreDeprecation(build); +} + class AbstractArrayHelper { beforeEach(assert) { this.assert = assert; @@ -172,75 +178,79 @@ class ArrayProxyHelpers extends AbstractArrayHelper { Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ -const TestArray = EmberObject.extend(EmberArray, { - _content: null, - - init() { - this._content = this._content || []; - }, - - // some methods to modify the array so we can test changes. Note that - // arrays can be modified even if they don't implement MutableArray. The - // MutableArray is just a standard API for mutation but not required. - addObject(obj) { - let idx = this._content.length; - arrayContentWillChange(this, idx, 0, 1); - this._content.push(obj); - arrayContentDidChange(this, idx, 0, 1); - }, - - removeFirst() { - arrayContentWillChange(this, 0, 1, 0); - this._content.shift(); - arrayContentDidChange(this, 0, 1, 0); - }, - - objectAt(idx) { - return this._content[idx]; - }, - - length: computed(function () { - return this._content.length; - }), -}); +const TestArray = classicFixture(() => + EmberObject.extend(EmberArray, { + _content: null, + + init() { + this._content = this._content || []; + }, + + // some methods to modify the array so we can test changes. Note that + // arrays can be modified even if they don't implement MutableArray. The + // MutableArray is just a standard API for mutation but not required. + addObject(obj) { + let idx = this._content.length; + arrayContentWillChange(this, idx, 0, 1); + this._content.push(obj); + arrayContentDidChange(this, idx, 0, 1); + }, + + removeFirst() { + arrayContentWillChange(this, 0, 1, 0); + this._content.shift(); + arrayContentDidChange(this, 0, 1, 0); + }, + + objectAt(idx) { + return this._content[idx]; + }, + + length: computed(function () { + return this._content.length; + }), + }) +); /* Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ -const TestMutableArray = EmberObject.extend(MutableArray, { - _content: null, - - init(ary = []) { - this._content = emberA(ary); - }, - - replace(idx, amt, objects) { - let args = objects ? objects.slice() : []; - let removeAmt = amt; - let addAmt = args.length; - - arrayContentWillChange(this, idx, removeAmt, addAmt); - - args.unshift(amt); - args.unshift(idx); - this._content.splice.apply(this._content, args); - arrayContentDidChange(this, idx, removeAmt, addAmt); - return this; - }, - - objectAt(idx) { - return this._content[idx]; - }, - - length: computed(function () { - return this._content.length; - }), - - slice() { - return this._content.slice(); - }, -}); +const TestMutableArray = classicFixture(() => + EmberObject.extend(MutableArray, { + _content: null, + + init(ary = []) { + this._content = emberA(ary); + }, + + replace(idx, amt, objects) { + let args = objects ? objects.slice() : []; + let removeAmt = amt; + let addAmt = args.length; + + arrayContentWillChange(this, idx, removeAmt, addAmt); + + args.unshift(amt); + args.unshift(idx); + this._content.splice.apply(this._content, args); + arrayContentDidChange(this, idx, removeAmt, addAmt); + return this; + }, + + objectAt(idx) { + return this._content[idx]; + }, + + length: computed(function () { + return this._content.length; + }), + + slice() { + return this._content.slice(); + }, + }) +); class MutableArrayHelpers extends NativeArrayHelpers { newObject(ary) { @@ -266,6 +276,7 @@ const ARRAY_TEST_HELPERS = { NativeArray: NativeArrayHelpers, }; +const CLASSIC_ARRAY_TEST_TYPES = ['EmberArray', 'MutableArray']; const DEFAULT_ARRAY_TEST_TYPES = ['ArrayProxy', 'EmberArray', 'MutableArray', 'NativeArray']; export function runArrayTests(name, Tests, ...types) { @@ -291,6 +302,13 @@ export function runArrayTests(name, Tests, ...types) { } for (let type of requested) { + if ( + CLASSIC_ARRAY_TEST_TYPES.includes(type) && + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved + ) { + continue; + } + moduleFor(`${type}: ${name}`, Tests, ARRAY_TEST_HELPERS[type]); } } diff --git a/packages/@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test.js b/packages/@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test.js index b20600f4be2..e26d3f87cad 100644 --- a/packages/@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test.js +++ b/packages/@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test.js @@ -1,5 +1,11 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import EmberObject, { get } from '@ember/object'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests @@ -19,112 +25,136 @@ function K() { let klass; -moduleFor( - 'EmberObject Concatenated Properties', - class extends AbstractTestCase { - beforeEach() { - klass = EmberObject.extend({ - concatenatedProperties: ['values', 'functions'], - values: ['a', 'b', 'c'], - functions: [K], - }); +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + 'EmberObject Concatenated Properties', + class extends AbstractTestCase { + beforeEach() { + expectClassicClassDeprecation(); + + klass = EmberObject.extend({ + concatenatedProperties: ['values', 'functions'], + values: ['a', 'b', 'c'], + functions: [K], + }); + } + + ['@test concatenates instances'](assert) { + let obj = klass.create({ + values: ['d', 'e', 'f'], + }); + + let values = get(obj, 'values'); + let expected = ['a', 'b', 'c', 'd', 'e', 'f']; + + assert.deepEqual( + values, + expected, + `should concatenate values property (expected: ${expected}, got: ${values})` + ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test concatenates subclasses`]( + assert + ) { + expectClassicClassDeprecation(); + + let subKlass = klass.extend({ + values: ['d', 'e', 'f'], + }); + let obj = subKlass.create(); + + let values = get(obj, 'values'); + let expected = ['a', 'b', 'c', 'd', 'e', 'f']; + + assert.deepEqual( + values, + expected, + `should concatenate values property (expected: ${expected}, got: ${values})` + ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test concatenates reopen`]( + assert + ) { + expectClassicClassDeprecation(); + + klass.reopen({ + values: ['d', 'e', 'f'], + }); + let obj = klass.create(); + + let values = get(obj, 'values'); + let expected = ['a', 'b', 'c', 'd', 'e', 'f']; + + assert.deepEqual( + values, + expected, + `should concatenate values property (expected: ${expected}, got: ${values})` + ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test concatenates mixin`]( + assert + ) { + expectClassicClassDeprecation(); + + let mixin = { + values: ['d', 'e'], + }; + let subKlass = klass.extend(mixin, { + values: ['f'], + }); + let obj = subKlass.create(); + + let values = get(obj, 'values'); + let expected = ['a', 'b', 'c', 'd', 'e', 'f']; + + assert.deepEqual( + values, + expected, + `should concatenate values property (expected: ${expected}, got: ${values})` + ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test concatenates reopen, subclass, and instance`]( + assert + ) { + expectClassicClassDeprecation(); + + klass.reopen({ values: ['d'] }); + let subKlass = klass.extend({ values: ['e'] }); + let obj = subKlass.create({ values: ['f'] }); + + let values = get(obj, 'values'); + let expected = ['a', 'b', 'c', 'd', 'e', 'f']; + + assert.deepEqual( + values, + expected, + `should concatenate values property (expected: ${expected}, got: ${values})` + ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test concatenates subclasses when the values are functions`]( + assert + ) { + expectClassicClassDeprecation(); + + let subKlass = klass.extend({ + functions: K, + }); + let obj = subKlass.create(); + + let values = get(obj, 'functions'); + let expected = [K, K]; + + assert.deepEqual( + values, + expected, + `should concatenate functions property (expected: ${expected}, got: ${values})` + ); + } } - - ['@test concatenates instances'](assert) { - let obj = klass.create({ - values: ['d', 'e', 'f'], - }); - - let values = get(obj, 'values'); - let expected = ['a', 'b', 'c', 'd', 'e', 'f']; - - assert.deepEqual( - values, - expected, - `should concatenate values property (expected: ${expected}, got: ${values})` - ); - } - - ['@test concatenates subclasses'](assert) { - let subKlass = klass.extend({ - values: ['d', 'e', 'f'], - }); - let obj = subKlass.create(); - - let values = get(obj, 'values'); - let expected = ['a', 'b', 'c', 'd', 'e', 'f']; - - assert.deepEqual( - values, - expected, - `should concatenate values property (expected: ${expected}, got: ${values})` - ); - } - - ['@test concatenates reopen'](assert) { - klass.reopen({ - values: ['d', 'e', 'f'], - }); - let obj = klass.create(); - - let values = get(obj, 'values'); - let expected = ['a', 'b', 'c', 'd', 'e', 'f']; - - assert.deepEqual( - values, - expected, - `should concatenate values property (expected: ${expected}, got: ${values})` - ); - } - - ['@test concatenates mixin'](assert) { - let mixin = { - values: ['d', 'e'], - }; - let subKlass = klass.extend(mixin, { - values: ['f'], - }); - let obj = subKlass.create(); - - let values = get(obj, 'values'); - let expected = ['a', 'b', 'c', 'd', 'e', 'f']; - - assert.deepEqual( - values, - expected, - `should concatenate values property (expected: ${expected}, got: ${values})` - ); - } - - ['@test concatenates reopen, subclass, and instance'](assert) { - klass.reopen({ values: ['d'] }); - let subKlass = klass.extend({ values: ['e'] }); - let obj = subKlass.create({ values: ['f'] }); - - let values = get(obj, 'values'); - let expected = ['a', 'b', 'c', 'd', 'e', 'f']; - - assert.deepEqual( - values, - expected, - `should concatenate values property (expected: ${expected}, got: ${values})` - ); - } - - ['@test concatenates subclasses when the values are functions'](assert) { - let subKlass = klass.extend({ - functions: K, - }); - let obj = subKlass.create(); - - let values = get(obj, 'functions'); - let expected = [K, K]; - - assert.deepEqual( - values, - expected, - `should concatenate functions property (expected: ${expected}, got: ${values})` - ); - } - } -); + ); +} diff --git a/packages/@ember/-internals/runtime/tests/mixins/array_test.js b/packages/@ember/-internals/runtime/tests/mixins/array_test.js index bf05aa13c7d..a7f04ba556e 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/array_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/array_test.js @@ -1,3 +1,4 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { objectAt, addObserver, @@ -8,385 +9,411 @@ import { } from '@ember/-internals/metal'; import EmberObject, { get, set, computed, observer as emberObserver } from '@ember/object'; import EmberArray, { A as emberA } from '@ember/array'; -import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; - -/* +import { + moduleFor, + AbstractTestCase, + runLoopSettled, + testUnless, + ignoreDeprecation, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; + +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + /* Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ -const TestArray = class extends EmberObject.extend(EmberArray) { - _content = null; - - init() { - this._content = this._content || []; - } - - // some methods to modify the array so we can test changes. Note that - // arrays can be modified even if they don't implement MutableArray. The - // MutableArray is just a standard API for mutation but not required. - addObject(obj) { - let idx = this._content.length; - arrayContentWillChange(this, idx, 0, 1); - this._content.push(obj); - arrayContentDidChange(this, idx, 0, 1); - } - - removeFirst() { - arrayContentWillChange(this, 0, 1, 0); - this._content.shift(); - arrayContentDidChange(this, 0, 1, 0); - } + // A test cannot expect a deprecation that fires at import time. + let ArrayObject = ignoreDeprecation(() => EmberObject.extend(EmberArray)); - objectAt(idx) { - return this._content[idx]; - } - - get length() { - return this._content.length; - } -}; - -moduleFor( - 'Ember.Array', - class extends AbstractTestCase { - ['@test the return value of slice has Ember.Array applied'](assert) { - let x = EmberObject.extend(EmberArray).create({ - length: 0, - }); - let y = x.slice(1); - assert.equal(EmberArray.detect(y), true, 'mixin should be applied'); - } + let TestArray = class extends ArrayObject { + _content = null; - ['@test slice supports negative index arguments'](assert) { - let testArray = TestArray.create({ _content: [1, 2, 3, 4] }); - - assert.deepEqual(testArray.slice(-2), [3, 4], 'slice(-2)'); - assert.deepEqual(testArray.slice(-2, -1), [3], 'slice(-2, -1'); - assert.deepEqual(testArray.slice(-2, -2), [], 'slice(-2, -2)'); - assert.deepEqual(testArray.slice(-1, -2), [], 'slice(-1, -2)'); - - assert.deepEqual(testArray.slice(-4, 1), [1], 'slice(-4, 1)'); - assert.deepEqual(testArray.slice(-4, 5), [1, 2, 3, 4], 'slice(-4, 5)'); - assert.deepEqual(testArray.slice(-4), [1, 2, 3, 4], 'slice(-4)'); - - assert.deepEqual(testArray.slice(0, -1), [1, 2, 3], 'slice(0, -1)'); - assert.deepEqual(testArray.slice(0, -4), [], 'slice(0, -4)'); - assert.deepEqual(testArray.slice(0, -3), [1], 'slice(0, -3)'); + init() { + this._content = this._content || []; } - } -); -// .......................................................... -// CONTENT DID CHANGE -// - -class DummyArray extends EmberObject.extend(EmberArray) { - length = 0; - objectAt(idx) { - return 'ITEM-' + idx; - } -} - -let obj, observer; - -// .......................................................... -// NOTIFY ARRAY OBSERVERS -// - -moduleFor( - 'mixins/array/arrayContent[Will|Did]Change', - class extends AbstractTestCase { - async ['@test should notify observers of []'](assert) { - obj = DummyArray.extend({ - enumerablePropertyDidChange: emberObserver('[]', function () { - this._count++; - }), - }).create({ - _count: 0, - }); - - assert.equal(obj._count, 0, 'should not have invoked yet'); - - arrayContentWillChange(obj, 0, 1, 1); - arrayContentDidChange(obj, 0, 1, 1); - await runLoopSettled(); - - assert.equal(obj._count, 1, 'should have invoked'); + // some methods to modify the array so we can test changes. Note that + // arrays can be modified even if they don't implement MutableArray. The + // MutableArray is just a standard API for mutation but not required. + addObject(obj) { + let idx = this._content.length; + arrayContentWillChange(this, idx, 0, 1); + this._content.push(obj); + arrayContentDidChange(this, idx, 0, 1); } - afterEach() { - obj.destroy(); - obj = undefined; - } - } -); - -// .......................................................... -// NOTIFY CHANGES TO LENGTH -// - -moduleFor( - 'notify observers of length', - class extends AbstractTestCase { - beforeEach(assert) { - obj = DummyArray.extend({ - lengthDidChange: emberObserver('length', function () { - this._after++; - }), - }).create({ - _after: 0, - }); - - assert.equal(obj._after, 0, 'should not have fired yet'); + removeFirst() { + arrayContentWillChange(this, 0, 1, 0); + this._content.shift(); + arrayContentDidChange(this, 0, 1, 0); } - afterEach() { - obj.destroy(); - obj = undefined; + objectAt(idx) { + return this._content[idx]; } - async ['@test should notify observers when call with no params'](assert) { - arrayContentWillChange(obj); - await runLoopSettled(); - - assert.equal(obj._after, 0); - - arrayContentDidChange(obj); - await runLoopSettled(); - - assert.equal(obj._after, 1); + get length() { + return this._content.length; } - - // API variation that included items only - async ['@test should not notify when passed lengths are same'](assert) { - arrayContentWillChange(obj, 0, 1, 1); - await runLoopSettled(); - - assert.equal(obj._after, 0); - - arrayContentDidChange(obj, 0, 1, 1); - await runLoopSettled(); - - assert.equal(obj._after, 0); + }; + + moduleFor( + 'Ember.Array', + class extends AbstractTestCase { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test the return value of slice has Ember.Array applied`]( + assert + ) { + expectClassicClassDeprecation(); + + let x = EmberObject.extend(EmberArray).create({ + length: 0, + }); + let y = x.slice(1); + assert.equal(EmberArray.detect(y), true, 'mixin should be applied'); + } + + ['@test slice supports negative index arguments'](assert) { + let testArray = TestArray.create({ _content: [1, 2, 3, 4] }); + + assert.deepEqual(testArray.slice(-2), [3, 4], 'slice(-2)'); + assert.deepEqual(testArray.slice(-2, -1), [3], 'slice(-2, -1'); + assert.deepEqual(testArray.slice(-2, -2), [], 'slice(-2, -2)'); + assert.deepEqual(testArray.slice(-1, -2), [], 'slice(-1, -2)'); + + assert.deepEqual(testArray.slice(-4, 1), [1], 'slice(-4, 1)'); + assert.deepEqual(testArray.slice(-4, 5), [1, 2, 3, 4], 'slice(-4, 5)'); + assert.deepEqual(testArray.slice(-4), [1, 2, 3, 4], 'slice(-4)'); + + assert.deepEqual(testArray.slice(0, -1), [1, 2, 3], 'slice(0, -1)'); + assert.deepEqual(testArray.slice(0, -4), [], 'slice(0, -4)'); + assert.deepEqual(testArray.slice(0, -3), [1], 'slice(0, -3)'); + } } + ); - async ['@test should notify when passed lengths are different'](assert) { - arrayContentWillChange(obj, 0, 1, 2); - await runLoopSettled(); - - assert.equal(obj._after, 0); - - arrayContentDidChange(obj, 0, 1, 2); - await runLoopSettled(); + // .......................................................... + // CONTENT DID CHANGE + // - assert.equal(obj._after, 1); + class DummyArray extends ArrayObject { + length = 0; + objectAt(idx) { + return 'ITEM-' + idx; } } -); - -// .......................................................... -// NOTIFY ARRAY OBSERVER -// - -moduleFor( - 'notify array observers (internal)', - class extends AbstractTestCase { - beforeEach(assert) { - obj = DummyArray.create(); - - observer = class extends EmberObject { - arrayWillChange() { - assert.equal(this._before, null); // should only call once - this._before = Array.prototype.slice.call(arguments); - } - - arrayDidChange() { - assert.equal(this._after, null); // should only call once - this._after = Array.prototype.slice.call(arguments); - } - }.create({ - _before: null, - _after: null, - }); - - addArrayObserver(obj, observer, { - willChange: 'arrayWillChange', - didChange: 'arrayDidChange', - }); - } - - afterEach() { - obj = observer = null; - } - - ['@test should notify array observers when called with no params'](assert) { - arrayContentWillChange(obj); - assert.deepEqual(observer._before, [obj, 0, -1, -1]); - arrayContentDidChange(obj); - assert.deepEqual(observer._after, [obj, 0, -1, -1]); + let obj, observer; + + // .......................................................... + // NOTIFY ARRAY OBSERVERS + // + + moduleFor( + 'mixins/array/arrayContent[Will|Did]Change', + class extends AbstractTestCase { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test should notify observers of []`]( + assert + ) { + expectClassicClassDeprecation(); + + obj = DummyArray.extend({ + enumerablePropertyDidChange: emberObserver('[]', function () { + this._count++; + }), + }).create({ + _count: 0, + }); + + assert.equal(obj._count, 0, 'should not have invoked yet'); + + arrayContentWillChange(obj, 0, 1, 1); + arrayContentDidChange(obj, 0, 1, 1); + await runLoopSettled(); + + assert.equal(obj._count, 1, 'should have invoked'); + } + + afterEach() { + obj.destroy(); + obj = undefined; + } } + ); - // API variation that included items only - ['@test should notify when called with same length items'](assert) { - arrayContentWillChange(obj, 0, 1, 1); - assert.deepEqual(observer._before, [obj, 0, 1, 1]); + // .......................................................... + // NOTIFY CHANGES TO LENGTH + // - arrayContentDidChange(obj, 0, 1, 1); - assert.deepEqual(observer._after, [obj, 0, 1, 1]); - } - - ['@test should notify when called with diff length items'](assert) { - arrayContentWillChange(obj, 0, 2, 1); - assert.deepEqual(observer._before, [obj, 0, 2, 1]); - - arrayContentDidChange(obj, 0, 2, 1); - assert.deepEqual(observer._after, [obj, 0, 2, 1]); - } + moduleFor( + 'notify observers of length', + class extends AbstractTestCase { + beforeEach(assert) { + expectClassicClassDeprecation(); - ['@test removing array observer should disable'](assert) { - removeArrayObserver(obj, observer, { - willChange: 'arrayWillChange', - didChange: 'arrayDidChange', - }); - arrayContentWillChange(obj); - assert.deepEqual(observer._before, null); + obj = DummyArray.extend({ + lengthDidChange: emberObserver('length', function () { + this._after++; + }), + }).create({ + _after: 0, + }); - arrayContentDidChange(obj); - assert.deepEqual(observer._after, null); - } - } -); - -// .......................................................... -// @each -// - -let ary; - -moduleFor( - 'EmberArray.@each support', - class extends AbstractTestCase { - beforeEach() { - ary = TestArray.create({ - _content: [ - { isDone: true, desc: 'Todo 1' }, - { isDone: false, desc: 'Todo 2' }, - { isDone: true, desc: 'Todo 3' }, - { isDone: false, desc: 'Todo 4' }, - ], - }); - } + assert.equal(obj._after, 0, 'should not have fired yet'); + } - afterEach() { - ary.destroy(); - ary = null; - } + afterEach() { + obj.destroy(); + obj = undefined; + } - async ['@test adding an object should notify (@each.isDone)'](assert) { - let called = 0; + async ['@test should notify observers when call with no params'](assert) { + arrayContentWillChange(obj); + await runLoopSettled(); - let observerObject = EmberObject.create({ - wasCalled() { - called++; - }, - }); + assert.equal(obj._after, 0); - addObserver(ary, '@each.isDone', observerObject, 'wasCalled'); + arrayContentDidChange(obj); + await runLoopSettled(); - ary.addObject( - EmberObject.create({ - desc: 'foo', - isDone: false, - }) - ); + assert.equal(obj._after, 1); + } - await runLoopSettled(); - assert.equal(called, 1, 'calls observer when object is pushed'); - } + // API variation that included items only + async ['@test should not notify when passed lengths are same'](assert) { + arrayContentWillChange(obj, 0, 1, 1); + await runLoopSettled(); - async ['@test using @each to observe arrays that does not return objects raise error'](assert) { - let called = 0; + assert.equal(obj._after, 0); - let observerObject = EmberObject.create({ - wasCalled() { - called++; - }, - }); + arrayContentDidChange(obj, 0, 1, 1); + await runLoopSettled(); - ary = TestArray.create({ - objectAt(idx) { - return get(this._content[idx], 'desc'); - }, - }); + assert.equal(obj._after, 0); + } - ary.addObject({ - desc: 'foo', - isDone: false, - }); + async ['@test should notify when passed lengths are different'](assert) { + arrayContentWillChange(obj, 0, 1, 2); + await runLoopSettled(); - assert.throwsAssertion(() => { - addObserver(ary, '@each.isDone', observerObject, 'wasCalled'); - }, /When using @each to observe the array/); + assert.equal(obj._after, 0); - await runLoopSettled(); - assert.equal(called, 0, 'not calls observer when object is pushed'); - } + arrayContentDidChange(obj, 0, 1, 2); + await runLoopSettled(); - ['@test `objectAt` returns correct object'](assert) { - let arr = ['first', 'second', 'third', 'fourth']; - assert.equal(objectAt(arr, 2), 'third'); - assert.equal(objectAt(arr, 4), undefined); + assert.equal(obj._after, 1); + } } - - ['@test should be clear caches for computed properties that have dependent keys on arrays that are changed after object initialization']( - assert - ) { - let obj = class extends EmberObject { - init() { - super.init(...arguments); - set(this, 'resources', emberA()); - } - - @computed('resources.@each.common') - get common() { - return get(objectAt(get(this, 'resources'), 0), 'common'); - } - }.create(); - - get(obj, 'resources').pushObject(EmberObject.create({ common: 'HI!' })); - assert.equal('HI!', get(obj, 'common')); - - set(objectAt(get(obj, 'resources'), 0), 'common', 'BYE!'); - assert.equal('BYE!', get(obj, 'common')); + ); + + // .......................................................... + // NOTIFY ARRAY OBSERVER + // + + moduleFor( + 'notify array observers (internal)', + class extends AbstractTestCase { + beforeEach(assert) { + obj = DummyArray.create(); + + observer = class extends EmberObject { + arrayWillChange() { + assert.equal(this._before, null); // should only call once + this._before = Array.prototype.slice.call(arguments); + } + + arrayDidChange() { + assert.equal(this._after, null); // should only call once + this._after = Array.prototype.slice.call(arguments); + } + }.create({ + _before: null, + _after: null, + }); + + addArrayObserver(obj, observer, { + willChange: 'arrayWillChange', + didChange: 'arrayDidChange', + }); + } + + afterEach() { + obj = observer = null; + } + + ['@test should notify array observers when called with no params'](assert) { + arrayContentWillChange(obj); + assert.deepEqual(observer._before, [obj, 0, -1, -1]); + + arrayContentDidChange(obj); + assert.deepEqual(observer._after, [obj, 0, -1, -1]); + } + + // API variation that included items only + ['@test should notify when called with same length items'](assert) { + arrayContentWillChange(obj, 0, 1, 1); + assert.deepEqual(observer._before, [obj, 0, 1, 1]); + + arrayContentDidChange(obj, 0, 1, 1); + assert.deepEqual(observer._after, [obj, 0, 1, 1]); + } + + ['@test should notify when called with diff length items'](assert) { + arrayContentWillChange(obj, 0, 2, 1); + assert.deepEqual(observer._before, [obj, 0, 2, 1]); + + arrayContentDidChange(obj, 0, 2, 1); + assert.deepEqual(observer._after, [obj, 0, 2, 1]); + } + + ['@test removing array observer should disable'](assert) { + removeArrayObserver(obj, observer, { + willChange: 'arrayWillChange', + didChange: 'arrayDidChange', + }); + arrayContentWillChange(obj); + assert.deepEqual(observer._before, null); + + arrayContentDidChange(obj); + assert.deepEqual(observer._after, null); + } } + ); + + // .......................................................... + // @each + // + + let ary; + + moduleFor( + 'EmberArray.@each support', + class extends AbstractTestCase { + beforeEach() { + ary = TestArray.create({ + _content: [ + { isDone: true, desc: 'Todo 1' }, + { isDone: false, desc: 'Todo 2' }, + { isDone: true, desc: 'Todo 3' }, + { isDone: false, desc: 'Todo 4' }, + ], + }); + } + + afterEach() { + ary.destroy(); + ary = null; + } + + async ['@test adding an object should notify (@each.isDone)'](assert) { + let called = 0; + + let observerObject = EmberObject.create({ + wasCalled() { + called++; + }, + }); - async ['@test observers that contain @each in the path should fire only once the first time they are accessed']( - assert - ) { - let count = 0; - - let obj = EmberObject.extend({ - init() { - this._super(...arguments); - // Observer does not fire on init - set(this, 'resources', emberA()); - }, - - commonDidChange: emberObserver('resources.@each.common', () => count++), - }).create(); - - // Observer fires first time when new object is added - get(obj, 'resources').pushObject(EmberObject.create({ common: 'HI!' })); - await runLoopSettled(); - - // Observer fires second time when property on an object is changed - set(objectAt(get(obj, 'resources'), 0), 'common', 'BYE!'); - await runLoopSettled(); - - assert.equal(count, 2, 'observers should be called twice'); + addObserver(ary, '@each.isDone', observerObject, 'wasCalled'); - obj.destroy(); + ary.addObject( + EmberObject.create({ + desc: 'foo', + isDone: false, + }) + ); + + await runLoopSettled(); + assert.equal(called, 1, 'calls observer when object is pushed'); + } + + async ['@test using @each to observe arrays that does not return objects raise error']( + assert + ) { + let called = 0; + + let observerObject = EmberObject.create({ + wasCalled() { + called++; + }, + }); + + ary = TestArray.create({ + objectAt(idx) { + return get(this._content[idx], 'desc'); + }, + }); + + ary.addObject({ + desc: 'foo', + isDone: false, + }); + + assert.throwsAssertion(() => { + addObserver(ary, '@each.isDone', observerObject, 'wasCalled'); + }, /When using @each to observe the array/); + + await runLoopSettled(); + assert.equal(called, 0, 'not calls observer when object is pushed'); + } + + ['@test `objectAt` returns correct object'](assert) { + let arr = ['first', 'second', 'third', 'fourth']; + assert.equal(objectAt(arr, 2), 'third'); + assert.equal(objectAt(arr, 4), undefined); + } + + ['@test should be clear caches for computed properties that have dependent keys on arrays that are changed after object initialization']( + assert + ) { + let obj = class extends EmberObject { + init() { + super.init(...arguments); + set(this, 'resources', emberA()); + } + + @computed('resources.@each.common') + get common() { + return get(objectAt(get(this, 'resources'), 0), 'common'); + } + }.create(); + + get(obj, 'resources').pushObject(EmberObject.create({ common: 'HI!' })); + assert.equal('HI!', get(obj, 'common')); + + set(objectAt(get(obj, 'resources'), 0), 'common', 'BYE!'); + assert.equal('BYE!', get(obj, 'common')); + } + + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observers that contain @each in the path should fire only once the first time they are accessed`]( + assert + ) { + expectClassicClassDeprecation(); + + let count = 0; + + let obj = EmberObject.extend({ + init() { + this._super(...arguments); + // Observer does not fire on init + set(this, 'resources', emberA()); + }, + + commonDidChange: emberObserver('resources.@each.common', () => count++), + }).create(); + + // Observer fires first time when new object is added + get(obj, 'resources').pushObject(EmberObject.create({ common: 'HI!' })); + await runLoopSettled(); + + // Observer fires second time when property on an object is changed + set(objectAt(get(obj, 'resources'), 0), 'common', 'BYE!'); + await runLoopSettled(); + + assert.equal(count, 2, 'observers should be called twice'); + + obj.destroy(); + } } - } -); + ); +} diff --git a/packages/@ember/-internals/runtime/tests/mixins/comparable_test.js b/packages/@ember/-internals/runtime/tests/mixins/comparable_test.js index 04212eb18a3..06d9b852322 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/comparable_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/comparable_test.js @@ -1,47 +1,58 @@ import EmberObject, { get } from '@ember/object'; import { compare } from '@ember/utils'; import Comparable from '../../lib/mixins/comparable'; -import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + expectDeprecation, + testUnless, + ignoreDeprecation, +} from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../deprecations'; -class Rectangle extends EmberObject.extend(Comparable) { - length = 0; - width = 0; +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + // A test cannot expect a deprecation that fires at import time. + let ComparableObject = ignoreDeprecation(() => EmberObject.extend(Comparable)); - area() { - return get(this, 'length') * get(this, 'width'); - } - - compare(a, b) { - return compare(a.area(), b.area()); - } -} - -let r1, r2; + class Rectangle extends ComparableObject { + length = 0; + width = 0; -const UTILS = DEPRECATIONS.DEPRECATE_EMBER_UTILS; - -moduleFor( - 'Comparable', - class extends AbstractTestCase { - beforeEach() { - expectDeprecation( - /The `Comparable` mixin is deprecated/, - DEPRECATIONS.DEPRECATE_COMPARABLE_MIXIN.isEnabled - ); - r1 = Rectangle.create({ length: 6, width: 12 }); - r2 = Rectangle.create({ length: 6, width: 13 }); + area() { + return get(this, 'length') * get(this, 'width'); } - [`${testUnless(DEPRECATIONS.DEPRECATE_COMPARABLE_MIXIN.isRemoved || UTILS.isRemoved)} @test should be comparable and return the correct result`]( - assert - ) { - expectDeprecation(/from `@ember\/utils` is deprecated/, UTILS.isEnabled); - - assert.equal(Comparable.detect(r1), true); - assert.equal(compare(r1, r1), 0); - assert.equal(compare(r1, r2), -1); - assert.equal(compare(r2, r1), 1); + compare(a, b) { + return compare(a.area(), b.area()); } } -); + + let r1, r2; + + let UTILS = DEPRECATIONS.DEPRECATE_EMBER_UTILS; + + moduleFor( + 'Comparable', + class extends AbstractTestCase { + beforeEach() { + expectDeprecation( + /The `Comparable` mixin is deprecated/, + DEPRECATIONS.DEPRECATE_COMPARABLE_MIXIN.isEnabled + ); + r1 = Rectangle.create({ length: 6, width: 12 }); + r2 = Rectangle.create({ length: 6, width: 13 }); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_COMPARABLE_MIXIN.isRemoved || UTILS.isRemoved)} @test should be comparable and return the correct result`]( + assert + ) { + expectDeprecation(/from `@ember\/utils` is deprecated/, UTILS.isEnabled); + + assert.equal(Comparable.detect(r1), true); + assert.equal(compare(r1, r1), 0); + assert.equal(compare(r1, r2), -1); + assert.equal(compare(r2, r1), 1); + } + } + ); +} diff --git a/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js b/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js index 1f9cbcfa175..62f3a8b2a6a 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js @@ -1,70 +1,75 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { getOwner } from '@ember/-internals/owner'; import { Container, Registry } from '@ember/-internals/container'; import ContainerProxy from '../../lib/mixins/container_proxy'; import EmberObject from '@ember/object'; import { run, schedule } from '@ember/runloop'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectClassicClassDeprecation } from 'internal-test-helpers'; import { destroy } from '@glimmer/destroyable'; -moduleFor( - '@ember/-internals/runtime/mixins/container_proxy', - class extends AbstractTestCase { - beforeEach() { - this.Owner = EmberObject.extend(ContainerProxy); - this.instance = this.Owner.create(); +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + '@ember/-internals/runtime/mixins/container_proxy', + class extends AbstractTestCase { + beforeEach() { + expectClassicClassDeprecation(); - this.registry = new Registry(); + this.Owner = EmberObject.extend(ContainerProxy); + this.instance = this.Owner.create(); - this.instance.__container__ = new Container(this.registry, { - owner: this.instance, - }); - } + this.registry = new Registry(); - ['@test provides ownerInjection helper method'](assert) { - let result = this.instance.ownerInjection(); + this.instance.__container__ = new Container(this.registry, { + owner: this.instance, + }); + } - assert.equal(getOwner(result), this.instance, 'returns an object with an associated owner'); - } + ['@test provides ownerInjection helper method'](assert) { + let result = this.instance.ownerInjection(); - ['@test actions queue completes before destruction'](assert) { - assert.expect(1); + assert.equal(getOwner(result), this.instance, 'returns an object with an associated owner'); + } - this.registry.register( - 'service:auth', - class extends EmberObject { - willDestroy() { - assert.ok(getOwner(this).lookup('service:auth'), 'can still lookup'); + ['@test actions queue completes before destruction'](assert) { + assert.expect(1); + + this.registry.register( + 'service:auth', + class extends EmberObject { + willDestroy() { + assert.ok(getOwner(this).lookup('service:auth'), 'can still lookup'); + } } - } - ); + ); - let service = this.instance.lookup('service:auth'); + let service = this.instance.lookup('service:auth'); - run(() => { - schedule('actions', service, 'destroy'); - this.instance.destroy(); - }); - } + run(() => { + schedule('actions', service, 'destroy'); + this.instance.destroy(); + }); + } - '@test being destroyed by @ember/destroyable properly destroys the container and created instances'( - assert - ) { - assert.expect(1); + '@test being destroyed by @ember/destroyable properly destroys the container and created instances'( + assert + ) { + assert.expect(1); - this.registry.register( - 'service:foo', - class FooService extends EmberObject { - willDestroy() { - assert.ok(true, 'is properly destroyed'); + this.registry.register( + 'service:foo', + class FooService extends EmberObject { + willDestroy() { + assert.ok(true, 'is properly destroyed'); + } } - } - ); + ); - this.instance.lookup('service:foo'); + this.instance.lookup('service:foo'); - run(() => { - destroy(this.instance); - }); + run(() => { + destroy(this.instance); + }); + } } - } -); + ); +} diff --git a/packages/@ember/-internals/runtime/tests/mixins/promise_proxy_test.js b/packages/@ember/-internals/runtime/tests/mixins/promise_proxy_test.js index 9efe631f1c1..fbdbc43fb76 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/promise_proxy_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/promise_proxy_test.js @@ -11,671 +11,676 @@ import { expectDeprecation, ignoreDeprecation, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { DEPRECATIONS } from '@ember/-internals/deprecations'; let ObjectPromiseProxy, proxy; -moduleFor( - 'Ember.PromiseProxy - ObjectProxy', - class extends AbstractTestCase { - beforeEach() { - ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin); - } +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + 'Ember.PromiseProxy - ObjectProxy', + class extends AbstractTestCase { + beforeEach() { + expectClassicClassDeprecation(); - expectProxyDeprecations() { - expectDeprecation( - /`ObjectProxy` is deprecated/, - DEPRECATIONS.DEPRECATE_OBJECT_PROXY.isEnabled - ); - expectDeprecation( - /`PromiseProxyMixin` is deprecated/, - DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isEnabled - ); - } + ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin); + } - afterEach() { - RSVP.on('error', onerrorDefault); - if (proxy) proxy.destroy(); - proxy = undefined; - } + expectProxyDeprecations() { + expectDeprecation( + /`ObjectProxy` is deprecated/, + DEPRECATIONS.DEPRECATE_OBJECT_PROXY.isEnabled + ); + expectDeprecation( + /`PromiseProxyMixin` is deprecated/, + DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isEnabled + ); + } - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test present on ember namespace`]( - assert - ) { - assert.ok(PromiseProxyMixin, 'expected PromiseProxyMixin to exist'); - } + afterEach() { + RSVP.on('error', onerrorDefault); + if (proxy) proxy.destroy(); + proxy = undefined; + } - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test no promise, invoking then should raise`]( - assert - ) { - this.expectProxyDeprecations(); + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test present on ember namespace`]( + assert + ) { + assert.ok(PromiseProxyMixin, 'expected PromiseProxyMixin to exist'); + } - proxy = ObjectPromiseProxy.create(); + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test no promise, invoking then should raise`]( + assert + ) { + this.expectProxyDeprecations(); + + proxy = ObjectPromiseProxy.create(); + + assert.throws(function () { + proxy.then( + function () { + return this; + }, + function () { + return this; + } + ); + }, new RegExp("PromiseProxy's promise must be set")); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test fulfillment`]( + assert + ) { + this.expectProxyDeprecations(); + + let value = { + firstName: 'stef', + lastName: 'penner', + }; + + let deferred = RSVP.defer(); + + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); + + let didFulfillCount = 0; + let didRejectCount = 0; - assert.throws(function () { proxy.then( - function () { - return this; - }, - function () { - return this; - } + () => didFulfillCount++, + () => didRejectCount++ ); - }, new RegExp("PromiseProxy's promise must be set")); - } - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test fulfillment`]( - assert - ) { - this.expectProxyDeprecations(); - - let value = { - firstName: 'stef', - lastName: 'penner', - }; - - let deferred = RSVP.defer(); - - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); - - let didFulfillCount = 0; - let didRejectCount = 0; - - proxy.then( - () => didFulfillCount++, - () => didRejectCount++ - ); - - assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); - assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); - assert.equal( - get(proxy, 'isPending'), - true, - 'expects the proxy to indicate that it is loading' - ); - assert.equal( - get(proxy, 'isSettled'), - false, - 'expects the proxy to indicate that it is not settled' - ); - assert.equal( - get(proxy, 'isRejected'), - false, - 'expects the proxy to indicate that it is not rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - - assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); - assert.equal(didRejectCount, 0, 'should not yet have been rejected'); - - run(deferred, 'resolve', value); - - assert.equal(didFulfillCount, 1, 'should have been fulfilled'); - assert.equal(didRejectCount, 0, 'should not have been rejected'); - - assert.equal(get(proxy, 'content'), value, 'expects the proxy to have content'); - assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to still have no reason'); - assert.equal( - get(proxy, 'isPending'), - false, - 'expects the proxy to indicate that it is no longer loading' - ); - assert.equal( - get(proxy, 'isSettled'), - true, - 'expects the proxy to indicate that it is settled' - ); - assert.equal( - get(proxy, 'isRejected'), - false, - 'expects the proxy to indicate that it is not rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - true, - 'expects the proxy to indicate that it is fulfilled' - ); - - run(deferred, 'resolve', value); - - assert.equal(didFulfillCount, 1, 'should still have been only fulfilled once'); - assert.equal(didRejectCount, 0, 'should still not have been rejected'); - - run(deferred, 'reject', value); - - assert.equal(didFulfillCount, 1, 'should still have been only fulfilled once'); - assert.equal(didRejectCount, 0, 'should still not have been rejected'); - - assert.equal( - get(proxy, 'content'), - value, - 'expects the proxy to have still have same content' - ); - assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy still to have no reason'); - assert.equal( - get(proxy, 'isPending'), - false, - 'expects the proxy to indicate that it is no longer loading' - ); - assert.equal( - get(proxy, 'isSettled'), - true, - 'expects the proxy to indicate that it is settled' - ); - assert.equal( - get(proxy, 'isRejected'), - false, - 'expects the proxy to indicate that it is not rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - true, - 'expects the proxy to indicate that it is fulfilled' - ); - - // rest of the promise semantics are tested in directly in RSVP - } + assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); + assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); + assert.equal( + get(proxy, 'isPending'), + true, + 'expects the proxy to indicate that it is loading' + ); + assert.equal( + get(proxy, 'isSettled'), + false, + 'expects the proxy to indicate that it is not settled' + ); + assert.equal( + get(proxy, 'isRejected'), + false, + 'expects the proxy to indicate that it is not rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test rejection`]( - assert - ) { - this.expectProxyDeprecations(); - - let reason = new Error('failure'); - let deferred = RSVP.defer(); - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); - - let didFulfillCount = 0; - let didRejectCount = 0; - - proxy.then( - () => didFulfillCount++, - () => didRejectCount++ - ); - - assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); - assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); - assert.equal( - get(proxy, 'isPending'), - true, - 'expects the proxy to indicate that it is loading' - ); - assert.equal( - get(proxy, 'isSettled'), - false, - 'expects the proxy to indicate that it is not settled' - ); - assert.equal( - get(proxy, 'isRejected'), - false, - 'expects the proxy to indicate that it is not rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - - assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); - assert.equal(didRejectCount, 0, 'should not yet have been rejected'); - - run(deferred, 'reject', reason); - - assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); - assert.equal(didRejectCount, 1, 'should have been rejected'); - - assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); - assert.equal(get(proxy, 'reason'), reason, 'expects the proxy to have a reason'); - assert.equal( - get(proxy, 'isPending'), - false, - 'expects the proxy to indicate that it is not longer loading' - ); - assert.equal( - get(proxy, 'isSettled'), - true, - 'expects the proxy to indicate that it is settled' - ); - assert.equal( - get(proxy, 'isRejected'), - true, - 'expects the proxy to indicate that it is rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - - run(deferred, 'reject', reason); - - assert.equal(didFulfillCount, 0, 'should stll not yet have been fulfilled'); - assert.equal(didRejectCount, 1, 'should still remain rejected'); - - run(deferred, 'resolve', 1); - - assert.equal(didFulfillCount, 0, 'should stll not yet have been fulfilled'); - assert.equal(didRejectCount, 1, 'should still remain rejected'); - - assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); - assert.equal(get(proxy, 'reason'), reason, 'expects the proxy to have a reason'); - assert.equal( - get(proxy, 'isPending'), - false, - 'expects the proxy to indicate that it is not longer loading' - ); - assert.equal( - get(proxy, 'isSettled'), - true, - 'expects the proxy to indicate that it is settled' - ); - assert.equal( - get(proxy, 'isRejected'), - true, - 'expects the proxy to indicate that it is rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - } + assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); + assert.equal(didRejectCount, 0, 'should not yet have been rejected'); - // https://github.com/emberjs/ember.js/issues/15694 - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test rejection without specifying reason`]( - assert - ) { - this.expectProxyDeprecations(); - - let deferred = RSVP.defer(); - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); - - let didFulfillCount = 0; - let didRejectCount = 0; - - proxy.then( - () => didFulfillCount++, - () => didRejectCount++ - ); - - assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); - assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); - assert.equal( - get(proxy, 'isPending'), - true, - 'expects the proxy to indicate that it is loading' - ); - assert.equal( - get(proxy, 'isSettled'), - false, - 'expects the proxy to indicate that it is not settled' - ); - assert.equal( - get(proxy, 'isRejected'), - false, - 'expects the proxy to indicate that it is not rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - - assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); - assert.equal(didRejectCount, 0, 'should not yet have been rejected'); - - run(deferred, 'reject'); - - assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); - assert.equal(didRejectCount, 1, 'should have been rejected'); - - assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); - assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to have a reason'); - assert.equal( - get(proxy, 'isPending'), - false, - 'expects the proxy to indicate that it is not longer loading' - ); - assert.equal( - get(proxy, 'isSettled'), - true, - 'expects the proxy to indicate that it is settled' - ); - assert.equal( - get(proxy, 'isRejected'), - true, - 'expects the proxy to indicate that it is rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - } + run(deferred, 'resolve', value); - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test unhandled rejects still propagate to RSVP.on('error', ...) `]( - assert - ) { - assert.expect(1); + assert.equal(didFulfillCount, 1, 'should have been fulfilled'); + assert.equal(didRejectCount, 0, 'should not have been rejected'); + + assert.equal(get(proxy, 'content'), value, 'expects the proxy to have content'); + assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to still have no reason'); + assert.equal( + get(proxy, 'isPending'), + false, + 'expects the proxy to indicate that it is no longer loading' + ); + assert.equal( + get(proxy, 'isSettled'), + true, + 'expects the proxy to indicate that it is settled' + ); + assert.equal( + get(proxy, 'isRejected'), + false, + 'expects the proxy to indicate that it is not rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + true, + 'expects the proxy to indicate that it is fulfilled' + ); + + run(deferred, 'resolve', value); + + assert.equal(didFulfillCount, 1, 'should still have been only fulfilled once'); + assert.equal(didRejectCount, 0, 'should still not have been rejected'); + + run(deferred, 'reject', value); + + assert.equal(didFulfillCount, 1, 'should still have been only fulfilled once'); + assert.equal(didRejectCount, 0, 'should still not have been rejected'); + + assert.equal( + get(proxy, 'content'), + value, + 'expects the proxy to have still have same content' + ); + assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy still to have no reason'); + assert.equal( + get(proxy, 'isPending'), + false, + 'expects the proxy to indicate that it is no longer loading' + ); + assert.equal( + get(proxy, 'isSettled'), + true, + 'expects the proxy to indicate that it is settled' + ); + assert.equal( + get(proxy, 'isRejected'), + false, + 'expects the proxy to indicate that it is not rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + true, + 'expects the proxy to indicate that it is fulfilled' + ); - RSVP.on('error', onerror); - RSVP.off('error', onerrorDefault); + // rest of the promise semantics are tested in directly in RSVP + } - let expectedReason = new Error('failure'); - let deferred = RSVP.defer(); + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test rejection`]( + assert + ) { + this.expectProxyDeprecations(); - // This test asserts an exact assertion count, so the proxy deprecations - // are silenced here rather than expected; they are covered by the other - // tests in this module. - proxy = ignoreDeprecation(() => - ObjectPromiseProxy.create({ + let reason = new Error('failure'); + let deferred = RSVP.defer(); + proxy = ObjectPromiseProxy.create({ promise: deferred.promise, - }) - ); + }); + + let didFulfillCount = 0; + let didRejectCount = 0; + + proxy.then( + () => didFulfillCount++, + () => didRejectCount++ + ); + + assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); + assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); + assert.equal( + get(proxy, 'isPending'), + true, + 'expects the proxy to indicate that it is loading' + ); + assert.equal( + get(proxy, 'isSettled'), + false, + 'expects the proxy to indicate that it is not settled' + ); + assert.equal( + get(proxy, 'isRejected'), + false, + 'expects the proxy to indicate that it is not rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); + + assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); + assert.equal(didRejectCount, 0, 'should not yet have been rejected'); + + run(deferred, 'reject', reason); + + assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); + assert.equal(didRejectCount, 1, 'should have been rejected'); + + assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); + assert.equal(get(proxy, 'reason'), reason, 'expects the proxy to have a reason'); + assert.equal( + get(proxy, 'isPending'), + false, + 'expects the proxy to indicate that it is not longer loading' + ); + assert.equal( + get(proxy, 'isSettled'), + true, + 'expects the proxy to indicate that it is settled' + ); + assert.equal( + get(proxy, 'isRejected'), + true, + 'expects the proxy to indicate that it is rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); + + run(deferred, 'reject', reason); + + assert.equal(didFulfillCount, 0, 'should stll not yet have been fulfilled'); + assert.equal(didRejectCount, 1, 'should still remain rejected'); + + run(deferred, 'resolve', 1); - proxy.get('promise'); + assert.equal(didFulfillCount, 0, 'should stll not yet have been fulfilled'); + assert.equal(didRejectCount, 1, 'should still remain rejected'); - function onerror(reason) { - assert.equal(reason, expectedReason, 'expected reason'); + assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); + assert.equal(get(proxy, 'reason'), reason, 'expects the proxy to have a reason'); + assert.equal( + get(proxy, 'isPending'), + false, + 'expects the proxy to indicate that it is not longer loading' + ); + assert.equal( + get(proxy, 'isSettled'), + true, + 'expects the proxy to indicate that it is settled' + ); + assert.equal( + get(proxy, 'isRejected'), + true, + 'expects the proxy to indicate that it is rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); } - RSVP.on('error', onerror); - RSVP.off('error', onerrorDefault); + // https://github.com/emberjs/ember.js/issues/15694 + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test rejection without specifying reason`]( + assert + ) { + this.expectProxyDeprecations(); - run(deferred, 'reject', expectedReason); + let deferred = RSVP.defer(); + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); - RSVP.on('error', onerrorDefault); - RSVP.off('error', onerror); + let didFulfillCount = 0; + let didRejectCount = 0; - run(deferred, 'reject', expectedReason); + proxy.then( + () => didFulfillCount++, + () => didRejectCount++ + ); - RSVP.on('error', onerrorDefault); - RSVP.off('error', onerror); - } + assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); + assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to have no reason'); + assert.equal( + get(proxy, 'isPending'), + true, + 'expects the proxy to indicate that it is loading' + ); + assert.equal( + get(proxy, 'isSettled'), + false, + 'expects the proxy to indicate that it is not settled' + ); + assert.equal( + get(proxy, 'isRejected'), + false, + 'expects the proxy to indicate that it is not rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should work with promise inheritance`]( - assert - ) { - this.expectProxyDeprecations(); + assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); + assert.equal(didRejectCount, 0, 'should not yet have been rejected'); - class PromiseSubclass extends RSVP.Promise {} + run(deferred, 'reject'); - proxy = ObjectPromiseProxy.create({ - promise: new PromiseSubclass(() => {}), - }); + assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled'); + assert.equal(didRejectCount, 1, 'should have been rejected'); - assert.ok(proxy.then() instanceof PromiseSubclass, 'promise proxy respected inheritance'); - } + assert.equal(get(proxy, 'content'), undefined, 'expects the proxy to have no content'); + assert.equal(get(proxy, 'reason'), undefined, 'expects the proxy to have a reason'); + assert.equal( + get(proxy, 'isPending'), + false, + 'expects the proxy to indicate that it is not longer loading' + ); + assert.equal( + get(proxy, 'isSettled'), + true, + 'expects the proxy to indicate that it is settled' + ); + assert.equal( + get(proxy, 'isRejected'), + true, + 'expects the proxy to indicate that it is rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); + } - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should reset isFulfilled and isRejected when promise is reset`]( - assert - ) { - this.expectProxyDeprecations(); - - let deferred = EmberRSVP.defer(); - - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); - - assert.equal( - get(proxy, 'isPending'), - true, - 'expects the proxy to indicate that it is loading' - ); - assert.equal( - get(proxy, 'isSettled'), - false, - 'expects the proxy to indicate that it is not settled' - ); - assert.equal( - get(proxy, 'isRejected'), - false, - 'expects the proxy to indicate that it is not rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - - run(deferred, 'resolve'); - - assert.equal( - get(proxy, 'isPending'), - false, - 'expects the proxy to indicate that it is no longer loading' - ); - assert.equal( - get(proxy, 'isSettled'), - true, - 'expects the proxy to indicate that it is settled' - ); - assert.equal( - get(proxy, 'isRejected'), - false, - 'expects the proxy to indicate that it is not rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - true, - 'expects the proxy to indicate that it is fulfilled' - ); - - let anotherDeferred = EmberRSVP.defer(); - proxy.set('promise', anotherDeferred.promise); - - assert.equal( - get(proxy, 'isPending'), - true, - 'expects the proxy to indicate that it is loading' - ); - assert.equal( - get(proxy, 'isSettled'), - false, - 'expects the proxy to indicate that it is not settled' - ); - assert.equal( - get(proxy, 'isRejected'), - false, - 'expects the proxy to indicate that it is not rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - - run(anotherDeferred, 'reject'); - - assert.equal( - get(proxy, 'isPending'), - false, - 'expects the proxy to indicate that it is not longer loading' - ); - assert.equal( - get(proxy, 'isSettled'), - true, - 'expects the proxy to indicate that it is settled' - ); - assert.equal( - get(proxy, 'isRejected'), - true, - 'expects the proxy to indicate that it is rejected' - ); - assert.equal( - get(proxy, 'isFulfilled'), - false, - 'expects the proxy to indicate that it is not fulfilled' - ); - } + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test unhandled rejects still propagate to RSVP.on('error', ...) `]( + assert + ) { + assert.expect(1); - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should have content when isFulfilled is set`]( - assert - ) { - this.expectProxyDeprecations(); + RSVP.on('error', onerror); + RSVP.off('error', onerrorDefault); - let deferred = EmberRSVP.defer(); + let expectedReason = new Error('failure'); + let deferred = RSVP.defer(); - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); + // This test asserts an exact assertion count, so the proxy deprecations + // are silenced here rather than expected; they are covered by the other + // tests in this module. + proxy = ignoreDeprecation(() => + ObjectPromiseProxy.create({ + promise: deferred.promise, + }) + ); - proxy.addObserver('isFulfilled', () => assert.equal(get(proxy, 'content'), true)); + proxy.get('promise'); - run(deferred, 'resolve', true); - } + function onerror(reason) { + assert.equal(reason, expectedReason, 'expected reason'); + } - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should have reason when isRejected is set`]( - assert - ) { - this.expectProxyDeprecations(); + RSVP.on('error', onerror); + RSVP.off('error', onerrorDefault); - let error = new Error('Y U REJECT?!?'); - let deferred = EmberRSVP.defer(); + run(deferred, 'reject', expectedReason); - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); + RSVP.on('error', onerrorDefault); + RSVP.off('error', onerror); - proxy.addObserver('isRejected', () => assert.equal(get(proxy, 'reason'), error)); + run(deferred, 'reject', expectedReason); - try { - run(deferred, 'reject', error); - } catch (e) { - assert.equal(e, error); + RSVP.on('error', onerrorDefault); + RSVP.off('error', onerror); } - } - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should not error if promise is resolved after proxy has been destroyed`]( - assert - ) { - this.expectProxyDeprecations(); + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should work with promise inheritance`]( + assert + ) { + this.expectProxyDeprecations(); - let deferred = EmberRSVP.defer(); + class PromiseSubclass extends RSVP.Promise {} - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); + proxy = ObjectPromiseProxy.create({ + promise: new PromiseSubclass(() => {}), + }); - proxy.then( - () => {}, - () => {} - ); + assert.ok(proxy.then() instanceof PromiseSubclass, 'promise proxy respected inheritance'); + } - run(proxy, 'destroy'); + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should reset isFulfilled and isRejected when promise is reset`]( + assert + ) { + this.expectProxyDeprecations(); - run(deferred, 'resolve', true); + let deferred = EmberRSVP.defer(); - assert.ok( - true, - 'resolving the promise after the proxy has been destroyed does not raise an error' - ); - } + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should not error if promise is rejected after proxy has been destroyed`]( - assert - ) { - this.expectProxyDeprecations(); + assert.equal( + get(proxy, 'isPending'), + true, + 'expects the proxy to indicate that it is loading' + ); + assert.equal( + get(proxy, 'isSettled'), + false, + 'expects the proxy to indicate that it is not settled' + ); + assert.equal( + get(proxy, 'isRejected'), + false, + 'expects the proxy to indicate that it is not rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); - let deferred = EmberRSVP.defer(); + run(deferred, 'resolve'); - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); + assert.equal( + get(proxy, 'isPending'), + false, + 'expects the proxy to indicate that it is no longer loading' + ); + assert.equal( + get(proxy, 'isSettled'), + true, + 'expects the proxy to indicate that it is settled' + ); + assert.equal( + get(proxy, 'isRejected'), + false, + 'expects the proxy to indicate that it is not rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + true, + 'expects the proxy to indicate that it is fulfilled' + ); - proxy.then( - () => {}, - () => {} - ); + let anotherDeferred = EmberRSVP.defer(); + proxy.set('promise', anotherDeferred.promise); - run(proxy, 'destroy'); + assert.equal( + get(proxy, 'isPending'), + true, + 'expects the proxy to indicate that it is loading' + ); + assert.equal( + get(proxy, 'isSettled'), + false, + 'expects the proxy to indicate that it is not settled' + ); + assert.equal( + get(proxy, 'isRejected'), + false, + 'expects the proxy to indicate that it is not rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); - run(deferred, 'reject', 'some reason'); + run(anotherDeferred, 'reject'); - assert.ok( - true, - 'rejecting the promise after the proxy has been destroyed does not raise an error' - ); - } + assert.equal( + get(proxy, 'isPending'), + false, + 'expects the proxy to indicate that it is not longer loading' + ); + assert.equal( + get(proxy, 'isSettled'), + true, + 'expects the proxy to indicate that it is settled' + ); + assert.equal( + get(proxy, 'isRejected'), + true, + 'expects the proxy to indicate that it is rejected' + ); + assert.equal( + get(proxy, 'isFulfilled'), + false, + 'expects the proxy to indicate that it is not fulfilled' + ); + } - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test promise chain is not broken if promised is resolved after proxy has been destroyed`]( - assert - ) { - this.expectProxyDeprecations(); - - let deferred = EmberRSVP.defer(); - let expectedValue = {}; - let receivedValue; - let didResolveCount = 0; - - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); - - proxy.then( - (value) => { - receivedValue = value; - didResolveCount++; - }, - () => {} - ); - - run(proxy, 'destroy'); - - run(deferred, 'resolve', expectedValue); - - assert.equal(didResolveCount, 1, 'callback called'); - assert.equal( - receivedValue, - expectedValue, - 'passed value is the value the promise was resolved with' - ); - } + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should have content when isFulfilled is set`]( + assert + ) { + this.expectProxyDeprecations(); + + let deferred = EmberRSVP.defer(); + + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); + + proxy.addObserver('isFulfilled', () => assert.equal(get(proxy, 'content'), true)); + + run(deferred, 'resolve', true); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should have reason when isRejected is set`]( + assert + ) { + this.expectProxyDeprecations(); + + let error = new Error('Y U REJECT?!?'); + let deferred = EmberRSVP.defer(); - [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test promise chain is not broken if promised is rejected after proxy has been destroyed`]( - assert - ) { - this.expectProxyDeprecations(); - - let deferred = EmberRSVP.defer(); - let expectedReason = 'some reason'; - let receivedReason; - let didRejectCount = 0; - - proxy = ObjectPromiseProxy.create({ - promise: deferred.promise, - }); - - proxy.then( - () => {}, - (reason) => { - receivedReason = reason; - didRejectCount++; + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); + + proxy.addObserver('isRejected', () => assert.equal(get(proxy, 'reason'), error)); + + try { + run(deferred, 'reject', error); + } catch (e) { + assert.equal(e, error); } - ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should not error if promise is resolved after proxy has been destroyed`]( + assert + ) { + this.expectProxyDeprecations(); - run(proxy, 'destroy'); + let deferred = EmberRSVP.defer(); + + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); + + proxy.then( + () => {}, + () => {} + ); - run(deferred, 'reject', expectedReason); + run(proxy, 'destroy'); - assert.equal(didRejectCount, 1, 'callback called'); - assert.equal( - receivedReason, - expectedReason, - 'passed reason is the reason the promise was rejected for' - ); + run(deferred, 'resolve', true); + + assert.ok( + true, + 'resolving the promise after the proxy has been destroyed does not raise an error' + ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test should not error if promise is rejected after proxy has been destroyed`]( + assert + ) { + this.expectProxyDeprecations(); + + let deferred = EmberRSVP.defer(); + + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); + + proxy.then( + () => {}, + () => {} + ); + + run(proxy, 'destroy'); + + run(deferred, 'reject', 'some reason'); + + assert.ok( + true, + 'rejecting the promise after the proxy has been destroyed does not raise an error' + ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test promise chain is not broken if promised is resolved after proxy has been destroyed`]( + assert + ) { + this.expectProxyDeprecations(); + + let deferred = EmberRSVP.defer(); + let expectedValue = {}; + let receivedValue; + let didResolveCount = 0; + + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); + + proxy.then( + (value) => { + receivedValue = value; + didResolveCount++; + }, + () => {} + ); + + run(proxy, 'destroy'); + + run(deferred, 'resolve', expectedValue); + + assert.equal(didResolveCount, 1, 'callback called'); + assert.equal( + receivedValue, + expectedValue, + 'passed value is the value the promise was resolved with' + ); + } + + [`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test promise chain is not broken if promised is rejected after proxy has been destroyed`]( + assert + ) { + this.expectProxyDeprecations(); + + let deferred = EmberRSVP.defer(); + let expectedReason = 'some reason'; + let receivedReason; + let didRejectCount = 0; + + proxy = ObjectPromiseProxy.create({ + promise: deferred.promise, + }); + + proxy.then( + () => {}, + (reason) => { + receivedReason = reason; + didRejectCount++; + } + ); + + run(proxy, 'destroy'); + + run(deferred, 'reject', expectedReason); + + assert.equal(didRejectCount, 1, 'callback called'); + assert.equal( + receivedReason, + expectedReason, + 'passed reason is the reason the promise was rejected for' + ); + } } - } -); + ); +} diff --git a/packages/@ember/-internals/runtime/tests/mixins/target_action_support_test.js b/packages/@ember/-internals/runtime/tests/mixins/target_action_support_test.js index 13fae5111ed..a053a8936c6 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/target_action_support_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/target_action_support_test.js @@ -1,7 +1,13 @@ import { context } from '@ember/-internals/environment'; import EmberObject from '@ember/object'; import TargetActionSupport from '../../lib/mixins/target_action_support'; -import { expectDeprecation, moduleFor, AbstractTestCase, testUnless } from 'internal-test-helpers'; +import { + expectDeprecation, + moduleFor, + AbstractTestCase, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../deprecations'; let originalLookup = context.lookup; @@ -18,9 +24,11 @@ moduleFor( context.lookup = originalLookup; } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should return false if no target or action are specified`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should return false if no target or action are specified`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(2); expectDeprecation( @@ -33,9 +41,11 @@ moduleFor( assert.ok(false === obj.triggerAction(), 'no target or action was specified'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should support actions specified as strings`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should support actions specified as strings`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( @@ -56,9 +66,11 @@ moduleFor( assert.ok(true === obj.triggerAction(), 'a valid target and action were specified'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should invoke the send() method on objects that implement it`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should invoke the send() method on objects that implement it`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(4); expectDeprecation( @@ -80,9 +92,11 @@ moduleFor( assert.ok(true === obj.triggerAction(), 'a valid target and action were specified'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should find targets specified using a property path`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should find targets specified using a property path`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( @@ -107,9 +121,11 @@ moduleFor( assert.ok(true === myObj.triggerAction(), 'a valid target and action were specified'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should use an actionContext object specified as a property on the object`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should use an actionContext object specified as a property on the object`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( @@ -133,9 +149,11 @@ moduleFor( assert.ok(true === obj.triggerAction(), 'a valid target and action were specified'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should find an actionContext specified as a property path`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should find an actionContext specified as a property path`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( @@ -160,9 +178,11 @@ moduleFor( assert.ok(true === obj.triggerAction(), 'a valid target and action were specified'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should use the target specified in the argument`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should use the target specified in the argument`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( @@ -185,9 +205,11 @@ moduleFor( ); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should use the action specified in the argument`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should use the action specified in the argument`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( @@ -209,9 +231,11 @@ moduleFor( ); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should use the actionContext specified in the argument`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should use the actionContext specified in the argument`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( @@ -235,9 +259,11 @@ moduleFor( ); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should allow multiple arguments from actionContext`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should allow multiple arguments from actionContext`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(4); expectDeprecation( @@ -269,9 +295,11 @@ moduleFor( ); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test it should use a null value specified in the actionContext argument`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it should use a null value specified in the actionContext argument`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( diff --git a/packages/@ember/-internals/runtime/tests/system/array_proxy/length_test.js b/packages/@ember/-internals/runtime/tests/system/array_proxy/length_test.js index 13408d36ee7..8e0b08c33a2 100644 --- a/packages/@ember/-internals/runtime/tests/system/array_proxy/length_test.js +++ b/packages/@ember/-internals/runtime/tests/system/array_proxy/length_test.js @@ -9,6 +9,7 @@ import { runLoopSettled, expectDeprecation, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { set, get } from '@ember/object'; import { createCache, getValue } from '@glimmer/validator'; @@ -183,9 +184,11 @@ moduleFor( assert.deepEqual(obj.content, ['foo'], 'content length was truncated'); } - async [`${testUnless(DEPRECATIONS.DEPRECATE_ARRAY_PROXY.isRemoved)} @test array proxy + aliasedProperty complex test`]( + async [`${testUnless(DEPRECATIONS.DEPRECATE_ARRAY_PROXY.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test array proxy + aliasedProperty complex test`]( assert ) { + expectClassicClassDeprecation(); + let aCalled, bCalled, cCalled, dCalled, eCalled; aCalled = bCalled = cCalled = dCalled = eCalled = 0; diff --git a/packages/@ember/-internals/runtime/tests/system/core_object_test.js b/packages/@ember/-internals/runtime/tests/system/core_object_test.js index 1649c657a4d..f8c7f69a694 100644 --- a/packages/@ember/-internals/runtime/tests/system/core_object_test.js +++ b/packages/@ember/-internals/runtime/tests/system/core_object_test.js @@ -1,3 +1,4 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { getOwner, setOwner } from '@ember/-internals/owner'; import { get, set, observer } from '@ember/object'; import CoreObject from '@ember/object/core'; @@ -7,6 +8,8 @@ import { buildOwner, runDestroy, runLoopSettled, + testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { track } from '@glimmer/validator'; import { destroy } from '@glimmer/destroyable'; @@ -97,7 +100,11 @@ moduleFor( TestObj.create(options); } - async ['@test observed properties are enumerable when set GH#14594'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observed properties are enumerable when set GH#14594`]( + assert + ) { + expectClassicClassDeprecation(); + let callCount = 0; let Test = CoreObject.extend({ myProp: null, diff --git a/packages/@ember/-internals/runtime/tests/system/namespace/base_test.js b/packages/@ember/-internals/runtime/tests/system/namespace/base_test.js index a1f72771eba..874acbd122a 100644 --- a/packages/@ember/-internals/runtime/tests/system/namespace/base_test.js +++ b/packages/@ember/-internals/runtime/tests/system/namespace/base_test.js @@ -1,10 +1,16 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { context } from '@ember/-internals/environment'; import { run } from '@ember/runloop'; import { get, setNamespaceSearchDisabled } from '@ember/-internals/metal'; import { guidFor, getName } from '@ember/-internals/utils'; import EmberObject from '@ember/object'; import Namespace from '@ember/application/namespace'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + expectClassicClassDeprecation, + testUnless, +} from 'internal-test-helpers'; const originalLookup = context.lookup; let lookup; @@ -59,7 +65,11 @@ moduleFor( ); } - ['@test Classes under an Namespace are properly named'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Classes under an Namespace are properly named`]( + assert + ) { + expectClassicClassDeprecation(); + let nsA = (lookup.NamespaceA = Namespace.create()); nsA.Foo = EmberObject.extend(); Namespace.processAll(); diff --git a/packages/@ember/-internals/runtime/tests/system/object_proxy_test.js b/packages/@ember/-internals/runtime/tests/system/object_proxy_test.js index 12a29167a3d..2ba7826ce3a 100644 --- a/packages/@ember/-internals/runtime/tests/system/object_proxy_test.js +++ b/packages/@ember/-internals/runtime/tests/system/object_proxy_test.js @@ -8,6 +8,7 @@ import { runLoopSettled, expectDeprecation, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { DEPRECATIONS } from '@ember/-internals/deprecations'; @@ -167,9 +168,11 @@ moduleFor( assert.equal(JSON.stringify(proxy), JSON.stringify({ content: 'hello' })); } - [`${testUnless(DEPRECATIONS.DEPRECATE_OBJECT_PROXY.isRemoved)} @test setting a property on the proxy's prototype avoids the assertion`]( + [`${testUnless(DEPRECATIONS.DEPRECATE_OBJECT_PROXY.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test setting a property on the proxy's prototype avoids the assertion`]( assert ) { + expectClassicClassDeprecation(); + let proxy = ObjectProxy.extend({ toJSON: null, }).create({ @@ -373,9 +376,11 @@ moduleFor( ); } - [`${testUnless(DEPRECATIONS.DEPRECATE_OBJECT_PROXY.isRemoved)} @test should not throw or deprecate when adding an observer to an ObjectProxy based class`]( + [`${testUnless(DEPRECATIONS.DEPRECATE_OBJECT_PROXY.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test should not throw or deprecate when adding an observer to an ObjectProxy based class`]( assert ) { + expectClassicClassDeprecation(); + let obj = ObjectProxy.extend({ observe: observer('foo', function () {}), }).create(); diff --git a/packages/@ember/-internals/utils/lib/internal-classic-class.ts b/packages/@ember/-internals/utils/lib/internal-classic-class.ts new file mode 100644 index 00000000000..f7118cb8b76 --- /dev/null +++ b/packages/@ember/-internals/utils/lib/internal-classic-class.ts @@ -0,0 +1,14 @@ +/** + Keys for the internal versions of the classic class APIs. + + Ember's own classes still apply framework mixins such as `Observable` and + `ActionHandler`, so they need `extend`, `reopen` and `reopenClass` without + the classic class deprecation. These are Symbols so that they do not show + up as discoverable property names and cannot be reached by name from + application code. + + @private +*/ +export const INTERNAL_EXTEND = Symbol('__internal__extend__'); +export const INTERNAL_REOPEN = Symbol('__internal__reopen__'); +export const INTERNAL_REOPEN_CLASS = Symbol('__internal__reopen_class__'); diff --git a/packages/@ember/-internals/views/lib/views/core_view.ts b/packages/@ember/-internals/views/lib/views/core_view.ts index c1a7d1c0a15..fdd41cf8776 100644 --- a/packages/@ember/-internals/views/lib/views/core_view.ts +++ b/packages/@ember/-internals/views/lib/views/core_view.ts @@ -13,6 +13,7 @@ import Evented from '@ember/object/evented'; import { FrameworkObject } from '@ember/object/-internals'; import type { ViewState } from './states'; import states from './states'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; /** `CoreView` is an abstract class that exists to give view-like behavior @@ -32,7 +33,7 @@ import states from './states'; */ interface CoreView extends ActionHandler, View {} -class CoreView extends FrameworkObject.extend(ActionHandler) { +class CoreView extends FrameworkObject[INTERNAL_EXTEND](ActionHandler) { static { // The deprecated Evented mixin is no longer applied, but instances still // provide its methods, so `Evented.detect` must keep returning true. diff --git a/packages/@ember/application/index.ts b/packages/@ember/application/index.ts index e4ac76f8de3..4822030b75e 100644 --- a/packages/@ember/application/index.ts +++ b/packages/@ember/application/index.ts @@ -29,6 +29,7 @@ import { setupApplicationRegistry } from '@ember/-internals/glimmer/lib/setup-re import RouterService from '@ember/routing/router-service'; import type { EngineInstanceOptions } from '@ember/engine/instance'; import type { SimpleDocument, SimpleElement } from '@simple-dom/interface'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; /** * @deprecated Use `import { getOwner } from '@ember/owner';` instead. @@ -386,7 +387,7 @@ class Application extends Engine { // Create subclass of Router for this Application instance. // This is to ensure that someone reopening `App.Router` does not // tamper with the default `Router`. - this.Router = (this.Router || Router).extend() as typeof Router; + this.Router = (this.Router || Router)[INTERNAL_EXTEND]() as typeof Router; this._buildDeprecatedInstance(); this.waitForDOMReady(); diff --git a/packages/@ember/array/proxy.ts b/packages/@ember/array/proxy.ts index 192c55215ee..aa60e8ba8c3 100644 --- a/packages/@ember/array/proxy.ts +++ b/packages/@ember/array/proxy.ts @@ -29,6 +29,7 @@ import { import { consumeTag } from '@glimmer/validator/lib/tracking'; import { tagFor } from '@glimmer/validator/lib/meta'; import type { Tag } from '@glimmer/interfaces'; +import { INTERNAL_REOPEN } from '@ember/-internals/utils/lib/internal-classic-class'; function isMutable(obj: T[] | EmberArray): obj is T[] | MutableArray { return Array.isArray(obj) || typeof (obj as MutableArray).replace === 'function'; @@ -412,7 +413,7 @@ class ArrayProxy extends EmberObject implements PropertyDidChange { } } -ArrayProxy.reopen(MutableArray, { +ArrayProxy[INTERNAL_REOPEN](MutableArray, { arrangedContent: alias('content'), }); diff --git a/packages/@ember/controller/index.ts b/packages/@ember/controller/index.ts index b83b4e6ae98..3a2e0d16b6a 100644 --- a/packages/@ember/controller/index.ts +++ b/packages/@ember/controller/index.ts @@ -13,6 +13,7 @@ import { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixi import type { RouteArgs } from '@ember/routing/-internals'; import ActionHandler from '@ember/-internals/runtime/lib/mixins/action_handler'; import type { Transition } from 'router_js'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; export type ControllerQueryParamType = 'boolean' | 'number' | 'array' | 'string'; export type ControllerQueryParam = @@ -317,7 +318,7 @@ const ControllerMixin = Mixin[INTERNAL_MIXIN_CREATE](ActionHandler, { @public */ interface Controller<_T = unknown> extends FrameworkObject, ControllerMixin<_T> {} -class Controller<_T = unknown> extends FrameworkObject.extend(ControllerMixin) {} +class Controller<_T = unknown> extends FrameworkObject[INTERNAL_EXTEND](ControllerMixin) {} /** Creates a property that lazily looks up another controller in the container. diff --git a/packages/@ember/controller/tests/controller_test.js b/packages/@ember/controller/tests/controller_test.js index 0c74bb29b94..d4a63325065 100644 --- a/packages/@ember/controller/tests/controller_test.js +++ b/packages/@ember/controller/tests/controller_test.js @@ -11,6 +11,7 @@ import { AbstractTestCase, runTask, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { action } from '@ember/object'; @@ -87,9 +88,11 @@ moduleFor( moduleFor( 'Controller event handling', class extends AbstractTestCase { - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test Action can be handled by a function on actions object`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Action can be handled by a function on actions object`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(2); expectDeprecation( @@ -108,9 +111,11 @@ moduleFor( controller.send('poke'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test A handled action can be bubbled to the target for continued processing`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test A handled action can be bubbled to the target for continued processing`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(3); expectDeprecation( @@ -148,9 +153,11 @@ moduleFor( runDestroy(owner); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved - )} @test Action can be handled by a superclass' actions object`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Action can be handled by a superclass' actions object`]( + assert + ) { + expectClassicClassDeprecation(); + assert.expect(5); expectDeprecation( diff --git a/packages/@ember/engine/index.ts b/packages/@ember/engine/index.ts index 478de9e9b13..b82e937f683 100644 --- a/packages/@ember/engine/index.ts +++ b/packages/@ember/engine/index.ts @@ -16,6 +16,10 @@ import { RoutingService } from '@ember/routing/-internals'; import { setupEngineRegistry } from '@ember/-internals/glimmer/lib/setup-registry'; import RegistryProxyMixin from '@ember/-internals/runtime/lib/mixins/registry_proxy'; import { StrictResolver } from './lib/strict-resolver'; +import { + INTERNAL_EXTEND, + INTERNAL_REOPEN_CLASS, +} from '@ember/-internals/utils/lib/internal-classic-class'; function props(obj: object) { let properties = []; @@ -56,7 +60,7 @@ export interface Initializer { */ // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface Engine extends RegistryProxyMixin {} -class Engine extends Namespace.extend(RegistryProxyMixin) { +class Engine extends Namespace[INTERNAL_EXTEND](RegistryProxyMixin) { static initializers: Record> = Object.create(null); static instanceInitializers: Record> = Object.create(null); @@ -494,7 +498,7 @@ export function buildInitializerMethod< let attrs = { [bucketName]: Object.create(this[bucketName]), }; - this.reopenClass(attrs); + this[INTERNAL_REOPEN_CLASS](attrs); } assert( diff --git a/packages/@ember/engine/instance.ts b/packages/@ember/engine/instance.ts index b49d1280121..58f9b637b73 100644 --- a/packages/@ember/engine/instance.ts +++ b/packages/@ember/engine/instance.ts @@ -16,6 +16,7 @@ import { type FullName, isFactory } from '@ember/-internals/owner'; import type Engine from '@ember/engine'; import type Application from '@ember/application'; import type { SimpleElement } from '@simple-dom/interface'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; export interface BootOptions { isBrowser?: boolean; @@ -59,7 +60,7 @@ export interface EngineInstanceOptions { // type checking, we have broken part of our public API contract. Medium-term, // the goal here is to `EngineInstance` simple be `Owner`. interface EngineInstance extends RegistryProxyMixin, ContainerProxyMixin, InternalOwner, Owner {} -class EngineInstance extends EmberObject.extend(RegistryProxyMixin, ContainerProxyMixin) { +class EngineInstance extends EmberObject[INTERNAL_EXTEND](RegistryProxyMixin, ContainerProxyMixin) { /** @private @method setupRegistry diff --git a/packages/@ember/object/core.ts b/packages/@ember/object/core.ts index 2dcec80ec1c..d844cd32137 100644 --- a/packages/@ember/object/core.ts +++ b/packages/@ember/object/core.ts @@ -20,6 +20,12 @@ import { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixi import ActionHandler from '@ember/-internals/runtime/lib/mixins/action_handler'; import makeArray from '@ember/array/make'; import { assert } from '@ember/debug'; +import { deprecateUntil, DEPRECATIONS } from '@ember/-internals/deprecations'; +import { + INTERNAL_EXTEND, + INTERNAL_REOPEN, + INTERNAL_REOPEN_CLASS, +} from '@ember/-internals/utils/lib/internal-classic-class'; import { DEBUG } from '@glimmer/env'; import { destroy, isDestroying, isDestroyed, registerDestructor } from '@glimmer/destroyable'; import { OWNER } from '@glimmer/owner'; @@ -312,6 +318,10 @@ class CoreObject { } reopen(...args: Array>): this { + deprecateUntil( + '`reopen` is part of the classic class system, which is deprecated. Define the properties and methods on a native class instead.', + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES + ); applyMixin(this, args); return this; } @@ -714,6 +724,19 @@ class CoreObject { ...mixins: M ): Readonly & EmberClassConstructor & MergeArray; static extend(...mixins: any[]) { + deprecateUntil( + '`.extend()` creates a classic class, which is deprecated. Use native class syntax (`class Foo extends Bar {}`) instead.', + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES + ); + return this[INTERNAL_EXTEND](...mixins); + } + + /** @internal */ + static [INTERNAL_EXTEND]>( + this: Statics & EmberClassConstructor, + ...mixins: M + ): Readonly & EmberClassConstructor & MergeArray; + static [INTERNAL_EXTEND](...mixins: any[]) { let Class = class extends this {}; reopen.apply(Class.PrototypeMixin, mixins); return Class; @@ -839,6 +862,15 @@ class CoreObject { @public */ static reopen(this: C, ...args: any[]): C { + deprecateUntil( + '`reopen` is part of the classic class system, which is deprecated. Define the properties and methods on a native class instead.', + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES + ); + return this[INTERNAL_REOPEN](...args); + } + + /** @internal */ + static [INTERNAL_REOPEN](this: C, ...args: any[]): C { this.willReopen(); reopen.apply(this.PrototypeMixin, args); return this; @@ -922,6 +954,18 @@ class CoreObject { static reopenClass( this: C, ...mixins: Array> + ): C { + deprecateUntil( + '`reopenClass` is part of the classic class system, which is deprecated. Define static properties and methods on a native class instead.', + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES + ); + return this[INTERNAL_REOPEN_CLASS](...mixins); + } + + /** @internal */ + static [INTERNAL_REOPEN_CLASS]( + this: C, + ...mixins: Array> ): C { applyMixin(this, mixins); return this; diff --git a/packages/@ember/object/index.ts b/packages/@ember/object/index.ts index aff353ba9ce..ab017613600 100644 --- a/packages/@ember/object/index.ts +++ b/packages/@ember/object/index.ts @@ -10,6 +10,7 @@ import { getFactoryFor } from '@ember/-internals/container/lib/container'; import { setObservers } from '@ember/-internals/utils/lib/super'; import type { AnyFn } from '@ember/-internals/utility-types'; import CoreObject from '@ember/object/core'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; import Observable from '@ember/object/observable'; import { type Decorator, @@ -42,7 +43,7 @@ export { default as computed } from '@ember/-internals/metal/lib/computed'; */ // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface EmberObject extends Observable {} -class EmberObject extends CoreObject.extend(Observable) { +class EmberObject extends CoreObject[INTERNAL_EXTEND](Observable) { get _debugContainerKey() { let factory = getFactoryFor(this); return factory !== undefined && factory.fullName; diff --git a/packages/@ember/object/tests/classic-classes-deprecation-test.js b/packages/@ember/object/tests/classic-classes-deprecation-test.js new file mode 100644 index 00000000000..0afdf0a587e --- /dev/null +++ b/packages/@ember/object/tests/classic-classes-deprecation-test.js @@ -0,0 +1,127 @@ +import EmberObject from '@ember/object'; +import Mixin from '@ember/object/mixin'; +import { DEPRECATIONS } from '@ember/-internals/deprecations'; +import { + INTERNAL_EXTEND, + INTERNAL_REOPEN, + INTERNAL_REOPEN_CLASS, +} from '@ember/-internals/utils/lib/internal-classic-class'; +import { + moduleFor, + AbstractTestCase, + expectDeprecation, + expectNoDeprecation, + testUnless, +} from 'internal-test-helpers'; + +const { isEnabled, isRemoved } = DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES; + +moduleFor( + 'Classic classes deprecation (RFC #1117)', + class extends AbstractTestCase { + [`${testUnless(isRemoved)} @test extend() is deprecated`](assert) { + let Person; + + expectDeprecation( + () => { + Person = EmberObject.extend({ name: 'Tom' }); + }, + /`\.extend\(\)` creates a classic class, which is deprecated/, + isEnabled + ); + + assert.equal(Person.create().name, 'Tom', 'the class still works'); + } + + [`${testUnless( + isRemoved || DEPRECATIONS.DEPRECATE_MIXINS.isRemoved + )} @test extend() is deprecated when applying a mixin`](assert) { + let Greeter; + + expectDeprecation( + () => { + Greeter = Mixin.create({ greet: () => 'hi' }); + }, + /Using mixins is deprecated/, + DEPRECATIONS.DEPRECATE_MIXINS.isEnabled + ); + let Person; + + expectDeprecation( + () => { + Person = EmberObject.extend(Greeter); + }, + /`\.extend\(\)` creates a classic class, which is deprecated/, + isEnabled + ); + + assert.equal(Person.create().greet(), 'hi', 'the mixin was still applied'); + } + + [`${testUnless(isRemoved)} @test reopen() is deprecated`](assert) { + let Person = class extends EmberObject {}; + + expectDeprecation( + () => { + Person.reopen({ name: 'Tom' }); + }, + /`reopen` is part of the classic class system, which is deprecated/, + isEnabled + ); + + assert.equal(Person.create().name, 'Tom', 'the property was still added'); + } + + [`${testUnless(isRemoved)} @test reopen() on an instance is deprecated`](assert) { + let person = class extends EmberObject {}.create(); + + expectDeprecation( + () => { + person.reopen({ name: 'Tom' }); + }, + /`reopen` is part of the classic class system, which is deprecated/, + isEnabled + ); + + assert.equal(person.name, 'Tom', 'the property was still added'); + } + + [`${testUnless(isRemoved)} @test reopenClass() is deprecated`](assert) { + let Person = class extends EmberObject {}; + + expectDeprecation( + () => { + Person.reopenClass({ species: 'Homo sapiens' }); + }, + /`reopenClass` is part of the classic class system, which is deprecated/, + isEnabled + ); + + assert.equal(Person.species, 'Homo sapiens', 'the static property was still added'); + } + + ['@test the internal methods do not deprecate, so Ember internals can build on them'](assert) { + let Person; + + expectNoDeprecation(() => { + Person = EmberObject[INTERNAL_EXTEND]({ name: 'Tom' }); + Person[INTERNAL_REOPEN]({ age: 5 }); + Person[INTERNAL_REOPEN_CLASS]({ species: 'Homo sapiens' }); + }); + + let person = Person.create(); + + assert.equal(person.name, 'Tom', 'the internal extend works'); + assert.equal(person.age, 5, 'the internal reopen works'); + assert.equal(Person.species, 'Homo sapiens', 'the internal reopenClass works'); + } + + ['@test native class syntax does not trigger the deprecation'](assert) { + class Person extends EmberObject { + name = 'Tom'; + } + + assert.equal(Person.create().name, 'Tom'); + } + } +); diff --git a/packages/@ember/object/tests/computed/dependent-key-compat-test.js b/packages/@ember/object/tests/computed/dependent-key-compat-test.js index 72dc33d5eb7..832f2f699fb 100644 --- a/packages/@ember/object/tests/computed/dependent-key-compat-test.js +++ b/packages/@ember/object/tests/computed/dependent-key-compat-test.js @@ -1,7 +1,14 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import EmberObject, { computed, observer } from '@ember/object'; import { tracked } from '@ember/-internals/metal'; import { dependentKeyCompat } from '@ember/object/compat'; -import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + runLoopSettled, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; moduleFor( 'dependentKeyCompat', @@ -58,7 +65,11 @@ moduleFor( assert.equal(tom.fullName, 'Thomas Dale'); } - async '@test it works with async observers'(assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it works with async observers`]( + assert + ) { + expectClassicClassDeprecation(); + let count = 0; let Person = EmberObject.extend({ @@ -95,7 +106,11 @@ moduleFor( tom.destroy(); } - '@test it does not work with sync observers'(assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test it does not work with sync observers`]( + assert + ) { + expectClassicClassDeprecation(); + let count = 0; let Person = EmberObject.extend({ diff --git a/packages/@ember/object/tests/computed/reduce_computed_macros_test.js b/packages/@ember/object/tests/computed/reduce_computed_macros_test.js index b481387b089..a2b96cc2883 100644 --- a/packages/@ember/object/tests/computed/reduce_computed_macros_test.js +++ b/packages/@ember/object/tests/computed/reduce_computed_macros_test.js @@ -32,6 +32,7 @@ import { runLoopSettled, expectDeprecation, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { DEPRECATIONS } from '@ember/-internals/deprecations'; @@ -2022,9 +2023,11 @@ moduleFor( assert.deepEqual(obj2.get('sortedItems').mapBy('name'), ['Z', 'X', 'Y', 'W'], 'final'); } - ['@test sort correctly when multiple sorts are chained on the same instance of a class']( + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test sort correctly when multiple sorts are chained on the same instance of a class`]( assert ) { + expectClassicClassDeprecation(); + let obj2 = klass .extend({ items: computed('sibling.sortedItems.[]', function () { @@ -2339,53 +2342,57 @@ moduleFor( ); let userFnCalls; -moduleFor( - 'Chaining array and reduced CPs', - class extends AbstractTestCase { - beforeEach() { - userFnCalls = 0; - obj = class extends EmberObject { - @mapBy('array', 'v') - mapped; - @max('mapped') - max; +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + 'Chaining array and reduced CPs', + class extends AbstractTestCase { + beforeEach() { + expectClassicClassDeprecation(); + + userFnCalls = 0; + obj = class extends EmberObject { + @mapBy('array', 'v') + mapped; + @max('mapped') + max; + } + .extend({ + maxDidChange: observer('max', () => userFnCalls++), + }) + .create({ + array: emberA([{ v: 1 }, { v: 3 }, { v: 2 }, { v: 1 }]), + }); } - .extend({ - maxDidChange: observer('max', () => userFnCalls++), - }) - .create({ - array: emberA([{ v: 1 }, { v: 3 }, { v: 2 }, { v: 1 }]), - }); - } - afterEach() { - run(obj, 'destroy'); - } + afterEach() { + run(obj, 'destroy'); + } - async ['@test it computes interdependent array computed properties'](assert) { - assert.equal(userFnCalls, 0, 'object defined observers have not fired yet'); - assert.equal(obj.get('max'), 3, 'sanity - it properly computes the maximum value'); + async ['@test it computes interdependent array computed properties'](assert) { + assert.equal(userFnCalls, 0, 'object defined observers have not fired yet'); + assert.equal(obj.get('max'), 3, 'sanity - it properly computes the maximum value'); - let calls = 0; + let calls = 0; - addObserver(obj, 'max', () => calls++); + addObserver(obj, 'max', () => calls++); - obj.get('array').pushObject({ v: 5 }); - await runLoopSettled(); + obj.get('array').pushObject({ v: 5 }); + await runLoopSettled(); - assert.equal(obj.get('max'), 5, 'maximum value is updated correctly'); - assert.equal(userFnCalls, 1, 'object defined observers fire'); - assert.equal(calls, 1, 'runtime created observers fire'); + assert.equal(obj.get('max'), 5, 'maximum value is updated correctly'); + assert.equal(userFnCalls, 1, 'object defined observers fire'); + assert.equal(calls, 1, 'runtime created observers fire'); - obj.get('array').pushObject({ v: 7 }); - await runLoopSettled(); + obj.get('array').pushObject({ v: 7 }); + await runLoopSettled(); - assert.equal(obj.get('max'), 7, 'maximum value is updated correctly again'); - assert.equal(userFnCalls, 2, 'object defined observers fire again'); - assert.equal(calls, 2, 'runtime created observers fire again'); + assert.equal(obj.get('max'), 7, 'maximum value is updated correctly again'); + assert.equal(userFnCalls, 2, 'object defined observers fire again'); + assert.equal(calls, 2, 'runtime created observers fire again'); + } } - } -); + ); +} moduleFor( 'sum', diff --git a/packages/@ember/object/tests/computed_test.js b/packages/@ember/object/tests/computed_test.js index 59d294d421f..6ae2de5187f 100644 --- a/packages/@ember/object/tests/computed_test.js +++ b/packages/@ember/object/tests/computed_test.js @@ -1,8 +1,14 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { notifyPropertyChange } from '@ember/-internals/metal'; import { alias, oneWay as reads } from '@ember/object/computed'; import { A as emberA, isArray } from '@ember/array'; import EmberObject, { defineProperty, get, set, computed, observer } from '@ember/object'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; function K() { return this; @@ -140,7 +146,11 @@ moduleFor( testGet(assert, 'BLARG 2', obj2, 'foo'); // should not invalidate property } - ['@test can retrieve metadata for a computed property'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test can retrieve metadata for a computed property`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ computedProperty: computed(function () {}).meta({ key: 'keyValue' }), }); @@ -175,9 +185,11 @@ moduleFor( }, "metaForProperty() could not find a computed property with key 'staticProperty'."); } - ['@test overriding a computed property with null removes it from eachComputedProperty iteration']( + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test overriding a computed property with null removes it from eachComputedProperty iteration`]( assert ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ foo: computed(function () {}), @@ -201,7 +213,11 @@ moduleFor( ); } - ['@test can iterate over a list of computed properties for a class'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test can iterate over a list of computed properties for a class`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ foo: computed(function () {}), @@ -251,9 +267,11 @@ moduleFor( ); } - ['@test list of properties updates when an additional property is added (such cache busting)']( + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test list of properties updates when an additional property is added (such cache busting)`]( assert ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ foo: computed(K), @@ -303,7 +321,11 @@ moduleFor( ); } - ['@test Calling _super in call outside the immediate function of a CP getter works'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Calling _super in call outside the immediate function of a CP getter works`]( + assert + ) { + expectClassicClassDeprecation(); + let macroCalls = 0; function macro(callback) { return computed(function () { @@ -328,7 +350,11 @@ moduleFor( assert.strictEqual(macroCalls, 1, "the macro's getter is used"); } - ['@test Calling _super in apply outside the immediate function of a CP getter works'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Calling _super in apply outside the immediate function of a CP getter works`]( + assert + ) { + expectClassicClassDeprecation(); + let macroCalls = 0; function macro(callback) { return computed(function () { @@ -353,9 +379,11 @@ moduleFor( assert.strictEqual(macroCalls, 1, "the macro's getter is used"); } - ['@test observing prop installed with computed macro reads and overriding it in create() works']( + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observing prop installed with computed macro reads and overriding it in create() works`]( assert ) { + expectClassicClassDeprecation(); + let Obj = EmberObject.extend({ name: reads('model.name'), nameDidChange: observer('name', function () {}), @@ -392,7 +420,11 @@ moduleFor( assert.equal(instance.bar, 456, 'setters work'); } - ['@test @each on maybe array'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test @each on maybe array`]( + assert + ) { + expectClassicClassDeprecation(); + let Normalizer = EmberObject.extend({ options: null, // null | undefined | { value: any } | Array<{ value: any }> @@ -470,7 +502,11 @@ moduleFor( }, /When using @each to observe the array `true,foo,123`, the items in the array must be objects/); } - ['@test @each works with array-likes'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test @each works with array-likes`]( + assert + ) { + expectClassicClassDeprecation(); + class ArrayLike { constructor(arr = []) { this.inner = arr; diff --git a/packages/@ember/object/tests/create_test.js b/packages/@ember/object/tests/create_test.js index 266ac20072b..74fde79c3b6 100644 --- a/packages/@ember/object/tests/create_test.js +++ b/packages/@ember/object/tests/create_test.js @@ -13,6 +13,7 @@ import { AbstractTestCase, expectDeprecation, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; import { destroy } from '@glimmer/destroyable'; @@ -47,7 +48,11 @@ moduleFor( runDestroy(owner); } - ['@test calls computed property setters'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test calls computed property setters`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ foo: computed({ get() { @@ -63,7 +68,11 @@ moduleFor( assert.equal(o.get('foo'), 'bar'); } - ['@test sets up mandatory setters for simple properties watched with observers'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test sets up mandatory setters for simple properties watched with observers`]( + assert + ) { + expectClassicClassDeprecation(); + if (DEBUG) { let MyClass = EmberObject.extend({ foo: null, @@ -136,7 +145,11 @@ moduleFor( } } - ['@test does not sets up separate mandatory setters on getters'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test does not sets up separate mandatory setters on getters`]( + assert + ) { + expectClassicClassDeprecation(); + if (DEBUG) { let MyClass = EmberObject.extend({ get foo() { diff --git a/packages/@ember/object/tests/destroy_test.js b/packages/@ember/object/tests/destroy_test.js index eea3ccdd957..a118d2f0821 100644 --- a/packages/@ember/object/tests/destroy_test.js +++ b/packages/@ember/object/tests/destroy_test.js @@ -1,9 +1,16 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { run } from '@ember/runloop'; import { beginPropertyChanges, endPropertyChanges } from '@ember/-internals/metal'; import { peekMeta } from '@ember/-internals/meta'; import EmberObject, { get, set, observer } from '@ember/object'; import { DEBUG } from '@glimmer/env'; -import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + runLoopSettled, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; moduleFor( '@ember/-internals/runtime/system/object/destroy_test', @@ -27,9 +34,11 @@ moduleFor( // MANDATORY_SETTER moves value to meta.values // a destroyed object removes meta but leaves the accessor // that looks it up - ['@test should raise an exception when modifying watched properties on a destroyed object']( + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test should raise an exception when modifying watched properties on a destroyed object`]( assert ) { + expectClassicClassDeprecation(); + if (DEBUG) { let obj = EmberObject.extend({ fooDidChange: observer('foo', function () {}), @@ -45,7 +54,11 @@ moduleFor( } } - async ['@test observers should not fire after an object has been destroyed'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observers should not fire after an object has been destroyed`]( + assert + ) { + expectClassicClassDeprecation(); + let count = 0; let obj = EmberObject.extend({ fooDidChange: observer('foo', function () { @@ -67,9 +80,11 @@ moduleFor( assert.equal(count, 1, 'observer was not called after object was destroyed'); } - async ['@test destroyed objects should not see each others changes during teardown but a long lived object should']( + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test destroyed objects should not see each others changes during teardown but a long lived object should`]( assert ) { + expectClassicClassDeprecation(); + let shouldChange = 0; let shouldNotChange = 0; diff --git a/packages/@ember/object/tests/es-compatibility-test.js b/packages/@ember/object/tests/es-compatibility-test.js index 9a5f32a34d5..768ae66db2c 100644 --- a/packages/@ember/object/tests/es-compatibility-test.js +++ b/packages/@ember/object/tests/es-compatibility-test.js @@ -15,6 +15,7 @@ import { runLoopSettled, expectDeprecation, testUnless, + expectClassicClassDeprecation, } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; @@ -174,7 +175,11 @@ moduleFor( }); } - [`${testUnless(DEPRECATIONS.DEPRECATE_MIXINS.isRemoved)} @test using mixins`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_MIXINS.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test using mixins`]( + assert + ) { + expectClassicClassDeprecation(); + expectDeprecation(/Using mixins is deprecated/, DEPRECATIONS.DEPRECATE_MIXINS.isEnabled); let Mixin1 = Mixin.create({ @@ -285,7 +290,7 @@ moduleFor( SubEmberObject.metaForProperty('foo'); } - [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test observes / removeObserver on / removeListener interop`]( + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observes / removeObserver on / removeListener interop`]( assert ) { let fooDidChangeBase = 0; @@ -297,6 +302,8 @@ moduleFor( let A; expectDeprecation( () => { + expectClassicClassDeprecation(); + A = class extends ( EmberObject.extend({ fooDidChange: observer('foo', function () { @@ -383,7 +390,11 @@ moduleFor( }); } - '@test super and _super interop between old and new methods'(assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test super and _super interop between old and new methods`]( + assert + ) { + expectClassicClassDeprecation(); + let calls = []; let changes = []; let events = []; diff --git a/packages/@ember/object/tests/evented_test.js b/packages/@ember/object/tests/evented_test.js index 6e2831d95da..dbdd0ca3032 100644 --- a/packages/@ember/object/tests/evented_test.js +++ b/packages/@ember/object/tests/evented_test.js @@ -4,7 +4,13 @@ import EventedMixin from '@ember/object/evented'; import Component from '@ember/component'; import Route from '@ember/routing/route'; import EmberRouter from '@ember/routing/router'; -import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + expectDeprecation, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; moduleFor( @@ -27,9 +33,11 @@ moduleFor( assert.false(EventedMixin.detect(EmberObject.prototype), 'EmberObject'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test works properly on proxy-ish objects`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test works properly on proxy-ish objects`]( + assert + ) { + expectClassicClassDeprecation(); + let eventedProxyObj; eventedProxyObj = class extends CoreObject.extend(EventedMixin) { unknownProperty() { diff --git a/packages/@ember/object/tests/events_test.js b/packages/@ember/object/tests/events_test.js index c9c600060c6..97205de301c 100644 --- a/packages/@ember/object/tests/events_test.js +++ b/packages/@ember/object/tests/events_test.js @@ -1,14 +1,22 @@ import EmberObject from '@ember/object'; import Evented from '@ember/object/evented'; -import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + expectDeprecation, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; moduleFor( 'Object events', class extends AbstractTestCase { - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test a listener can be added to an object`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test a listener can be added to an object`]( + assert + ) { + expectClassicClassDeprecation(); + let count = 0; let F = function () { count++; @@ -45,11 +53,11 @@ moduleFor( assert.equal(count, 2, 'the event was triggered'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test a listener can be added and removed automatically the first time it is triggered`]( + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test a listener can be added and removed automatically the first time it is triggered`]( assert ) { + expectClassicClassDeprecation(); + let count = 0; let F = function () { count++; @@ -87,9 +95,11 @@ moduleFor( assert.equal(count, 1, 'the event was not triggered again'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test triggering an event can have arguments`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test triggering an event can have arguments`]( + assert + ) { + expectClassicClassDeprecation(); + let self, args; let obj; @@ -118,9 +128,11 @@ moduleFor( assert.equal(self, obj); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test a listener can be added and removed automatically and have arguments`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test a listener can be added and removed automatically and have arguments`]( + assert + ) { + expectClassicClassDeprecation(); + let self, args; let count = 0; @@ -164,9 +176,11 @@ moduleFor( assert.equal(self, obj); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test binding an event can specify a different target`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test binding an event can specify a different target`]( + assert + ) { + expectClassicClassDeprecation(); + let self, args; let obj; @@ -196,11 +210,11 @@ moduleFor( assert.equal(self, target); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test a listener registered with one can take method as string and can be added with different target`]( + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test a listener registered with one can take method as string and can be added with different target`]( assert ) { + expectClassicClassDeprecation(); + let count = 0; let target = {}; target.fn = function () { @@ -239,9 +253,11 @@ moduleFor( assert.equal(count, 1, 'the event was not triggered again'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test a listener registered with one can be removed with off`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test a listener registered with one can be removed with off`]( + assert + ) { + expectClassicClassDeprecation(); + let obj; obj = class extends EmberObject.extend(Evented) { F() {} @@ -301,9 +317,11 @@ moduleFor( assert.equal(objHas, false, 'has no more events'); } - [`${testUnless( - DEPRECATIONS.DEPRECATE_EVENTED.isRemoved - )} @test adding and removing listeners should be chainable`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test adding and removing listeners should be chainable`]( + assert + ) { + expectClassicClassDeprecation(); + let obj; obj = EmberObject.extend(Evented).create(); let F = function () {}; diff --git a/packages/@ember/object/tests/extend_test.js b/packages/@ember/object/tests/extend_test.js index a99259276a7..a2c18910680 100644 --- a/packages/@ember/object/tests/extend_test.js +++ b/packages/@ember/object/tests/extend_test.js @@ -1,18 +1,29 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { computed, get } from '@ember/object'; import EmberObject, { observer } from '@ember/object'; -import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + runLoopSettled, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; moduleFor( 'EmberObject.extend', class extends AbstractTestCase { - ['@test Basic extend'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Basic extend`](assert) { + expectClassicClassDeprecation(); + let SomeClass = EmberObject.extend({ foo: 'BAR' }); assert.ok(SomeClass.isClass, 'A class has isClass of true'); let obj = SomeClass.create(); assert.equal(obj.foo, 'BAR'); } - ['@test Sub-subclass'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Sub-subclass`](assert) { + expectClassicClassDeprecation(); + let SomeClass = EmberObject.extend({ foo: 'BAR' }); let AnotherClass = SomeClass.extend({ bar: 'FOO' }); let obj = AnotherClass.create(); @@ -20,7 +31,11 @@ moduleFor( assert.equal(obj.bar, 'FOO'); } - ['@test Overriding a method several layers deep'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Overriding a method several layers deep`]( + assert + ) { + expectClassicClassDeprecation(); + let SomeClass = EmberObject.extend({ fooCnt: 0, foo() { @@ -69,7 +84,11 @@ moduleFor( assert.equal(obj.barCnt, 2, 'should invoke both'); } - ['@test With concatenatedProperties'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test With concatenatedProperties`]( + assert + ) { + expectClassicClassDeprecation(); + let SomeClass = EmberObject.extend({ things: 'foo', concatenatedProperties: ['things'], @@ -92,7 +111,11 @@ moduleFor( ); } - ['@test With concatenatedProperties class properties'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test With concatenatedProperties class properties`]( + assert + ) { + expectClassicClassDeprecation(); + let SomeClass = EmberObject.extend(); SomeClass.reopenClass({ concatenatedProperties: ['things'], @@ -122,7 +145,11 @@ moduleFor( ); } - async ['@test Overriding a computed property with an observer'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Overriding a computed property with an observer`]( + assert + ) { + expectClassicClassDeprecation(); + let Parent = EmberObject.extend({ foo: computed(function () { return 'FOO'; diff --git a/packages/@ember/object/tests/mixin/deprecation_test.js b/packages/@ember/object/tests/mixin/deprecation_test.js index 9e1229a6530..379407908b8 100644 --- a/packages/@ember/object/tests/mixin/deprecation_test.js +++ b/packages/@ember/object/tests/mixin/deprecation_test.js @@ -54,17 +54,21 @@ moduleFor( } [`${testUnless( - DEPRECATIONS.DEPRECATE_MIXINS.isRemoved - )} @test extending EmberObject does not deprecate`](assert) { + DEPRECATIONS.DEPRECATE_MIXINS.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved + )} @test extending EmberObject does not give the mixin deprecation`](assert) { // EmberObject builds a PrototypeMixin internally for every subclass, so // extending must not warn apps that never author a mixin themselves. let Subclass; - expectNoDeprecation(() => { - Subclass = EmberObject.extend({ - foo: 'FOO', - }); - }); + expectDeprecation( + () => { + Subclass = EmberObject.extend({ + foo: 'FOO', + }); + }, + /classic class/, + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isEnabled + ); let obj = Subclass.create(); assert.equal(obj.foo, 'FOO', 'the subclass works'); @@ -72,15 +76,19 @@ moduleFor( } [`${testUnless( - DEPRECATIONS.DEPRECATE_MIXINS.isRemoved - )} @test reopening an EmberObject subclass does not deprecate`](assert) { - let Subclass = EmberObject.extend(); + DEPRECATIONS.DEPRECATE_MIXINS.isRemoved || DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved + )} @test reopening an EmberObject subclass does not give the mixin deprecation`](assert) { + let Subclass = class extends EmberObject {}; - expectNoDeprecation(() => { - Subclass.reopen({ - foo: 'FOO', - }); - }); + expectDeprecation( + () => { + Subclass.reopen({ + foo: 'FOO', + }); + }, + /classic class/, + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isEnabled + ); let obj = Subclass.create(); assert.equal(obj.foo, 'FOO', 'the reopened property is present'); diff --git a/packages/@ember/object/tests/mixin/reopen_test.js b/packages/@ember/object/tests/mixin/reopen_test.js index e3fc0e91b8e..13ee8553391 100644 --- a/packages/@ember/object/tests/mixin/reopen_test.js +++ b/packages/@ember/object/tests/mixin/reopen_test.js @@ -1,7 +1,13 @@ import EmberObject, { get } from '@ember/object'; import Mixin from '@ember/object/mixin'; import { run } from '@ember/runloop'; -import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + expectDeprecation, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../-internals/deprecations'; moduleFor( @@ -22,9 +28,11 @@ moduleFor( assert.equal(get(obj, 'bar'), 'BAR', 'include MixinB props'); } - ['@test using reopen() and calling _super where there is not a super function does not cause infinite recursion']( + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test using reopen() and calling _super where there is not a super function does not cause infinite recursion`]( assert ) { + expectClassicClassDeprecation(); + let Taco = class extends EmberObject { createBreakfast() { // There is no original createBreakfast function. diff --git a/packages/@ember/object/tests/observable_test.js b/packages/@ember/object/tests/observable_test.js index 8b6ce290204..4d7c79a75cf 100644 --- a/packages/@ember/object/tests/observable_test.js +++ b/packages/@ember/object/tests/observable_test.js @@ -1,10 +1,17 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { context } from '@ember/-internals/environment'; import { run } from '@ember/runloop'; import { get, computed } from '@ember/object'; import EmberObject, { observer } from '@ember/object'; import Observable from '@ember/object/observable'; import { A as emberA } from '@ember/array'; -import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + runLoopSettled, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests @@ -33,7 +40,8 @@ import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpe let object, objectA, objectB, objectC, objectD, objectE, objectF, lookup; -const ObservableObject = EmberObject.extend(Observable); +// `EmberObject` applies `Observable` itself. +const ObservableObject = class extends EmberObject {}; const originalLookup = context.lookup; class ObservableTestCase extends AbstractTestCase { @@ -54,125 +62,139 @@ class ObservableTestCase extends AbstractTestCase { // GET() // -moduleFor( - 'object.get()', - class extends ObservableTestCase { - beforeEach() { - object = ObservableObject.extend(Observable, { - computed: computed(function () { - return 'value'; - }), - method() { - return 'value'; - }, - unknownProperty(key) { - this.lastUnknownProperty = key; - return 'unknown'; - }, - }).create({ - normal: 'value', - numberVal: 24, - toggleVal: true, - nullProperty: null, - }); - } +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + 'object.get()', + class extends ObservableTestCase { + beforeEach() { + expectClassicClassDeprecation(); - ['@test should get normal properties'](assert) { - assert.equal(object.get('normal'), 'value'); - } + object = ObservableObject.extend(Observable, { + computed: computed(function () { + return 'value'; + }), + method() { + return 'value'; + }, + unknownProperty(key) { + this.lastUnknownProperty = key; + return 'unknown'; + }, + }).create({ + normal: 'value', + numberVal: 24, + toggleVal: true, + nullProperty: null, + }); + } - ['@test should call computed properties and return their result'](assert) { - assert.equal(object.get('computed'), 'value'); - } + ['@test should get normal properties'](assert) { + assert.equal(object.get('normal'), 'value'); + } - ['@test should return the function for a non-computed property'](assert) { - let value = object.get('method'); - assert.equal(typeof value, 'function'); - } + ['@test should call computed properties and return their result'](assert) { + assert.equal(object.get('computed'), 'value'); + } - ['@test should return null when property value is null'](assert) { - assert.equal(object.get('nullProperty'), null); - } + ['@test should return the function for a non-computed property'](assert) { + let value = object.get('method'); + assert.equal(typeof value, 'function'); + } + + ['@test should return null when property value is null'](assert) { + assert.equal(object.get('nullProperty'), null); + } - ['@test should call unknownProperty when value is undefined'](assert) { - assert.equal(object.get('unknown'), 'unknown'); - assert.equal(object.lastUnknownProperty, 'unknown'); + ['@test should call unknownProperty when value is undefined'](assert) { + assert.equal(object.get('unknown'), 'unknown'); + assert.equal(object.lastUnknownProperty, 'unknown'); + } } - } -); + ); +} // .......................................................... // Ember.GET() // -moduleFor( - 'Ember.get()', - class extends ObservableTestCase { - beforeEach() { - objectA = ObservableObject.extend({ - computed: computed(function () { - return 'value'; - }), - method() { - return 'value'; - }, - unknownProperty(key) { - this.lastUnknownProperty = key; - return 'unknown'; - }, - }).create({ - normal: 'value', - numberVal: 24, - toggleVal: true, - nullProperty: null, - }); +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + 'Ember.get()', + class extends ObservableTestCase { + beforeEach() { + expectClassicClassDeprecation(); + + objectA = ObservableObject.extend({ + computed: computed(function () { + return 'value'; + }), + method() { + return 'value'; + }, + unknownProperty(key) { + this.lastUnknownProperty = key; + return 'unknown'; + }, + }).create({ + normal: 'value', + numberVal: 24, + toggleVal: true, + nullProperty: null, + }); - objectB = { - normal: 'value', - nullProperty: null, - }; - } + objectB = { + normal: 'value', + nullProperty: null, + }; + } - ['@test should get normal properties on Ember.Observable'](assert) { - assert.equal(get(objectA, 'normal'), 'value'); - } + ['@test should get normal properties on Ember.Observable'](assert) { + assert.equal(get(objectA, 'normal'), 'value'); + } - ['@test should call computed properties on Ember.Observable and return their result'](assert) { - assert.equal(get(objectA, 'computed'), 'value'); - } + ['@test should call computed properties on Ember.Observable and return their result']( + assert + ) { + assert.equal(get(objectA, 'computed'), 'value'); + } - ['@test should return the function for a non-computed property on Ember.Observable'](assert) { - let value = get(objectA, 'method'); - assert.equal(typeof value, 'function'); - } + ['@test should return the function for a non-computed property on Ember.Observable'](assert) { + let value = get(objectA, 'method'); + assert.equal(typeof value, 'function'); + } - ['@test should return null when property value is null on Ember.Observable'](assert) { - assert.equal(get(objectA, 'nullProperty'), null); - } + ['@test should return null when property value is null on Ember.Observable'](assert) { + assert.equal(get(objectA, 'nullProperty'), null); + } - ['@test should call unknownProperty when value is undefined on Ember.Observable'](assert) { - assert.equal(get(objectA, 'unknown'), 'unknown'); - assert.equal(objectA.lastUnknownProperty, 'unknown'); - } + ['@test should call unknownProperty when value is undefined on Ember.Observable'](assert) { + assert.equal(get(objectA, 'unknown'), 'unknown'); + assert.equal(objectA.lastUnknownProperty, 'unknown'); + } - ['@test should get normal properties on standard objects'](assert) { - assert.equal(get(objectB, 'normal'), 'value'); - } + ['@test should get normal properties on standard objects'](assert) { + assert.equal(get(objectB, 'normal'), 'value'); + } - ['@test should return null when property is null on standard objects'](assert) { - assert.equal(get(objectB, 'nullProperty'), null); - } + ['@test should return null when property is null on standard objects'](assert) { + assert.equal(get(objectB, 'nullProperty'), null); + } - ['@test raise if the provided object is undefined']() { - expectAssertion(function () { - get(undefined, 'key'); - }, /Cannot call get with 'key' on an undefined object/i); + ['@test raise if the provided object is undefined']() { + expectAssertion(function () { + get(undefined, 'key'); + }, /Cannot call get with 'key' on an undefined object/i); + } } - } -); + ); +} moduleFor( 'Ember.get() with paths', class extends ObservableTestCase { - ['@test should return a property at a given path relative to the passed object'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test should return a property at a given path relative to the passed object`]( + assert + ) { + expectClassicClassDeprecation(); + let foo = ObservableObject.create({ bar: ObservableObject.extend({ baz: computed(function () { @@ -202,531 +224,553 @@ moduleFor( // SET() // -moduleFor( - 'object.set()', - class extends ObservableTestCase { - beforeEach() { - object = ObservableObject.extend({ - computed: computed({ - get() { - return this._computed; - }, - set(key, value) { - this._computed = value; - return this._computed; - }, - }), +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + 'object.set()', + class extends ObservableTestCase { + beforeEach() { + expectClassicClassDeprecation(); + + object = ObservableObject.extend({ + computed: computed({ + get() { + return this._computed; + }, + set(key, value) { + this._computed = value; + return this._computed; + }, + }), - method(key, value) { - if (value !== undefined) { - this._method = value; - } - return this._method; - }, + method(key, value) { + if (value !== undefined) { + this._method = value; + } + return this._method; + }, - unknownProperty() { - return this._unknown; - }, + unknownProperty() { + return this._unknown; + }, - setUnknownProperty(key, value) { - this._unknown = value; - return this._unknown; - }, + setUnknownProperty(key, value) { + this._unknown = value; + return this._unknown; + }, - // normal property - normal: 'value', + // normal property + normal: 'value', - // computed property - _computed: 'computed', - // method, but not a property - _method: 'method', - // null property - nullProperty: null, + // computed property + _computed: 'computed', + // method, but not a property + _method: 'method', + // null property + nullProperty: null, - // unknown property - _unknown: 'unknown', - }).create(); - } + // unknown property + _unknown: 'unknown', + }).create(); + } - ['@test should change normal properties and return the value'](assert) { - let ret = object.set('normal', 'changed'); - assert.equal(object.get('normal'), 'changed'); - assert.equal(ret, 'changed'); - } + ['@test should change normal properties and return the value'](assert) { + let ret = object.set('normal', 'changed'); + assert.equal(object.get('normal'), 'changed'); + assert.equal(ret, 'changed'); + } - ['@test should call computed properties passing value and return the value'](assert) { - let ret = object.set('computed', 'changed'); - assert.equal(object.get('_computed'), 'changed'); - assert.equal(ret, 'changed'); - } + ['@test should call computed properties passing value and return the value'](assert) { + let ret = object.set('computed', 'changed'); + assert.equal(object.get('_computed'), 'changed'); + assert.equal(ret, 'changed'); + } - ['@test should change normal properties when passing undefined'](assert) { - let ret = object.set('normal', undefined); - assert.equal(object.get('normal'), undefined); - assert.equal(ret, undefined); - } + ['@test should change normal properties when passing undefined'](assert) { + let ret = object.set('normal', undefined); + assert.equal(object.get('normal'), undefined); + assert.equal(ret, undefined); + } - ['@test should replace the function for a non-computed property and return the value'](assert) { - let ret = object.set('method', 'changed'); - assert.equal(object.get('_method'), 'method'); // make sure this was NOT run - assert.ok(typeof object.get('method') !== 'function'); - assert.equal(ret, 'changed'); - } + ['@test should replace the function for a non-computed property and return the value']( + assert + ) { + let ret = object.set('method', 'changed'); + assert.equal(object.get('_method'), 'method'); // make sure this was NOT run + assert.ok(typeof object.get('method') !== 'function'); + assert.equal(ret, 'changed'); + } - ['@test should replace prover when property value is null'](assert) { - let ret = object.set('nullProperty', 'changed'); - assert.equal(object.get('nullProperty'), 'changed'); - assert.equal(ret, 'changed'); - } + ['@test should replace prover when property value is null'](assert) { + let ret = object.set('nullProperty', 'changed'); + assert.equal(object.get('nullProperty'), 'changed'); + assert.equal(ret, 'changed'); + } - ['@test should call unknownProperty with value when property is undefined'](assert) { - let ret = object.set('unknown', 'changed'); - assert.equal(object.get('_unknown'), 'changed'); - assert.equal(ret, 'changed'); + ['@test should call unknownProperty with value when property is undefined'](assert) { + let ret = object.set('unknown', 'changed'); + assert.equal(object.get('_unknown'), 'changed'); + assert.equal(ret, 'changed'); + } } - } -); + ); +} // .......................................................... // COMPUTED PROPERTIES // -moduleFor( - 'Computed properties', - class extends ObservableTestCase { - beforeEach() { - lookup = context.lookup = {}; - - object = ObservableObject.extend({ - computed: computed({ - get() { - this.computedCalls.push('getter-called'); - return 'computed'; - }, - set(key, value) { - this.computedCalls.push(value); - }, - }), +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + 'Computed properties', + class extends ObservableTestCase { + beforeEach() { + expectClassicClassDeprecation(); + + lookup = context.lookup = {}; + + object = ObservableObject.extend({ + computed: computed({ + get() { + this.computedCalls.push('getter-called'); + return 'computed'; + }, + set(key, value) { + this.computedCalls.push(value); + }, + }), - dependent: computed('changer', { - get() { - this.dependentCalls.push('getter-called'); - return 'dependent'; - }, - set(key, value) { - this.dependentCalls.push(value); - }, - }), + dependent: computed('changer', { + get() { + this.dependentCalls.push('getter-called'); + return 'dependent'; + }, + set(key, value) { + this.dependentCalls.push(value); + }, + }), - inc: computed('changer', function () { - return this.incCallCount++; - }), + inc: computed('changer', function () { + return this.incCallCount++; + }), - nestedInc: computed('inc', function () { - get(this, 'inc'); - return this.nestedIncCallCount++; - }), + nestedInc: computed('inc', function () { + get(this, 'inc'); + return this.nestedIncCallCount++; + }), - isOn: computed('state', { - get() { - return this.get('state') === 'on'; - }, - set() { - this.set('state', 'on'); - return this.get('state') === 'on'; - }, - }), + isOn: computed('state', { + get() { + return this.get('state') === 'on'; + }, + set() { + this.set('state', 'on'); + return this.get('state') === 'on'; + }, + }), - isOff: computed('state', { - get() { - return this.get('state') === 'off'; - }, - set() { - this.set('state', 'off'); - return this.get('state') === 'off'; - }, - }), - }).create({ - computedCalls: [], - changer: 'foo', - dependentCalls: [], - incCallCount: 0, - nestedIncCallCount: 0, - state: 'on', - }); - } + isOff: computed('state', { + get() { + return this.get('state') === 'off'; + }, + set() { + this.set('state', 'off'); + return this.get('state') === 'off'; + }, + }), + }).create({ + computedCalls: [], + changer: 'foo', + dependentCalls: [], + incCallCount: 0, + nestedIncCallCount: 0, + state: 'on', + }); + } - ['@test getting values should call function return value'](assert) { - // get each property twice. Verify return. - let keys = ['computed', 'dependent']; + ['@test getting values should call function return value'](assert) { + // get each property twice. Verify return. + let keys = ['computed', 'dependent']; - keys.forEach(function (key) { - assert.equal(object.get(key), key, `Try #1: object.get(${key}) should run function`); - assert.equal(object.get(key), key, `Try #2: object.get(${key}) should run function`); - }); + keys.forEach(function (key) { + assert.equal(object.get(key), key, `Try #1: object.get(${key}) should run function`); + assert.equal(object.get(key), key, `Try #2: object.get(${key}) should run function`); + }); - // verify each call count. cached should only be called once - ['computedCalls', 'dependentCalls'].forEach((key) => { - assert.equal(object[key].length, 1, `non-cached property ${key} should be called 1x`); - }); - } + // verify each call count. cached should only be called once + ['computedCalls', 'dependentCalls'].forEach((key) => { + assert.equal(object[key].length, 1, `non-cached property ${key} should be called 1x`); + }); + } - ['@test setting values should call function return value'](assert) { - // get each property twice. Verify return. - let keys = ['computed', 'dependent']; - let values = ['value1', 'value2']; + ['@test setting values should call function return value'](assert) { + // get each property twice. Verify return. + let keys = ['computed', 'dependent']; + let values = ['value1', 'value2']; - keys.forEach((key) => { - assert.equal( - object.set(key, values[0]), - values[0], - `Try #1: object.set(${key}, ${values[0]}) should run function` - ); + keys.forEach((key) => { + assert.equal( + object.set(key, values[0]), + values[0], + `Try #1: object.set(${key}, ${values[0]}) should run function` + ); - assert.equal( - object.set(key, values[1]), - values[1], - `Try #2: object.set(${key}, ${values[1]}) should run function` - ); + assert.equal( + object.set(key, values[1]), + values[1], + `Try #2: object.set(${key}, ${values[1]}) should run function` + ); - assert.equal( - object.set(key, values[1]), - values[1], - `Try #3: object.set(${key}, ${values[1]}) should not run function since it is setting same value as before` - ); - }); + assert.equal( + object.set(key, values[1]), + values[1], + `Try #3: object.set(${key}, ${values[1]}) should not run function since it is setting same value as before` + ); + }); - // verify each call count. cached should only be called once - keys.forEach((key) => { - let calls = object[key + 'Calls']; - let idx, expectedLength; + // verify each call count. cached should only be called once + keys.forEach((key) => { + let calls = object[key + 'Calls']; + let idx, expectedLength; - // Cached properties first check their cached value before setting the - // property. Other properties blindly call set. - expectedLength = 3; - assert.equal( - calls.length, - expectedLength, - `set(${key}) should be called the right amount of times` - ); - for (idx = 0; idx < 2; idx++) { + // Cached properties first check their cached value before setting the + // property. Other properties blindly call set. + expectedLength = 3; assert.equal( - calls[idx], - values[idx], - `call #${idx + 1} to set(${key}) should have passed value ${values[idx]}` + calls.length, + expectedLength, + `set(${key}) should be called the right amount of times` ); - } - }); - } + for (idx = 0; idx < 2; idx++) { + assert.equal( + calls[idx], + values[idx], + `call #${idx + 1} to set(${key}) should have passed value ${values[idx]}` + ); + } + }); + } - ['@test notify change should clear cache'](assert) { - // call get several times to collect call count - object.get('computed'); // should run func - object.get('computed'); // should not run func + ['@test notify change should clear cache'](assert) { + // call get several times to collect call count + object.get('computed'); // should run func + object.get('computed'); // should not run func - object.notifyPropertyChange('computed'); + object.notifyPropertyChange('computed'); - object.get('computed'); // should run again - assert.equal(object.computedCalls.length, 2, 'should have invoked method 2x'); - } + object.get('computed'); // should run again + assert.equal(object.computedCalls.length, 2, 'should have invoked method 2x'); + } - ['@test change dependent should clear cache'](assert) { - // call get several times to collect call count - let ret1 = object.get('inc'); // should run func - assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); + ['@test change dependent should clear cache'](assert) { + // call get several times to collect call count + let ret1 = object.get('inc'); // should run func + assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); - object.set('changer', 'bar'); + object.set('changer', 'bar'); - assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again - } + assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again + } - ['@test just notifying change of dependent should clear cache'](assert) { - // call get several times to collect call count - let ret1 = object.get('inc'); // should run func - assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); + ['@test just notifying change of dependent should clear cache'](assert) { + // call get several times to collect call count + let ret1 = object.get('inc'); // should run func + assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); - object.notifyPropertyChange('changer'); + object.notifyPropertyChange('changer'); - assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again - } + assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again + } - ['@test changing dependent should clear nested cache'](assert) { - // call get several times to collect call count - let ret1 = object.get('nestedInc'); // should run func - assert.equal(object.get('nestedInc'), ret1, 'multiple calls should not run cached prop'); + ['@test changing dependent should clear nested cache'](assert) { + // call get several times to collect call count + let ret1 = object.get('nestedInc'); // should run func + assert.equal(object.get('nestedInc'), ret1, 'multiple calls should not run cached prop'); - object.set('changer', 'bar'); + object.set('changer', 'bar'); - assert.equal( - object.get('nestedInc'), - ret1 + 1, - 'should increment after dependent key changes' - ); // should run again - } + assert.equal( + object.get('nestedInc'), + ret1 + 1, + 'should increment after dependent key changes' + ); // should run again + } - ['@test just notifying change of dependent should clear nested cache'](assert) { - // call get several times to collect call count - let ret1 = object.get('nestedInc'); // should run func - assert.equal(object.get('nestedInc'), ret1, 'multiple calls should not run cached prop'); + ['@test just notifying change of dependent should clear nested cache'](assert) { + // call get several times to collect call count + let ret1 = object.get('nestedInc'); // should run func + assert.equal(object.get('nestedInc'), ret1, 'multiple calls should not run cached prop'); - object.notifyPropertyChange('changer'); + object.notifyPropertyChange('changer'); - assert.equal( - object.get('nestedInc'), - ret1 + 1, - 'should increment after dependent key changes' - ); // should run again - } + assert.equal( + object.get('nestedInc'), + ret1 + 1, + 'should increment after dependent key changes' + ); // should run again + } - // This verifies a specific bug encountered where observers for computed - // properties would fire before their prop caches were cleared. - ['@test change dependent should clear cache when observers of dependent are called'](assert) { - // call get several times to collect call count - let ret1 = object.get('inc'); // should run func - assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); + // This verifies a specific bug encountered where observers for computed + // properties would fire before their prop caches were cleared. + ['@test change dependent should clear cache when observers of dependent are called'](assert) { + // call get several times to collect call count + let ret1 = object.get('inc'); // should run func + assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); - // add observer to verify change... - object.addObserver('inc', this, function () { - assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again - }); + // add observer to verify change... + object.addObserver('inc', this, function () { + assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again + }); - // now run - object.set('changer', 'bar'); - } + // now run + object.set('changer', 'bar'); + } - ['@test setting one of two computed properties that depend on a third property should clear the kvo cache']( - assert - ) { - // we have to call set twice to fill up the cache - object.set('isOff', true); - object.set('isOn', true); - - // setting isOff to true should clear the kvo cache - object.set('isOff', true); - assert.equal(object.get('isOff'), true, 'object.isOff should be true'); - assert.equal(object.get('isOn'), false, 'object.isOn should be false'); - } + ['@test setting one of two computed properties that depend on a third property should clear the kvo cache']( + assert + ) { + // we have to call set twice to fill up the cache + object.set('isOff', true); + object.set('isOn', true); + + // setting isOff to true should clear the kvo cache + object.set('isOff', true); + assert.equal(object.get('isOff'), true, 'object.isOff should be true'); + assert.equal(object.get('isOn'), false, 'object.isOn should be false'); + } - ['@test dependent keys should be able to be specified as property paths'](assert) { - let depObj = ObservableObject.extend({ - menuPrice: computed('menu.price', function () { - return this.get('menu.price'); - }), - }).create({ - menu: ObservableObject.create({ - price: 5, - }), - }); + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test dependent keys should be able to be specified as property paths`]( + assert + ) { + expectClassicClassDeprecation(); - assert.equal(depObj.get('menuPrice'), 5, 'precond - initial value returns 5'); + let depObj = ObservableObject.extend({ + menuPrice: computed('menu.price', function () { + return this.get('menu.price'); + }), + }).create({ + menu: ObservableObject.create({ + price: 5, + }), + }); - depObj.set('menu.price', 6); + assert.equal(depObj.get('menuPrice'), 5, 'precond - initial value returns 5'); - assert.equal( - depObj.get('menuPrice'), - 6, - 'cache is properly invalidated after nested property changes' - ); - } + depObj.set('menu.price', 6); - ['@test cacheable nested dependent keys should clear after their dependencies update'](assert) { - assert.ok(true); + assert.equal( + depObj.get('menuPrice'), + 6, + 'cache is properly invalidated after nested property changes' + ); + } - let DepObj; + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test cacheable nested dependent keys should clear after their dependencies update`]( + assert + ) { + expectClassicClassDeprecation(); - run(function () { - lookup.DepObj = DepObj = ObservableObject.extend({ - price: computed('restaurant.menu.price', function () { - return this.get('restaurant.menu.price'); - }), - }).create({ - restaurant: ObservableObject.create({ - menu: ObservableObject.create({ - price: 5, + assert.ok(true); + + let DepObj; + + run(function () { + lookup.DepObj = DepObj = ObservableObject.extend({ + price: computed('restaurant.menu.price', function () { + return this.get('restaurant.menu.price'); }), - }), + }).create({ + restaurant: ObservableObject.create({ + menu: ObservableObject.create({ + price: 5, + }), + }), + }); }); - }); - assert.equal(DepObj.get('price'), 5, 'precond - computed property is correct'); + assert.equal(DepObj.get('price'), 5, 'precond - computed property is correct'); - run(function () { - DepObj.set('restaurant.menu.price', 10); - }); - assert.equal( - DepObj.get('price'), - 10, - 'cacheable computed properties are invalidated even if no run loop occurred' - ); - - run(function () { - DepObj.set('restaurant.menu.price', 20); - }); - assert.equal( - DepObj.get('price'), - 20, - 'cacheable computed properties are invalidated after a second get before a run loop' - ); - assert.equal( - DepObj.get('price'), - 20, - 'precond - computed properties remain correct after a run loop' - ); - - run(function () { - DepObj.set( - 'restaurant.menu', - ObservableObject.create({ - price: 15, - }) + run(function () { + DepObj.set('restaurant.menu.price', 10); + }); + assert.equal( + DepObj.get('price'), + 10, + 'cacheable computed properties are invalidated even if no run loop occurred' ); - }); - assert.equal( - DepObj.get('price'), - 15, - 'cacheable computed properties are invalidated after a middle property changes' - ); - - run(function () { - DepObj.set( - 'restaurant.menu', - ObservableObject.create({ - price: 25, - }) + run(function () { + DepObj.set('restaurant.menu.price', 20); + }); + assert.equal( + DepObj.get('price'), + 20, + 'cacheable computed properties are invalidated after a second get before a run loop' + ); + assert.equal( + DepObj.get('price'), + 20, + 'precond - computed properties remain correct after a run loop' ); - }); - assert.equal( - DepObj.get('price'), - 25, - 'cacheable computed properties are invalidated after a middle property changes again, before a run loop' - ); + run(function () { + DepObj.set( + 'restaurant.menu', + ObservableObject.create({ + price: 15, + }) + ); + }); + + assert.equal( + DepObj.get('price'), + 15, + 'cacheable computed properties are invalidated after a middle property changes' + ); + + run(function () { + DepObj.set( + 'restaurant.menu', + ObservableObject.create({ + price: 25, + }) + ); + }); + + assert.equal( + DepObj.get('price'), + 25, + 'cacheable computed properties are invalidated after a middle property changes again, before a run loop' + ); + } } - } -); + ); +} // .......................................................... // OBSERVABLE OBJECTS // -moduleFor( - 'Observable objects & object properties ', - class extends ObservableTestCase { - beforeEach() { - object = ObservableObject.extend({ - getEach() { - let keys = ['normal', 'abnormal']; - let ret = []; - for (let idx = 0; idx < keys.length; idx++) { - ret[ret.length] = this.get(keys[idx]); - } - return ret; - }, +if (!DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved) { + moduleFor( + 'Observable objects & object properties ', + class extends ObservableTestCase { + beforeEach() { + expectClassicClassDeprecation(); + + object = ObservableObject.extend({ + getEach() { + let keys = ['normal', 'abnormal']; + let ret = []; + for (let idx = 0; idx < keys.length; idx++) { + ret[ret.length] = this.get(keys[idx]); + } + return ret; + }, - newObserver() { - this.abnormal = 'changedValueObserved'; - }, + newObserver() { + this.abnormal = 'changedValueObserved'; + }, - testObserver: observer('normal', function () { - this.abnormal = 'removedObserver'; - }), + testObserver: observer('normal', function () { + this.abnormal = 'removedObserver'; + }), - testArrayObserver: observer('normalArray.[]', function () { - this.abnormal = 'notifiedObserver'; - }), - }).create({ - normal: 'value', - abnormal: 'zeroValue', - numberVal: 24, - toggleVal: true, - observedProperty: 'beingWatched', - testRemove: 'observerToBeRemoved', - normalArray: emberA([1, 2, 3, 4, 5]), - }); - } + testArrayObserver: observer('normalArray.[]', function () { + this.abnormal = 'notifiedObserver'; + }), + }).create({ + normal: 'value', + abnormal: 'zeroValue', + numberVal: 24, + toggleVal: true, + observedProperty: 'beingWatched', + testRemove: 'observerToBeRemoved', + normalArray: emberA([1, 2, 3, 4, 5]), + }); + } - ['@test incrementProperty and decrementProperty'](assert) { - let newValue = object.incrementProperty('numberVal'); - - assert.equal(25, newValue, 'numerical value incremented'); - object.numberVal = 24; - newValue = object.decrementProperty('numberVal'); - assert.equal(23, newValue, 'numerical value decremented'); - object.numberVal = 25; - newValue = object.incrementProperty('numberVal', 5); - assert.equal(30, newValue, 'numerical value incremented by specified increment'); - object.numberVal = 25; - newValue = object.incrementProperty('numberVal', -5); - assert.equal(20, newValue, 'minus numerical value incremented by specified increment'); - object.numberVal = 25; - newValue = object.incrementProperty('numberVal', 0); - assert.equal(25, newValue, 'zero numerical value incremented by specified increment'); - - expectAssertion(function () { - newValue = object.incrementProperty('numberVal', 0 - void 0); // Increment by NaN - }, /Must pass a numeric value to incrementProperty/i); - - expectAssertion(function () { - newValue = object.incrementProperty('numberVal', 'Ember'); // Increment by non-numeric String - }, /Must pass a numeric value to incrementProperty/i); - - expectAssertion(function () { - newValue = object.incrementProperty('numberVal', 1 / 0); // Increment by Infinity - }, /Must pass a numeric value to incrementProperty/i); - - assert.equal( - 25, - newValue, - 'Attempting to increment by non-numeric values should not increment value' - ); - - object.numberVal = 25; - newValue = object.decrementProperty('numberVal', 5); - assert.equal(20, newValue, 'numerical value decremented by specified increment'); - object.numberVal = 25; - newValue = object.decrementProperty('numberVal', -5); - assert.equal(30, newValue, 'minus numerical value decremented by specified increment'); - object.numberVal = 25; - newValue = object.decrementProperty('numberVal', 0); - assert.equal(25, newValue, 'zero numerical value decremented by specified increment'); - - expectAssertion(function () { - newValue = object.decrementProperty('numberVal', 0 - void 0); // Decrement by NaN - }, /Must pass a numeric value to decrementProperty/i); - - expectAssertion(function () { - newValue = object.decrementProperty('numberVal', 'Ember'); // Decrement by non-numeric String - }, /Must pass a numeric value to decrementProperty/i); - - expectAssertion(function () { - newValue = object.decrementProperty('numberVal', 1 / 0); // Decrement by Infinity - }, /Must pass a numeric value to decrementProperty/i); - - assert.equal( - 25, - newValue, - 'Attempting to decrement by non-numeric values should not decrement value' - ); - } + ['@test incrementProperty and decrementProperty'](assert) { + let newValue = object.incrementProperty('numberVal'); + + assert.equal(25, newValue, 'numerical value incremented'); + object.numberVal = 24; + newValue = object.decrementProperty('numberVal'); + assert.equal(23, newValue, 'numerical value decremented'); + object.numberVal = 25; + newValue = object.incrementProperty('numberVal', 5); + assert.equal(30, newValue, 'numerical value incremented by specified increment'); + object.numberVal = 25; + newValue = object.incrementProperty('numberVal', -5); + assert.equal(20, newValue, 'minus numerical value incremented by specified increment'); + object.numberVal = 25; + newValue = object.incrementProperty('numberVal', 0); + assert.equal(25, newValue, 'zero numerical value incremented by specified increment'); + + expectAssertion(function () { + newValue = object.incrementProperty('numberVal', 0 - void 0); // Increment by NaN + }, /Must pass a numeric value to incrementProperty/i); + + expectAssertion(function () { + newValue = object.incrementProperty('numberVal', 'Ember'); // Increment by non-numeric String + }, /Must pass a numeric value to incrementProperty/i); + + expectAssertion(function () { + newValue = object.incrementProperty('numberVal', 1 / 0); // Increment by Infinity + }, /Must pass a numeric value to incrementProperty/i); - ['@test toggle function, should be boolean'](assert) { - assert.equal(object.toggleProperty('toggleVal', true, false), object.get('toggleVal')); - assert.equal(object.toggleProperty('toggleVal', true, false), object.get('toggleVal')); - assert.equal( - object.toggleProperty('toggleVal', undefined, undefined), - object.get('toggleVal') - ); - } + assert.equal( + 25, + newValue, + 'Attempting to increment by non-numeric values should not increment value' + ); - async ['@test should notify array observer when array changes'](assert) { - get(object, 'normalArray').replace(0, 0, [6]); - await runLoopSettled(); + object.numberVal = 25; + newValue = object.decrementProperty('numberVal', 5); + assert.equal(20, newValue, 'numerical value decremented by specified increment'); + object.numberVal = 25; + newValue = object.decrementProperty('numberVal', -5); + assert.equal(30, newValue, 'minus numerical value decremented by specified increment'); + object.numberVal = 25; + newValue = object.decrementProperty('numberVal', 0); + assert.equal(25, newValue, 'zero numerical value decremented by specified increment'); + + expectAssertion(function () { + newValue = object.decrementProperty('numberVal', 0 - void 0); // Decrement by NaN + }, /Must pass a numeric value to decrementProperty/i); + + expectAssertion(function () { + newValue = object.decrementProperty('numberVal', 'Ember'); // Decrement by non-numeric String + }, /Must pass a numeric value to decrementProperty/i); - assert.equal(object.abnormal, 'notifiedObserver', 'observer should be notified'); + expectAssertion(function () { + newValue = object.decrementProperty('numberVal', 1 / 0); // Decrement by Infinity + }, /Must pass a numeric value to decrementProperty/i); + + assert.equal( + 25, + newValue, + 'Attempting to decrement by non-numeric values should not decrement value' + ); + } + + ['@test toggle function, should be boolean'](assert) { + assert.equal(object.toggleProperty('toggleVal', true, false), object.get('toggleVal')); + assert.equal(object.toggleProperty('toggleVal', true, false), object.get('toggleVal')); + assert.equal( + object.toggleProperty('toggleVal', undefined, undefined), + object.get('toggleVal') + ); + } + + async ['@test should notify array observer when array changes'](assert) { + get(object, 'normalArray').replace(0, 0, [6]); + await runLoopSettled(); + + assert.equal(object.abnormal, 'notifiedObserver', 'observer should be notified'); + } } - } -); + ); +} moduleFor( 'object.addObserver()', diff --git a/packages/@ember/object/tests/observer_test.js b/packages/@ember/object/tests/observer_test.js index 3e181e2e007..aaf6ac8593f 100644 --- a/packages/@ember/object/tests/observer_test.js +++ b/packages/@ember/object/tests/observer_test.js @@ -1,12 +1,23 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { run } from '@ember/runloop'; import { alias } from '@ember/-internals/metal'; import EmberObject, { get, set, observer } from '@ember/object'; -import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + runLoopSettled, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; moduleFor( 'EmberObject observer', class extends AbstractTestCase { - async ['@test observer on class'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observer on class`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ count: 0, @@ -26,7 +37,11 @@ moduleFor( obj.destroy(); } - async ['@test setting `undefined` value on observed property behaves correctly'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test setting \`undefined\` value on observed property behaves correctly`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ mood: 'good', foo: observer('mood', function () {}), @@ -53,7 +68,11 @@ moduleFor( obj.destroy(); } - async ['@test observer on subclass'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observer on subclass`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ count: 0, @@ -84,7 +103,11 @@ moduleFor( obj.destroy(); } - async ['@test observer on instance'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observer on instance`]( + assert + ) { + expectClassicClassDeprecation(); + let obj = EmberObject.extend({ foo: observer('bar', function () { set(this, 'count', get(this, 'count') + 1); @@ -104,7 +127,11 @@ moduleFor( await runLoopSettled(); } - async ['@test observer on instance overriding class'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observer on instance overriding class`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ count: 0, @@ -135,7 +162,11 @@ moduleFor( obj.destroy(); } - async ['@test observer should not fire after being destroyed'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test observer should not fire after being destroyed`]( + assert + ) { + expectClassicClassDeprecation(); + let obj = EmberObject.extend({ count: 0, foo: observer('bar', function () { @@ -160,7 +191,11 @@ moduleFor( // COMPLEX PROPERTIES // - async ['@test chain observer on class'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test chain observer on class`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ count: 0, @@ -196,7 +231,11 @@ moduleFor( obj2.destroy(); } - async ['@test clobbering a chain observer on subclass'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test clobbering a chain observer on subclass`]( + assert + ) { + expectClassicClassDeprecation(); + let MyClass = EmberObject.extend({ count: 0, @@ -243,9 +282,11 @@ moduleFor( obj2.destroy(); } - async ['@test chain observer on class that has a reference to an uninitialized object will finish chains that reference it']( + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test chain observer on class that has a reference to an uninitialized object will finish chains that reference it`]( assert ) { + expectClassicClassDeprecation(); + let changed = false; let ChildClass = EmberObject.extend({ @@ -284,7 +325,11 @@ moduleFor( parent.destroy(); } - async ['@test cannot re-enter observer while it is flushing'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test cannot re-enter observer while it is flushing`]( + assert + ) { + expectClassicClassDeprecation(); + let changed = false; let Class = EmberObject.extend({ diff --git a/packages/@ember/object/tests/reopenClass_test.js b/packages/@ember/object/tests/reopenClass_test.js index 76d562a2076..1d7793f02a8 100644 --- a/packages/@ember/object/tests/reopenClass_test.js +++ b/packages/@ember/object/tests/reopenClass_test.js @@ -1,11 +1,21 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { get } from '@ember/object'; import EmberObject from '@ember/object'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; moduleFor( 'system/object/reopenClass', class extends AbstractTestCase { - ['@test adds new properties to subclass'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test adds new properties to subclass`]( + assert + ) { + expectClassicClassDeprecation(); + let Subclass = class extends EmberObject {}; Subclass.reopenClass({ foo() { @@ -18,7 +28,11 @@ moduleFor( assert.equal(get(Subclass, 'bar'), 'BAR', 'Adds property'); } - ['@test class properties inherited by subclasses'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test class properties inherited by subclasses`]( + assert + ) { + expectClassicClassDeprecation(); + let Subclass = class extends EmberObject {}; Subclass.reopenClass({ foo() { diff --git a/packages/@ember/object/tests/reopen_test.js b/packages/@ember/object/tests/reopen_test.js index a68e236fb42..9dab0c14e23 100644 --- a/packages/@ember/object/tests/reopen_test.js +++ b/packages/@ember/object/tests/reopen_test.js @@ -1,11 +1,21 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import EmberObject, { get } from '@ember/object'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; // TODO: Update these tests (or the title) to match each other. moduleFor( 'system/core_object/reopen', class extends AbstractTestCase { - ['@test adds new properties to subclass instance'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test adds new properties to subclass instance`]( + assert + ) { + expectClassicClassDeprecation(); + let Subclass = class extends EmberObject {}; Subclass.reopen({ foo() { @@ -18,7 +28,11 @@ moduleFor( assert.equal(get(Subclass.create(), 'bar'), 'BAR', 'Adds property'); } - ['@test reopened properties inherited by subclasses'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test reopened properties inherited by subclasses`]( + assert + ) { + expectClassicClassDeprecation(); + let Subclass = class extends EmberObject {}; let SubSub = class extends Subclass {}; @@ -33,7 +47,11 @@ moduleFor( assert.equal(get(SubSub.create(), 'bar'), 'BAR', 'Adds property'); } - ['@test allows reopening already instantiated classes'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test allows reopening already instantiated classes`]( + assert + ) { + expectClassicClassDeprecation(); + let Subclass = class extends EmberObject {}; Subclass.create(); diff --git a/packages/@ember/routing/lib/routing-service.ts b/packages/@ember/routing/lib/routing-service.ts index b44e2daf70c..c185776e466 100644 --- a/packages/@ember/routing/lib/routing-service.ts +++ b/packages/@ember/routing/lib/routing-service.ts @@ -11,6 +11,7 @@ import type { ModelFor } from '@ember/routing/route'; import EmberRouter from '@ember/routing/router'; import type RouterState from './router_state'; import { ROUTER } from '@ember/routing/router-service'; +import { INTERNAL_REOPEN } from '@ember/-internals/utils/lib/internal-classic-class'; /** The Routing service is used by LinkTo, and provides facilities for @@ -128,7 +129,7 @@ export default class RoutingService extends Service { } } -RoutingService.reopen({ +RoutingService[INTERNAL_REOPEN]({ targetState: readOnly('router.targetState'), currentState: readOnly('router.currentState'), currentRouteName: readOnly('router.currentRouteName'), diff --git a/packages/@ember/routing/none-location.ts b/packages/@ember/routing/none-location.ts index e396d8ef335..c697ce974ff 100644 --- a/packages/@ember/routing/none-location.ts +++ b/packages/@ember/routing/none-location.ts @@ -2,6 +2,7 @@ import EmberObject from '@ember/object'; import { assert } from '@ember/debug'; import type { default as EmberLocation, UpdateCallback } from '@ember/routing/location'; import { escapeRegExp } from './lib/location-utils'; +import { INTERNAL_REOPEN } from '@ember/-internals/utils/lib/internal-classic-class'; /** @module @ember/routing/none-location @@ -127,7 +128,7 @@ export default class NoneLocation extends EmberObject implements EmberLocation { } } -NoneLocation.reopen({ +NoneLocation[INTERNAL_REOPEN]({ path: '', rootURL: '/', }); diff --git a/packages/@ember/routing/route.ts b/packages/@ember/routing/route.ts index d3078d53886..17a3da60f8d 100644 --- a/packages/@ember/routing/route.ts +++ b/packages/@ember/routing/route.ts @@ -48,6 +48,10 @@ import { queryParamType, stashParamNames, } from './lib/utils'; +import { + INTERNAL_EXTEND, + INTERNAL_REOPEN, +} from '@ember/-internals/utils/lib/internal-classic-class'; export interface ExtendedInternalRouteInfo extends InternalRouteInfo> { _names?: unknown[]; @@ -281,7 +285,7 @@ interface Route extends ActionHandler { error?(error: Error, transition: Transition): boolean | void; } -class Route extends EmberObject.extend(ActionHandler) { +class Route extends EmberObject[INTERNAL_EXTEND](ActionHandler) { static { // The deprecated Evented mixin is no longer applied, but instances still // provide its methods, so `Evented.detect` must keep returning true. @@ -1982,7 +1986,7 @@ export function hasDefaultSerialize(route: Route): boolean { } // Set these here so they can be overridden with extend -Route.reopen({ +Route[INTERNAL_REOPEN]({ mergedProperties: ['queryParams'], queryParams: {}, templateName: null, diff --git a/packages/@ember/routing/router.ts b/packages/@ember/routing/router.ts index 3f6a356eaa1..0dece25d53c 100644 --- a/packages/@ember/routing/router.ts +++ b/packages/@ember/routing/router.ts @@ -70,6 +70,10 @@ import { eventedOff, eventedHas, } from '@ember/-internals/metal/lib/evented-methods'; +import { + INTERNAL_REOPEN, + INTERNAL_REOPEN_CLASS, +} from '@ember/-internals/utils/lib/internal-classic-class'; /** @module @ember/routing/router @@ -303,7 +307,7 @@ class EmberRouter extends EmberObject { if (!this.dslCallbacks) { this.dslCallbacks = []; // FIXME: Can we remove this? - this.reopenClass({ dslCallbacks: this.dslCallbacks }); + this[INTERNAL_REOPEN_CLASS]({ dslCallbacks: this.dslCallbacks }); } this.dslCallbacks.push(callback); @@ -1829,7 +1833,7 @@ function forEachQueryParam( } } -EmberRouter.reopen({ +EmberRouter[INTERNAL_REOPEN]({ didTransition: defaultDidTransition, willTransition: defaultWillTransition, rootURL: '/', diff --git a/packages/@ember/routing/tests/location/history_location_test.js b/packages/@ember/routing/tests/location/history_location_test.js index 68505a5a3ec..6b7654dbd19 100644 --- a/packages/@ember/routing/tests/location/history_location_test.js +++ b/packages/@ember/routing/tests/location/history_location_test.js @@ -68,15 +68,16 @@ moduleFor( ['@test HistoryLocation initState does not get fired on init'](assert) { assert.expect(1); - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { assert.ok(true, 'init was called'); - this._super(...arguments); - }, + super.init(...arguments); + } + initState() { assert.ok(false, 'initState() should not be called automatically'); - }, - }); + } + }; createLocation(); } @@ -120,20 +121,20 @@ moduleFor( ['@test base URL is removed when retrieving the current pathname'](assert) { assert.expect(1); - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/base/foo/bar')); set(this, 'baseURL', '/base/'); - }, + } initState() { - this._super(...arguments); + super.initState(...arguments); assert.equal(this.getURL(), '/foo/bar'); - }, - }); + } + }; createLocation(); location.initState(); @@ -142,14 +143,14 @@ moduleFor( ['@test base URL is preserved when moving around'](assert) { assert.expect(2); - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/base/foo/bar')); set(this, 'baseURL', '/base/'); - }, - }); + } + }; createLocation(); location.initState(); @@ -184,15 +185,15 @@ moduleFor( ['@test HistoryLocation.getURL() returns the current url, excluding both rootURL and baseURL']( assert ) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/base/foo/bar')); set(this, 'rootURL', '/app/'); set(this, 'baseURL', '/base/'); - }, - }); + } + }; createLocation(); @@ -202,14 +203,14 @@ moduleFor( ['@test HistoryLocation.getURL() returns the current url, does not remove rootURL if its not at start of url']( assert ) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/foo/bar/baz')); set(this, 'rootURL', '/bar/'); - }, - }); + } + }; createLocation(); @@ -219,13 +220,13 @@ moduleFor( ['@test HistoryLocation.getURL() will not remove the rootURL when only a partial match']( assert ) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/bars/baz')); set(this, 'rootURL', '/bar/'); - }, - }); + } + }; createLocation(); @@ -235,14 +236,14 @@ moduleFor( ['@test HistoryLocation.getURL() returns the current url, does not remove baseURL if its not at start of url']( assert ) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/foo/bar/baz')); set(this, 'baseURL', '/bar/'); - }, - }); + } + }; createLocation(); @@ -252,13 +253,13 @@ moduleFor( ['@test HistoryLocation.getURL() will not remove the baseURL when only a partial match']( assert ) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/bars/baz')); set(this, 'baseURL', '/bar/'); - }, - }); + } + }; createLocation(); @@ -266,12 +267,12 @@ moduleFor( } ['@test HistoryLocation.getURL() includes location.search'](assert) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/foo/bar?time=morphin')); - }, - }); + } + }; createLocation(); @@ -279,12 +280,12 @@ moduleFor( } ['@test HistoryLocation.getURL() includes location.hash'](assert) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/foo/bar#pink-power-ranger')); - }, - }); + } + }; createLocation(); @@ -292,12 +293,12 @@ moduleFor( } ['@test HistoryLocation.getURL() includes location.hash and location.search'](assert) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/foo/bar?time=morphin#pink-power-ranger')); - }, - }); + } + }; createLocation(); @@ -305,14 +306,14 @@ moduleFor( } ['@test HistoryLocation.getURL() drops duplicate slashes'](assert) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); let location = mockBrowserLocation('//admin//profile//'); location.pathname = '//admin//profile//'; // mockBrowserLocation does not allow for `//`, so force it set(this, 'location', location); - }, - }); + } + }; createLocation(); @@ -322,14 +323,14 @@ moduleFor( ['@test HistoryLocation.getURL() treats regex metacharacters in rootURL and baseURL literally']( assert ) { - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/axc/secret')); set(this, 'rootURL', '/a.c/'); set(this, 'baseURL', '/a.c/'); - }, - }); + } + }; createLocation(); @@ -344,12 +345,12 @@ moduleFor( FakeHistory.state = existingState; - HistoryTestLocation.reopen({ + HistoryTestLocation = class extends HistoryTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'location', mockBrowserLocation('/route/path')); - }, - }); + } + }; createLocation(); location.initState(); diff --git a/packages/@ember/routing/tests/location/none_location_test.js b/packages/@ember/routing/tests/location/none_location_test.js index 04f77db057b..ca1091f6d89 100644 --- a/packages/@ember/routing/tests/location/none_location_test.js +++ b/packages/@ember/routing/tests/location/none_location_test.js @@ -29,12 +29,12 @@ moduleFor( } ['@test NoneLocation.formatURL() returns the current url always appending rootURL'](assert) { - NoneTestLocation.reopen({ + NoneTestLocation = class extends NoneTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'rootURL', '/en/'); - }, - }); + } + }; createLocation(); @@ -42,13 +42,13 @@ moduleFor( } ['@test NoneLocation.getURL() returns the current path minus rootURL'](assert) { - NoneTestLocation.reopen({ + NoneTestLocation = class extends NoneTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'rootURL', '/foo/'); set(this, 'path', '/foo/bar'); - }, - }); + } + }; createLocation(); @@ -58,13 +58,13 @@ moduleFor( ['@test NoneLocation.getURL() will remove the rootURL only from the beginning of a url']( assert ) { - NoneTestLocation.reopen({ + NoneTestLocation = class extends NoneTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'rootURL', '/bar/'); set(this, 'path', '/foo/bar/baz'); - }, - }); + } + }; createLocation(); @@ -72,13 +72,13 @@ moduleFor( } ['@test NoneLocation.getURL() will not remove the rootURL when only a partial match'](assert) { - NoneTestLocation.reopen({ + NoneTestLocation = class extends NoneTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'rootURL', '/bar/'); set(this, 'path', '/bars/baz'); - }, - }); + } + }; createLocation(); @@ -86,13 +86,13 @@ moduleFor( } ['@test NoneLocation.getURL() treats regex metacharacters in rootURL literally'](assert) { - NoneTestLocation.reopen({ + NoneTestLocation = class extends NoneTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'rootURL', '/a.c/'); set(this, 'path', '/axc/secret'); - }, - }); + } + }; createLocation(); @@ -100,13 +100,13 @@ moduleFor( } ['@test NoneLocation.getURL() strips the rootURL when it has an extra trailing slash'](assert) { - NoneTestLocation.reopen({ + NoneTestLocation = class extends NoneTestLocation { init() { - this._super(...arguments); + super.init(...arguments); set(this, 'rootURL', '/foo//'); set(this, 'path', '/foo//bar'); - }, - }); + } + }; createLocation(); diff --git a/packages/@ember/routing/tests/system/dsl_test.js b/packages/@ember/routing/tests/system/dsl_test.js index 5b7d63df119..1d502629f3c 100644 --- a/packages/@ember/routing/tests/system/dsl_test.js +++ b/packages/@ember/routing/tests/system/dsl_test.js @@ -87,11 +87,7 @@ moduleFor( this.route('blork'); }); - this.routerInstance.reopen({ - _hasModuleBasedResolver() { - return true; - }, - }); + this.routerInstance._hasModuleBasedResolver = () => true; let router = this.routerInstance; router._initRouterJs(); @@ -133,11 +129,7 @@ moduleFor( }); }); - this.routerInstance.reopen({ - _hasModuleBasedResolver() { - return true; - }, - }); + this.routerInstance._hasModuleBasedResolver = () => true; let router = this.routerInstance; router._initRouterJs(); @@ -285,11 +277,7 @@ moduleFor( this.mount('chat'); }); - this.routerInstance.reopen({ - _hasModuleBasedResolver() { - return true; - }, - }); + this.routerInstance._hasModuleBasedResolver = () => true; this.routerInstance._initRouterJs(); let router = this.routerInstance; assert.ok(router._routerMicrolib.recognizer.names['chat'], 'main route was created'); @@ -304,11 +292,7 @@ moduleFor( this.mount('chat', { as: 'shoutbox' }); }); - this.routerInstance.reopen({ - _hasModuleBasedResolver() { - return true; - }, - }); + this.routerInstance._hasModuleBasedResolver = () => true; this.routerInstance._initRouterJs(); let router = this.routerInstance; @@ -351,11 +335,7 @@ moduleFor( }); }); - this.routerInstance.reopen({ - _hasModuleBasedResolver() { - return true; - }, - }); + this.routerInstance._hasModuleBasedResolver = () => true; this.routerInstance._initRouterJs(); let router = this.routerInstance; diff --git a/packages/@ember/utils/tests/compare_test.js b/packages/@ember/utils/tests/compare_test.js index 36c107bd826..9d1ef5be7b7 100644 --- a/packages/@ember/utils/tests/compare_test.js +++ b/packages/@ember/utils/tests/compare_test.js @@ -4,13 +4,11 @@ import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'inte import { DEPRECATIONS } from '@ember/-internals/deprecations'; let data = []; -let Comp = EmberObject.extend(); - -Comp.reopenClass({ - compare(obj) { +class Comp extends EmberObject { + static compare(obj) { return obj.get('val'); - }, -}); + } +} const UTILS = DEPRECATIONS.DEPRECATE_EMBER_UTILS; diff --git a/packages/@ember/utils/tests/type_of_test.js b/packages/@ember/utils/tests/type_of_test.js index c1739a7ae48..b2f32fd1bbd 100644 --- a/packages/@ember/utils/tests/type_of_test.js +++ b/packages/@ember/utils/tests/type_of_test.js @@ -45,7 +45,7 @@ moduleFor( assert.equal(typeOf(instance), 'instance', 'item of type instance'); assert.equal(typeOf(instance.method), 'function', 'item of type function'); assert.equal(typeOf(instance.asyncMethod), 'function', 'item of type async function'); - assert.equal(typeOf(EmberObject.extend()), 'class', 'item of type class'); + assert.equal(typeOf(class extends EmberObject {}), 'class', 'item of type class'); assert.equal(typeOf(new Error()), 'error', 'item of type error'); } diff --git a/packages/ember-testing/lib/adapters/adapter.ts b/packages/ember-testing/lib/adapters/adapter.ts index 26876da9d42..f26093b49c9 100644 --- a/packages/ember-testing/lib/adapters/adapter.ts +++ b/packages/ember-testing/lib/adapters/adapter.ts @@ -1,4 +1,5 @@ import EmberObject from '@ember/object'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; /** @module @ember/test @@ -16,7 +17,7 @@ interface Adapter extends EmberObject { asyncEnd(): void; exception(error: unknown): never; } -const Adapter = EmberObject.extend({ +const Adapter = EmberObject[INTERNAL_EXTEND]({ /** This callback will be called whenever an async operation is about to start. diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index c404f8dcd88..197bd23ed79 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -361,19 +361,23 @@ moduleFor( ['@test using replaceWith calls location.replaceURL if available'](assert) { let setCount = 0; let replaceCount = 0; - this.router.reopen({ - location: NoneLocation.create({ - setURL(path) { - setCount++; - set(this, 'path', path); - }, - - replaceURL(path) { - replaceCount++; - set(this, 'path', path); - }, - }), - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + location = NoneLocation.create({ + setURL(path) { + setCount++; + set(this, 'path', path); + }, + + replaceURL(path) { + replaceCount++; + set(this, 'path', path); + }, + }); + } + ); this.router.map(function () { this.route('root', { path: '/' }); @@ -396,14 +400,18 @@ moduleFor( ['@test using replaceWith calls setURL if location.replaceURL is not defined'](assert) { let setCount = 0; - this.router.reopen({ - location: NoneLocation.create({ - setURL(path) { - setCount++; - set(this, 'path', path); - }, - }), - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + location = NoneLocation.create({ + setURL(path) { + setCount++; + set(this, 'path', path); + }, + }); + } + ); this.router.map(function () { this.route('root', { path: '/' }); @@ -671,11 +679,15 @@ moduleFor( }, }); - this.router.reopen({ - // location: 'historyTest', - location, - rootURL: rootURL, - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + location = location; + + rootURL = rootURL; + } + ); this.router.map(function () { this.route('posts', { path: '/posts' }); @@ -716,19 +728,23 @@ moduleFor( } ); - this.router.reopen({ - location: 'history-test', - rootURL: rootURL, - // if we transition in this test we will receive failures - // if the tests are run from a static file - _doURLTransition() { - return { - followRedirects() { - return RSVP.resolve(''); - }, - }; - }, - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + location = 'history-test'; + + rootURL = rootURL; + + _doURLTransition() { + return { + followRedirects() { + return RSVP.resolve(''); + }, + }; + } + } + ); return this.visit('/'); } diff --git a/packages/ember/tests/routing/query_params_test.js b/packages/ember/tests/routing/query_params_test.js index 76a0214c0ed..85a4d42d809 100644 --- a/packages/ember/tests/routing/query_params_test.js +++ b/packages/ember/tests/routing/query_params_test.js @@ -1,3 +1,4 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import Controller from '@ember/controller'; import { dasherize } from '@ember/-internals/string'; import EmberObject, { action, get, computed } from '@ember/object'; @@ -11,7 +12,14 @@ import { PARAMS_SYMBOL } from 'router_js'; import { service } from '@ember/service'; import { precompileTemplate } from '@ember/template-compilation'; -import { QueryParamTestCase, moduleFor, getTextOf, runLoopSettled } from 'internal-test-helpers'; +import { + QueryParamTestCase, + moduleFor, + getTextOf, + runLoopSettled, + expectClassicClassDeprecation, + testUnless, +} from 'internal-test-helpers'; moduleFor( 'Query Params - main', @@ -1629,9 +1637,11 @@ moduleFor( return this.refreshModelWhileLoadingTest(true); } - async ["@test warn user that Route's queryParams configuration must be an Object, not an Array"]( + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test warn user that Route's queryParams configuration must be an Object, not an Array`]( assert ) { + expectClassicClassDeprecation(); + assert.expect(1); this.add( diff --git a/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js b/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js index c369825e7c9..9cf54b3065b 100644 --- a/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js +++ b/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js @@ -18,14 +18,6 @@ class ModelDependentQPTestCase extends QueryParamTestCase { 'there should be no pending expectation of expected model hook params' ); } - - reopenController(name, options) { - this.application.resolveRegistration(`controller:${name}`).reopen(options); - } - - reopenRoute(name, options) { - this.application.resolveRegistration(`route:${name}`).reopen(options); - } } moduleFor( @@ -69,19 +61,22 @@ moduleFor( this.add( 'controller:article', - Controller.extend({ - queryParams: ['q', 'z'], - q: 'wat', - z: 0, - }) + class extends Controller { + queryParams = ['q', 'z']; + + q = 'wat'; + + z = 0; + } ); this.add( 'controller:comments', - Controller.extend({ - queryParams: 'page', - page: 1, - }) + class extends Controller { + queryParams = ['page']; + + page = 1; + } ); this.add( @@ -249,9 +244,13 @@ moduleFor( this.setupApplication(); - this.reopenController('article', { - queryParams: { q: { scope: 'controller' } }, - }); + this.subclass( + 'controller:article', + (ArticleController) => + class extends ArticleController { + queryParams = ['q', 'z', { q: { scope: 'controller' } }]; + } + ); await this.visitApplication(); this.$link1.click(); @@ -328,14 +327,18 @@ moduleFor( this.setupApplication(); - this.reopenRoute('article', { - resetController(controller, isExiting) { - this.controllerFor('comments').set('page', 1); - if (isExiting) { - controller.set('q', 'imdone'); + this.subclass( + 'route:article', + (ArticleRoute) => + class extends ArticleRoute { + resetController(controller, isExiting) { + this.controllerFor('comments').set('page', 1); + if (isExiting) { + controller.set('q', 'imdone'); + } + } } - }, - }); + ); this.add( 'template:about', @@ -415,19 +418,22 @@ moduleFor( this.add( 'controller:site.article', - Controller.extend({ - queryParams: ['q', 'z'], - q: 'wat', - z: 0, - }) + class extends Controller { + queryParams = ['q', 'z']; + + q = 'wat'; + + z = 0; + } ); this.add( 'controller:site.article.comments', - Controller.extend({ - queryParams: 'page', - page: 1, - }) + class extends Controller { + queryParams = ['page']; + + page = 1; + } ); this.add( @@ -595,9 +601,13 @@ moduleFor( this.setupApplication(); - this.reopenController('site.article', { - queryParams: { q: { scope: 'controller' } }, - }); + this.subclass( + 'controller:site.article', + (ArticleController) => + class extends ArticleController { + queryParams = ['q', 'z', { q: { scope: 'controller' } }]; + } + ); await this.visitApplication(); this.$link1.click(); @@ -674,14 +684,18 @@ moduleFor( this.setupApplication(); - this.reopenRoute('site.article', { - resetController(controller, isExiting) { - this.controllerFor('site.article.comments').set('page', 1); - if (isExiting) { - controller.set('q', 'imdone'); + this.subclass( + 'route:site.article', + (ArticleRoute) => + class extends ArticleRoute { + resetController(controller, isExiting) { + this.controllerFor('site.article.comments').set('page', 1); + if (isExiting) { + controller.set('q', 'imdone'); + } + } } - }, - }); + ); this.add( 'template:about', @@ -800,27 +814,31 @@ moduleFor( this.add( 'controller:site', - Controller.extend({ - queryParams: ['country'], - country: 'au', - }) + class extends Controller { + queryParams = ['country']; + + country = 'au'; + } ); this.add( 'controller:site.article', - Controller.extend({ - queryParams: ['q', 'z'], - q: 'wat', - z: 0, - }) + class extends Controller { + queryParams = ['q', 'z']; + + q = 'wat'; + + z = 0; + } ); this.add( 'controller:site.article.comments', - Controller.extend({ - queryParams: ['page'], - page: 1, - }) + class extends Controller { + queryParams = ['page']; + + page = 1; + } ); this.add( diff --git a/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js b/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js index 3843cde3489..304abe2e6fd 100644 --- a/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js +++ b/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js @@ -115,9 +115,9 @@ moduleFor( ) { assert.expect(1); - let parentController = Controller.extend({ - queryParams: { page: 'page' }, - }); + let parentController = class extends Controller { + queryParams = [{ page: 'page' }]; + }; this.add('controller:parent', parentController); this.add( 'route:parent.child', @@ -149,19 +149,21 @@ moduleFor( async ['@test Support shared but overridable query param defaults'](assert) { assert.expect(7); - let hasPage = { - queryParams: 'page', - page: 1, - }; - this.add( 'controller:parent', - Controller.extend(hasPage, { - queryParams: { page: 'yespage' }, - }) + class extends Controller { + queryParams = ['page', { page: 'yespage' }]; + page = 1; + } ); - this.add('controller:parent.child', Controller.extend(hasPage)); + this.add( + 'controller:parent.child', + class extends Controller { + queryParams = ['page']; + page = 1; + } + ); await this.setupBase(); this.assertCurrentPath('/parent/child'); diff --git a/packages/ember/tests/routing/router_service_test/basic_test.js b/packages/ember/tests/routing/router_service_test/basic_test.js index aa5cc812eea..15c92e6f94c 100644 --- a/packages/ember/tests/routing/router_service_test/basic_test.js +++ b/packages/ember/tests/routing/router_service_test/basic_test.js @@ -156,14 +156,19 @@ moduleFor( ['@test RouterService can be injected into router and accessed on init'](assert) { assert.expect(1); - this.router.reopen({ - routerService: service('router'), - init() { - this.routerService.one('routeDidChange', () => { - assert.ok(true, 'routeDidChange event listener called'); - }); - }, - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + @service('router') routerService; + + init() { + this.routerService.one('routeDidChange', () => { + assert.ok(true, 'routeDidChange event listener called'); + }); + } + } + ); return this.visit('/'); } diff --git a/packages/ember/tests/routing/router_service_test/recognize_test.js b/packages/ember/tests/routing/router_service_test/recognize_test.js index 19674c280dd..03884bcf61d 100644 --- a/packages/ember/tests/routing/router_service_test/recognize_test.js +++ b/packages/ember/tests/routing/router_service_test/recognize_test.js @@ -33,9 +33,13 @@ moduleFor( } '@test respects the usage of a different rootURL'(assert) { - this.router.reopen({ - rootURL: '/app/', - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + rootURL = '/app/'; + } + ); return this.visit('/app').then(() => { let routeInfo = this.routerService.recognize('/app/child/'); @@ -51,9 +55,13 @@ moduleFor( this.add('template:parent', precompileTemplate('Parent')); this.add('template:dynamic-with-child.child', precompileTemplate('Dynamic Child')); - this.router.reopen({ - rootURL: '/app/', - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + rootURL = '/app/'; + } + ); return this.visit('/app').then(() => { expectAssertion(() => { @@ -138,9 +146,13 @@ moduleFor( } '@test respects the usage of a different rootURL'(assert) { - this.router.reopen({ - rootURL: '/app/', - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + rootURL = '/app/'; + } + ); return this.visit('/app') .then(() => { @@ -156,9 +168,13 @@ moduleFor( } '@test must include rootURL'() { - this.router.reopen({ - rootURL: '/app/', - }); + this.subclass( + 'router:main', + (Router) => + class extends Router { + rootURL = '/app/'; + } + ); return this.visit('/app').then(() => { expectAssertion(() => { diff --git a/packages/ember/tests/routing/router_service_test/transitionTo_test.js b/packages/ember/tests/routing/router_service_test/transitionTo_test.js index 2378177d65d..ef93b12a54e 100644 --- a/packages/ember/tests/routing/router_service_test/transitionTo_test.js +++ b/packages/ember/tests/routing/router_service_test/transitionTo_test.js @@ -256,10 +256,11 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ - queryParams: ['sort'], - sort: 'ASC', - }) + class extends Controller { + queryParams = ['sort']; + + sort = 'ASC'; + } ); let queryParams = this.buildQueryParams({ sort: 'ASC' }); @@ -278,9 +279,9 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ - queryParams: ['sort'], - }) + class extends Controller { + queryParams = ['sort']; + } ); let queryParams = this.buildQueryParams({ sort: 'DESC' }); @@ -305,12 +306,15 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ - queryParams: ['sort', 'page', 'category', 'extra'], - sort: 'ASC', - page: null, - category: undefined, - }) + class extends Controller { + queryParams = ['sort', 'page', 'category', 'extra']; + + sort = 'ASC'; + + page = null; + + category = undefined; + } ); let queryParams = this.buildQueryParams({ sort: 'DESC' }); @@ -331,12 +335,15 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ - queryParams: { - cont_sort: 'url_sort', - }, - cont_sort: 'ASC', - }) + class extends Controller { + queryParams = [ + { + cont_sort: 'url_sort', + }, + ]; + + cont_sort = 'ASC'; + } ); let queryParams = this.buildQueryParams({ url_sort: 'DESC' }); @@ -357,12 +364,15 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ - queryParams: { - cont_sort: 'url_sort', - }, - cont_sort: 'ASC', - }) + class extends Controller { + queryParams = [ + { + cont_sort: 'url_sort', + }, + ]; + + cont_sort = 'ASC'; + } ); let queryParams = this.buildQueryParams({ cont_sort: 'ASC' }); @@ -396,12 +406,13 @@ moduleFor( this.add( 'route:parent.child', - Route.extend({ - queryParams: { + class extends Route { + queryParams = { cont_sort: { as: 'url_sort' }, - }, - cont_sort: 'ASC', - }) + }; + + cont_sort = 'ASC'; + } ); return this.visit('/').then(() => { @@ -426,9 +437,9 @@ moduleFor( ); this.add( 'controller:parent', - Controller.extend({ - queryParams: ['url_sort'], - }) + class extends Controller { + queryParams = ['url_sort']; + } ); return this.visit('/child?url_sort=a').then(() => { @@ -459,10 +470,11 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ - queryParams: ['bar'], - bar: null, - }) + class extends Controller { + queryParams = ['bar']; + + bar = null; + } ); await this.visit('/'); @@ -500,10 +512,11 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ - queryParams: ['bar'], - bar: null, - }) + class extends Controller { + queryParams = ['bar']; + + bar = null; + } ); await this.visit('/child'); @@ -611,11 +624,13 @@ moduleFor( this.add( 'controller:application', - Controller.extend({ - queryParams: ['filter', 'sort'], - filter: '', - sort: '', - }) + class extends Controller { + queryParams = ['filter', 'sort']; + + filter = ''; + + sort = ''; + } ); await this.visit('/?filter=&sort='); @@ -669,11 +684,13 @@ moduleFor( this.add( 'controller:application', - Controller.extend({ - queryParams: ['filter', 'sort'], - filter: '', - sort: '', - }) + class extends Controller { + queryParams = ['filter', 'sort']; + + filter = ''; + + sort = ''; + } ); await this.visit('/?filter=&sort='); diff --git a/packages/ember/tests/service_injection_test.js b/packages/ember/tests/service_injection_test.js index 1e6033f9e1d..1bcf089c621 100644 --- a/packages/ember/tests/service_injection_test.js +++ b/packages/ember/tests/service_injection_test.js @@ -1,8 +1,14 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { getOwner } from '@ember/-internals/owner'; import Controller from '@ember/controller'; import Service, { service } from '@ember/service'; import { _ProxyMixin } from '@ember/-internals/runtime'; -import { moduleFor, ApplicationTestCase } from 'internal-test-helpers'; +import { + moduleFor, + ApplicationTestCase, + testUnless, + expectClassicClassDeprecation, +} from 'internal-test-helpers'; import { computed } from '@ember/object'; import { precompileTemplate } from '@ember/template-compilation'; @@ -27,7 +33,11 @@ moduleFor( assert.ok(controller.get('myService') instanceof MyService); } - async ['@test Service can be an object proxy and access owner in init GH#16484'](assert) { + async [`${testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)} @test Service can be an object proxy and access owner in init GH#16484`]( + assert + ) { + expectClassicClassDeprecation(); + let serviceOwner; this.add( diff --git a/packages/internal-test-helpers/index.ts b/packages/internal-test-helpers/index.ts index f3947560e95..f2485fb8149 100644 --- a/packages/internal-test-helpers/index.ts +++ b/packages/internal-test-helpers/index.ts @@ -18,6 +18,7 @@ export { expectDeprecationAsync, ignoreDeprecation, } from './lib/ember-dev/deprecation'; +export { expectClassicClassDeprecation } from './lib/classic-class-deprecation'; export { defineSimpleHelper, defineSimpleModifier } from './lib/define-template-values'; export { testIf, testUnless } from './lib/conditional-test'; export { equalsElement, classes, styles, regex } from './lib/matchers'; diff --git a/packages/internal-test-helpers/lib/classic-class-deprecation.ts b/packages/internal-test-helpers/lib/classic-class-deprecation.ts new file mode 100644 index 00000000000..d809354ef6c --- /dev/null +++ b/packages/internal-test-helpers/lib/classic-class-deprecation.ts @@ -0,0 +1,20 @@ +import { DEPRECATIONS } from '@ember/-internals/deprecations'; +import { expectDeprecationQuietly } from './ember-dev/deprecation'; + +/** + Expects the `deprecate-classic-classes` deprecation for the rest of the test. + + Call it first in a test that uses `extend`, `reopen` or `reopenClass`. + + Guard that test with `testUnless(DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isRemoved)`. + + While the deprecation is not enabled, this expects nothing. + A test can then also expect other deprecations that are enabled. + A match adds no assertion, so `assert.expect` counts stay the same. + `@ember/object/tests/classic-classes-deprecation-test` checks that nothing fires in that state. +*/ +export function expectClassicClassDeprecation(): void { + if (DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES.isEnabled) { + expectDeprecationQuietly(/classic class/); + } +} diff --git a/packages/internal-test-helpers/lib/ember-dev/deprecation.ts b/packages/internal-test-helpers/lib/ember-dev/deprecation.ts index 99c8486c9d2..69ae7390439 100644 --- a/packages/internal-test-helpers/lib/ember-dev/deprecation.ts +++ b/packages/internal-test-helpers/lib/ember-dev/deprecation.ts @@ -8,6 +8,7 @@ type ExtendedWindow = Window & expectNoDeprecation: DeprecationAssert['expectNoDeprecation'] | undefined; expectNoDeprecationAsync: DeprecationAssert['expectNoDeprecationAsync'] | undefined; expectDeprecation: DeprecationAssert['expectDeprecation'] | undefined; + expectDeprecationQuietly: DeprecationAssert['expectDeprecationQuietly'] | undefined; expectDeprecationAsync: DeprecationAssert['expectDeprecationAsync'] | undefined; ignoreDeprecation: DeprecationAssert['ignoreDeprecation'] | undefined; }; @@ -32,6 +33,12 @@ export let expectDeprecation: DeprecationAssert['expectDeprecation'] = () => { ); }; +export let expectDeprecationQuietly: DeprecationAssert['expectDeprecationQuietly'] = () => { + throw new Error( + 'DeprecationAssert: To use `expectDeprecationQuietly` in a test you must call `setupDeprecationHelpers` first' + ); +}; + // Unlike the other helpers, this one does not touch the deprecation tracker, so // it also works outside of a test -- e.g. when a test module builds fixtures at // import time. @@ -67,6 +74,8 @@ class DeprecationAssert extends DebugAssert { w.expectNoDeprecationAsync = expectNoDeprecationAsync = this.expectNoDeprecationAsync.bind(this); w.expectDeprecation = expectDeprecation = this.expectDeprecation.bind(this); + w.expectDeprecationQuietly = expectDeprecationQuietly = + this.expectDeprecationQuietly.bind(this); w.expectDeprecationAsync = expectDeprecationAsync = this.expectDeprecationAsync.bind(this); w.ignoreDeprecation = ignoreDeprecation = this.ignoreDeprecation.bind(this); super.inject(); @@ -78,6 +87,7 @@ class DeprecationAssert extends DebugAssert { w.expectNoDeprecation = undefined; w.expectNoDeprecationAsync = undefined; w.expectDeprecation = undefined; + w.expectDeprecationQuietly = undefined; w.expectDeprecationAsync = undefined; w.ignoreDeprecation = undefined; } @@ -181,6 +191,23 @@ class DeprecationAssert extends DebugAssert { } } + // Expects a deprecation from the time of calling until the end of the test. + // + // Unlike `expectDeprecation`, a match adds no assertion. Use it for a + // deprecation that is not enabled in each test run, so that `assert.expect` + // counts stay the same in each run. + private expectDeprecationQuietly(message: Message): void { + this.runExpectation(undefined, (tracker) => { + if (tracker.isExpectingNoCalls()) { + throw new Error( + 'expectDeprecationQuietly was called after expectNoDeprecation was called!' + ); + } + + tracker.expectCall(message, ['id', 'until'], true); + }); + } + private async expectDeprecationAsync(func: () => Promise, message?: Message): Promise; private async expectDeprecationAsync( func: () => Promise, diff --git a/packages/internal-test-helpers/lib/ember-dev/method-call-tracker.ts b/packages/internal-test-helpers/lib/ember-dev/method-call-tracker.ts index 369d1bb2ca9..8f3239dcc7f 100644 --- a/packages/internal-test-helpers/lib/ember-dev/method-call-tracker.ts +++ b/packages/internal-test-helpers/lib/ember-dev/method-call-tracker.ts @@ -13,6 +13,7 @@ export default class MethodCallTracker { private _isExpectingNoCalls: boolean; private _expectedMessages: Message[]; private _expectedOptionLists: OptionList[]; + private _expectedQuietly: boolean[]; private _actuals: Actual[]; private _originalMethod: DebugFunction | undefined; @@ -22,6 +23,7 @@ export default class MethodCallTracker { this._isExpectingNoCalls = false; this._expectedMessages = []; this._expectedOptionLists = []; + this._expectedQuietly = []; this._actuals = []; this._originalMethod = undefined; } @@ -50,10 +52,13 @@ export default class MethodCallTracker { } } - expectCall(message?: Message, options?: OptionList): void { + // A quiet expectation adds an assertion only when it fails, so the + // assertion count of a test does not depend on it. + expectCall(message?: Message, options?: OptionList, quiet = false): void { this.stubMethod(); this._expectedMessages.push(message || /.*/); this._expectedOptionLists.push(options); + this._expectedQuietly.push(quiet); } expectNoCalls(): void { @@ -157,7 +162,9 @@ export default class MethodCallTracker { `Received no Ember.${methodName} calls at all, expecting: ${expectedMessage}` ); } else if (match && !match[1]) { - assert.ok(true, `Received failing Ember.${methodName} call with message: ${match[0]}`); + if (!this._expectedQuietly[o]) { + assert.ok(true, `Received failing Ember.${methodName} call with message: ${match[0]}`); + } } else if (match && match[1]) { assert.ok( false, diff --git a/packages/internal-test-helpers/lib/test-cases/application.ts b/packages/internal-test-helpers/lib/test-cases/application.ts index 81a09b77ce0..483cb89c682 100644 --- a/packages/internal-test-helpers/lib/test-cases/application.ts +++ b/packages/internal-test-helpers/lib/test-cases/application.ts @@ -7,6 +7,7 @@ import Resolver from '../test-resolver'; import { assert as emberAssert } from '@ember/debug'; import type Controller from '@ember/controller'; import type ApplicationInstance from '@ember/application/instance'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; export default abstract class ApplicationTestCase extends TestResolverApplicationTestCase { application: Application; @@ -24,7 +25,7 @@ export default abstract class ApplicationTestCase extends TestResolverApplicatio emberAssert('expected a resolver', resolver instanceof Resolver); this.resolver = resolver; - resolver.add('router:main', Router.extend(this.routerOptions)); + resolver.add('router:main', Router[INTERNAL_EXTEND](this.routerOptions)); } createApplication(myOptions = {}, MyApplication = Application) { diff --git a/packages/internal-test-helpers/lib/test-cases/autoboot-application.ts b/packages/internal-test-helpers/lib/test-cases/autoboot-application.ts index eaac5b77e2e..86798772206 100644 --- a/packages/internal-test-helpers/lib/test-cases/autoboot-application.ts +++ b/packages/internal-test-helpers/lib/test-cases/autoboot-application.ts @@ -4,6 +4,7 @@ import Router from '@ember/routing/router'; import Resolver from '../test-resolver'; import { assert } from '@ember/debug'; import type ApplicationInstance from '@ember/application/instance'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; export default abstract class AutobootApplicationTestCase extends TestResolverApplicationTestCase { resolver?: Resolver; @@ -18,7 +19,7 @@ export default abstract class AutobootApplicationTestCase extends TestResolverAp assert('expected a resolver', resolver instanceof Resolver); this.resolver = resolver; - resolver.add('router:main', Router.extend(this.routerOptions)); + resolver.add('router:main', Router[INTERNAL_EXTEND](this.routerOptions)); return application; } diff --git a/packages/internal-test-helpers/lib/test-cases/query-param.ts b/packages/internal-test-helpers/lib/test-cases/query-param.ts index 18cddb58c0a..84ba1133c02 100644 --- a/packages/internal-test-helpers/lib/test-cases/query-param.ts +++ b/packages/internal-test-helpers/lib/test-cases/query-param.ts @@ -5,6 +5,7 @@ import NoneLocation from '@ember/routing/none-location'; import ApplicationTestCase from './application'; import { runLoopSettled } from '../run'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; export default abstract class QueryParamTestCase extends ApplicationTestCase { expectedPushURL: unknown; @@ -99,7 +100,7 @@ export default abstract class QueryParamTestCase extends ApplicationTestCase { setSingleQPController(routeName: string, param = 'foo', defaultValue = 'bar', options = {}) { this.add( `controller:${routeName}`, - Controller.extend( + Controller[INTERNAL_EXTEND]( { queryParams: [param], [param]: defaultValue, @@ -124,7 +125,7 @@ export default abstract class QueryParamTestCase extends ApplicationTestCase { ) { this.add( `controller:${routeName}`, - Controller.extend( + Controller[INTERNAL_EXTEND]( { queryParams: { [prop]: urlKey, diff --git a/packages/internal-test-helpers/lib/test-cases/rendering.ts b/packages/internal-test-helpers/lib/test-cases/rendering.ts index 1dab130a719..29b76f2c55c 100644 --- a/packages/internal-test-helpers/lib/test-cases/rendering.ts +++ b/packages/internal-test-helpers/lib/test-cases/rendering.ts @@ -14,6 +14,7 @@ import buildOwner from '../build-owner'; import { define } from '../module-for'; import { runAppend, runDestroy, runTask } from '../run'; import AbstractTestCase from './abstract'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; const TextNode = window.Text; @@ -111,7 +112,7 @@ export default abstract class RenderingTestCase extends AbstractTestCase { layoutName: '-top-level', }); - owner.register('component:-top-level', Component.extend(attrs)); + owner.register('component:-top-level', Component[INTERNAL_EXTEND](attrs)); this.component = owner.lookup('component:-top-level'); @@ -137,7 +138,7 @@ export default abstract class RenderingTestCase extends AbstractTestCase { if (typeof funcOrClassBody === 'function') { this.owner.register(`helper:${name}`, helper(funcOrClassBody)); } else if (typeof funcOrClassBody === 'object' && funcOrClassBody !== null) { - this.owner.register(`helper:${name}`, Helper.extend(funcOrClassBody)); + this.owner.register(`helper:${name}`, Helper[INTERNAL_EXTEND](funcOrClassBody)); } else { throw new Error(`Cannot register ${funcOrClassBody} as a helper`); } diff --git a/packages/internal-test-helpers/lib/test-cases/router-non-application.ts b/packages/internal-test-helpers/lib/test-cases/router-non-application.ts index e78cb8f4e12..731ef5f5489 100644 --- a/packages/internal-test-helpers/lib/test-cases/router-non-application.ts +++ b/packages/internal-test-helpers/lib/test-cases/router-non-application.ts @@ -14,6 +14,7 @@ import { runAppend, runDestroy } from '../run'; import type { BootOptions, EngineInstanceOptions } from '@ember/engine/instance'; import type EngineInstance from '@ember/engine/instance'; import type { InternalFactory } from '@ember/-internals/owner'; +import { INTERNAL_EXTEND } from '@ember/-internals/utils/lib/internal-classic-class'; export default class RouterNonApplicationTestCase extends AbstractTestCase { owner: EngineInstance; @@ -147,7 +148,7 @@ export default class RouterNonApplicationTestCase extends AbstractTestCase { layoutName: '-top-level', }); - owner.register('component:-top-level', Component.extend(attrs)); + owner.register('component:-top-level', Component[INTERNAL_EXTEND](attrs)); this.component = owner.lookup('component:-top-level'); diff --git a/packages/internal-test-helpers/lib/test-cases/test-resolver-application.ts b/packages/internal-test-helpers/lib/test-cases/test-resolver-application.ts index 199a996607c..13733e39be8 100644 --- a/packages/internal-test-helpers/lib/test-cases/test-resolver-application.ts +++ b/packages/internal-test-helpers/lib/test-cases/test-resolver-application.ts @@ -15,4 +15,17 @@ export default abstract class TestResolverApplicationTestCase extends AbstractAp add(specifier: string, factory: InternalFactory | object) { this.resolver!.add(specifier, factory); } + + /** + Replaces a registration with a subclass of it. + + The registry caches a class after the first lookup, so the old + registration must go before the resolver can supply the subclass. + */ + subclass(specifier: `${string}:${string}`, build: (Base: T) => T) { + let Base = this.application.resolveRegistration(specifier) as T; + + this.application.unregister(specifier); + this.add(specifier, build(Base)); + } }