From b0da0b6d2d8f3ff110acd6abe92267784dd07644 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 14 Aug 2026 14:15:26 -0400 Subject: [PATCH 01/11] Prose updates --- text/1203-deprecate-get-set-test-context.md | 163 ++++++++++++++------ 1 file changed, 116 insertions(+), 47 deletions(-) diff --git a/text/1203-deprecate-get-set-test-context.md b/text/1203-deprecate-get-set-test-context.md index cb29c01e43..d53e11a5c6 100644 --- a/text/1203-deprecate-get-set-test-context.md +++ b/text/1203-deprecate-get-set-test-context.md @@ -12,29 +12,63 @@ prs: project-link: --- -# Deprecate `this.get` and `this.set` on Test Contexts +# Deprecate `get`, `set`, `getProperties`, and `setProperties` on test contexts ## Summary -Deprecate the use of `this.get` and `this.set` on the test context object in rendering tests, as a logical conclusion of [RFC #785](https://github.com/emberjs/rfcs/blob/master/text/0785-remove-set-get-in-tests.md), which introduced `render(component)` and `rerender()` as the modern replacements. +Deprecate the four data-manipulation methods that `@ember/test-helpers` installs on the test context: `this.get`, `this.set`, `this.getProperties`, and `this.setProperties`. Everything else the test context provides is untouched: `this.owner`, `this.element`, `this.pauseTest`, `this.resumeTest`, and properties you assign yourself. ## Motivation -RFC #785 introduced two new testing utilities — an updated `render` helper that accepts a component directly, and a new `rerender()` function — specifically to remove the need for `this.get` and `this.set` in rendering tests. Those APIs shipped in Ember v4.5.0 and are now the recommended approach for writing rendering tests. +[RFC #785](https://github.com/emberjs/rfcs/blob/main/text/0785-remove-set-get-in-tests.md) made the case against these methods five years ago and shipped the replacements: `render` learned to accept a component, and `rerender` was added alongside it, in `@ember/test-helpers` 2.8.0, backed by `renderSettled` from the `@ember/renderer` module that landed in `ember-source` 4.5.0. What that RFC did not do was set an end date for the old way. This one does. -The legacy pattern of setting values on `this` in a rendering test was problematic for several reasons laid out in RFC #785: +The arguments have not changed since #785, so they are worth restating only briefly. `get` and `set` are not how anyone writes application code after Octane, so tests written this way are teaching a model that exists nowhere else. Stashing template state on `this` means TypeScript users have to widen `TestContext` per module, and those widenings then appear to apply to every test in the module whether or not the property is actually there. And the test context doing double duty, as both test harness *and* backing object for the template, is just hard to explain. -1. **Inconsistency with application code.** In post-Octane Ember, `get` and `set` are unnecessary; properties are tracked natively. Requiring them in tests is a confusing holdover. +There is one newer argument. `this.set` and `this.setProperties` are `run()`-wrapped: -2. **Incorrect rendering semantics.** `this.set` in tests is run-wrapped, causing a synchronous full DOM flush on every call. This does not reflect how Ember schedules DOM updates in production code, where changes to tracked state are coalesced. +```js +Object.defineProperty(context, 'setProperties', { + value(hash) { + return run(function () { + return setProperties(context, hash); + }); + }, + // ... +}); +``` -3. **TypeScript friction.** Assigning arbitrary properties to `this` forces developers to redeclare the `TestContext` interface for every test module, causing leakage of property declarations across tests and defeating the purpose of static type checking. +Every call synchronously flushes the entire DOM. Nothing in an application behaves this way; there, updates to tracked state coalesce into one render pass. That synchronous flush is also precisely the behavior that a render-aware scheduler ([RFC #957](https://github.com/emberjs/rfcs/pull/957)) cannot preserve. To be clear: this deprecation is not a prerequisite for that work. A test that already avoids these methods can adopt an async scheduler as-is. But every test that still calls `this.set` is a test that will have to be rewritten when the scheduler changes, and it is better to rewrite it against a deprecation with a migration guide than against a scheduler change. -Now that the modern replacements have been stable for multiple major versions, it is appropriate to deprecate the old approach and eventually remove it, completing the migration to a cleaner and more accurate testing model. +Note that `get` and `getProperties` are not `run()`-wrapped. They are thin wrappers over `get`/`getProperties` from `@ember/object` applied to the context. They are included here because they exist only to read back what `set` wrote, and keeping them after `set` is gone serves no one. ## Transition Path -### Before (deprecated) +### What is deprecated + +`setupContext` from `@ember/test-helpers` installs `get`, `set`, `getProperties`, and `setProperties` on the context. All four are deprecated. Because they come from `setupContext` and not `setupRenderingContext`, they are present in unit, rendering, and application tests alike, and the deprecation covers all three. + +### What is not deprecated + +- `this.owner`, which is the whole point of the test context. +- `this.element`, available in rendering tests. Whether it should exist at all is a separate conversation; this RFC does not have it. +- `this.pauseTest()` and `this.resumeTest()`, which are useful precisely because you can reach for them mid-debug without editing your imports. +- Assigning your own properties to `this`. `this.foo = someValue` is a common pattern for sharing setup between hooks and tests and remains supported. + +That last point has a consequence worth spelling out, because it is easy to misread this RFC as doing more than it does. `render` installs the test context as the rendered outlet's `controller`, which is what makes `{{this.name}}` in an `hbs` template resolve against the test context. Deprecating these four methods does not remove that binding: + +```js +// still works after this deprecation +this.name = 'Zoey'; +await render(hbs`{{this.name}}`); // renders "Zoey" + +this.name = 'Tomster'; +await rerender(); +assert.dom().hasText('Zoey'); // ...and still "Zoey" +``` + +The initial render picks the value up; the reassignment does nothing, because the property is not tracked and nothing is `run()`-wrapping the write. Severing the template-to-context binding is a larger, separate change and needs its own RFC. + +### Before ```js import { render } from '@ember/test-helpers'; @@ -45,24 +79,27 @@ test('it renders the name', async function (assert) { await render(hbs``); - assert.dom('[data-test-name]').hasText(this.get('name')); + assert.dom('[data-test-name]').hasText('Zoey'); this.set('name', 'Tomster'); - assert.dom('[data-test-name]').hasText(this.get('name')); + assert.dom('[data-test-name]').hasText('Tomster'); }); ``` -### After (recommended) +### After ```js import { render, rerender } from '@ember/test-helpers'; import { tracked } from '@glimmer/tracking'; +import MyComponent from 'my-app/components/my-component'; test('it renders the name', async function (assert) { - const state = new class { + class State { @tracked name = 'Zoey'; - }; + } + + const state = new State(); await render(); @@ -75,21 +112,28 @@ test('it renders the name', async function (assert) { }); ``` -For projects that have not yet adopted `