Skip to content

Commit 5b0a0e5

Browse files
committed
update to not use this.set or assert.equal
1 parent 2d9b0b0 commit 5b0a0e5

File tree

1 file changed

+92
-49
lines changed

1 file changed

+92
-49
lines changed

guides/release/testing/testing-components.md

Lines changed: 92 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ and cleaning up once your tests in this module are finished.
3535
<div class="cta-note-heading">Zoey says...</div>
3636
<div class="cta-note-message">
3737
If you generated your component using <code>ember generate component pretty-color</code> it will already have generated
38-
this file for you with all the boilerplate needed to get started. We are describing the steps to build to that same
38+
the following file for you with all the boilerplate needed to get started. We are describing the steps to build to that same
3939
boilerplate for educational purposes.
4040
</div>
4141
</div>
@@ -55,6 +55,8 @@ module('Integration | Component | pretty-color', function(hooks) {
5555
});
5656
```
5757

58+
The first thing to notice about this file is the filename, we are creating a `.gjs` file for a rendering test because we will be using `<template></template>` to render our Component under test and this only works inside `.gjs` files.
59+
5860
Inside your `module` and after setting up the test, we can now start to create our first test case.
5961
Here, we can use the QUnit's `test` function, and we can give it a descriptive name:
6062

@@ -87,12 +89,11 @@ module('Integration | Component | pretty-color', function(hooks) {
8789
setupRenderingTest(hooks);
8890
8991
test('should change colors', async function(assert) {
90-
// set the outer context to red
91-
this.set('colorValue', 'red');
92+
let colorValue = 'red'
9293
93-
await render(<template> <PrettyColor @name={{this.colorValue}} /> </template>);
94+
await render(<template> <PrettyColor @name={{colorValue}} /> </template>);
9495
95-
assert.equal(this.element.querySelector('div').getAttribute('style'), 'color: red', 'starts as red');
96+
assert.strictEqual(this.element.querySelector('div').getAttribute('style'), 'color: red', 'starts as red');
9697
});
9798
});
9899
```
@@ -104,56 +105,97 @@ Also notice, the keyword `await` in front of the call to `render`.
104105
It allows the test which we marked as `async` earlier to wait for any asynchronous behavior to complete before executing the rest of the code below.
105106
In this case our first assertion will correctly execute after the component has fully rendered.
106107

107-
Next we can test that changing the component's `name` property updates the
108-
component's `style` attribute and is reflected in the rendered HTML:
108+
Next we can test to see if changing the component's `name` property updates the
109+
component's `style` attribute and is reflected in the rendered HTML. Note: we expect this to fail so continue reading after this example if you want to find out why this fails:
109110

110111
```gjs {data-filename="tests/integration/components/pretty-color-test.gjs"}
111112
import { module, test } from 'qunit';
112113
import { setupRenderingTest } from 'my-app-name/tests/helpers';
113114
import { render } from '@ember/test-helpers';
114-
import { hbs } from 'ember-cli-htmlbars';
115115
import PrettyColor from 'my-app-name/components/pretty-color';
116116
117117
module('Integration | Component | pretty-color', function(hooks) {
118118
setupRenderingTest(hooks);
119119
120120
test('it renders', async function(assert) {
121-
// set the outer context to red
122-
this.set('colorValue', 'red');
121+
let colorValue = 'red'
123122
124-
await render(<template> <PrettyColor @name={{this.colorValue}} /> </template>);
123+
await render(<template> <PrettyColor @name={{colorValue}} /> </template>);
125124
126-
assert.equal(this.element.querySelector('div').getAttribute('style'), 'color: red', 'starts as red');
125+
assert.strictEqual(this.element.querySelector('div').getAttribute('style'), 'color: red', 'starts as red');
127126
128-
this.set('colorValue', 'blue');
127+
colorValue = 'blue';
129128
130-
assert.equal(this.element.querySelector('div').getAttribute('style'), 'color: blue', 'updates to blue');
129+
assert.strictEqual(this.element.querySelector('div').getAttribute('style'), 'color: blue', 'updates to blue');
131130
});
132131
});
133132
```
134133

135-
We might also test this component to ensure that the content of its template is being rendered properly:
134+
This test is now failing with the following error:
135+
136+
```
137+
Expected: "color: blue"
138+
Result: "color: red"
139+
```
140+
141+
This means that the `name` attribute never updated the template after we update the value in `colorValue`. This happens because we need to mark data as `@tracked` before we can expect it to update templates automatically. You can read more about the tracking system on the [Autotracking In-Depth](../in-depth-topics/autotracking-in-depth/) topic.
142+
143+
Also it's worth noting that currently we can only use `@tracked` in the context of a class field, so we need to create an inline class with the data in the test:
144+
145+
```gjs {data-filename="tests/integration/components/pretty-color-test.gjs"}
146+
import { module, test } from 'qunit';
147+
import { setupRenderingTest } from 'ember-strict/tests/helpers';
148+
import { render, rerender } from '@ember/test-helpers';
149+
import { tracked } from '@glimmer/tracking';
150+
import PrettyColor from 'ember-strict/components/pretty-color';
151+
152+
module('Integration | Component | pretty-color', function (hooks) {
153+
setupRenderingTest(hooks);
154+
155+
test('it renders', async function (assert) {
156+
const data = new class {
157+
@tracked colorValue = 'red';
158+
};
159+
160+
await render(<template> <PrettyColor @name={{data.colorValue}} /> </template>);
161+
162+
assert.strictEqual(this.element.querySelector('div').getAttribute('style'), 'color: red', 'starts as red');
163+
164+
data.colorValue = 'blue';
165+
await rerender();
166+
167+
assert.strictEqual(this.element.querySelector('div').getAttribute('style'), 'color: blue', 'updates to blue');
168+
});
169+
});
170+
```
171+
172+
We also needed to add a call to `await rerender()` for this to work. This function returns a promise that will resolve when all the template updates have finished excecuting. We can await this promise to wait until all templates have updated before continuing to assert against the DOM.
173+
174+
Now that we have data updating correctly in a test, we can start testing other things about this component e.g. we can also test this that the content of its template is being rendered properly:
136175

137176
```gjs {data-filename="tests/integration/components/pretty-color-test.gjs"}
138177
import { module, test } from 'qunit';
139178
import { setupRenderingTest } from 'my-app-name/tests/helpers';
140-
import { render } from '@ember/test-helpers';
141-
import { hbs } from 'ember-cli-htmlbars';
179+
import { render, rerender } from '@ember/test-helpers';
180+
import { tracked } from '@glimmer/tracking';
142181
import PrettyColor from 'my-app-name/components/pretty-color';
143182
144183
module('Integration | Component | pretty-color', function(hooks) {
145184
setupRenderingTest(hooks);
146185
147186
test('it renders', async function(assert) {
148-
this.set('colorValue', 'orange');
187+
const data = new class {
188+
@tracked colorValue = 'red';
189+
};
149190
150-
await render(<template> <PrettyColor @name={{this.colorValue}} /> </template>);
191+
await render(<template> <PrettyColor @name={{data.colorValue}} /> </template>);
151192
152-
assert.equal(this.element.textContent.trim(), 'Pretty Color: orange', 'text starts as orange');
193+
assert.strictEqual(this.element.textContent.trim(), 'Pretty Color: orange', 'text starts as orange');
153194
154-
this.set('colorValue', 'green');
195+
data.colorValue = 'green';
196+
await rerender();
155197
156-
assert.equal(this.element.textContent.trim(), 'Pretty Color: green', 'text switches to green');
198+
assert.strictEqual(this.element.textContent.trim(), 'Pretty Color: green', 'text switches to green');
157199
});
158200
});
159201
```
@@ -196,20 +238,19 @@ And our test might look like this:
196238
import { module, test } from 'qunit';
197239
import { setupRenderingTest } from 'my-app-name/tests/helpers';
198240
import { click, render } from '@ember/test-helpers';
199-
import { hbs } from 'ember-cli-htmlbars';
200241
201242
module('Integration | Component | magic-title', function(hooks) {
202243
setupRenderingTest(hooks);
203244
204245
test('should update title on button click', async function(assert) {
205246
await render(<template><MagicTitle /></template>);
206247
207-
assert.equal(this.element.querySelector('h2').textContent.trim(), 'Hello World', 'initial text is hello world');
248+
assert.strictEqual(this.element.querySelector('h2').textContent.trim(), 'Hello World', 'initial text is hello world');
208249
209250
// Click on the button
210251
await click('.title-button');
211252
212-
assert.equal(this.element.querySelector('h2').textContent.trim(), 'This is Magic', 'title changes after click');
253+
assert.strictEqual(this.element.querySelector('h2').textContent.trim(), 'This is Magic', 'title changes after click');
213254
});
214255
});
215256
```
@@ -265,19 +306,18 @@ expect the closure action to have been called.
265306
import { module, test } from 'qunit';
266307
import { setupRenderingTest } from 'my-app-name/tests/helpers';
267308
import { click, fillIn, render } from '@ember/test-helpers';
268-
import { hbs } from 'ember-cli-htmlbars';
269309
270310
module('Integration | Component | comment-form', function(hooks) {
271311
setupRenderingTest(hooks);
272312
273313
test('should trigger external action on form submit', async function(assert) {
274314
// test double for the external action
275315
let actual;
276-
this.set('externalAction', (data) => {
316+
let externalAction = (data) => {
277317
actual = data;
278318
});
279319
280-
await render(<template><CommentForm @submitComment={{this.externalAction}} /></template>);
320+
await render(<template><CommentForm @submitComment={{externalAction}} /></template>);
281321
282322
// fill out the form and force an onchange
283323
await fillIn('textarea', 'You are not a wizard!');
@@ -410,7 +450,7 @@ module('Integration | Component | location-indicator', function(hooks) {
410450
411451
test('should reveal current location', async function(assert) {
412452
await render(<template><LocationIndicator /></template>);
413-
assert.equal(this.element.textContent.trim(),
453+
assert.strictEqual(this.element.textContent.trim(),
414454
'You currently are located in New York, USA');
415455
});
416456
});
@@ -422,8 +462,7 @@ values that can change in our subbed service!
422462
```gjs {data-filename="tests/integration/components/location-indicator-test.gjs" data-diff="+38,+39,+40,+41,+42,+43,+44,+45,+46,+47,+48,+49,+50,+51,+52"}
423463
import { module, test } from 'qunit';
424464
import { setupRenderingTest } from 'my-app-name/tests/helpers';
425-
import { render } from '@ember/test-helpers';
426-
import { hbs } from 'ember-cli-htmlbars';
465+
import { render, rerender } from '@ember/test-helpers';
427466
import Service from '@ember/service';
428467
import { tracked } from '@glimmer/tracking';
429468
@@ -449,27 +488,29 @@ module('Integration | Component | location-indicator', function(hooks) {
449488
setupRenderingTest(hooks);
450489
451490
hooks.beforeEach(function(assert) {
452-
this.owner.register('service:location-service', LocationStub);
491+
this.owner.register('service:location', LocationStub);
453492
});
454493
455494
test('should reveal current location', async function(assert) {
456-
await render(hbs`<LocationIndicator />`);
457-
assert.equal(this.element.textContent.trim(),
495+
await render(<template><LocationIndicator /></template>);
496+
assert.strictEqual(this.element.textContent.trim(),
458497
'You currently are located in New York, USA');
459498
});
460499
461500
test('should change displayed location when current location changes', async function (assert) {
462501
await render(<template><LocationIndicator /></template>);
463502
464-
assert.equal(this.element.textContent.trim(),
503+
assert.strictEqual(this.element.textContent.trim(),
465504
'You currently are located in New York, USA', 'origin location should display');
466505
467-
this.locationService = this.owner.lookup('service:location');
468-
this.set('locationService.city', 'Beijing');
469-
this.set('locationService.country', 'China');
470-
this.set('locationService.currentLocation', { x: 11111, y: 222222 });
506+
let locationService = this.owner.lookup('service:location');
507+
locationService.city = 'Beijing';
508+
locationService.country = 'China';
509+
locationService.currentLocation = { x: 11111, y: 222222 };
510+
511+
await rerender();
471512
472-
assert.equal(this.element.textContent.trim(),
513+
assert.strictEqual(this.element.textContent.trim(),
473514
'You currently are located in Beijing, China', 'location display should change');
474515
});
475516
});
@@ -483,7 +524,7 @@ To use them in your tests, you can `await` any of them to make sure that subsequ
483524

484525
```javascript
485526
await click('button.submit-button'); // clicks a button and waits for any async behavior initiated by the click to settle
486-
assert.equal(this.element.querySelector('.form-message').textContent, 'Your details have been submitted successfully.');
527+
assert.strictEqual(this.element.querySelector('.form-message').textContent, 'Your details have been submitted successfully.');
487528
```
488529

489530
Nearly all of the helpers for DOM interaction from `@ember/test-helpers` return a call to `settled` - a function
@@ -544,24 +585,26 @@ module('Integration | Component | delayed-typeahead', function(hooks) {
544585
];
545586
546587
test('should render results after typing a term', async function(assert) {
547-
this.set('results', []);
588+
const data = new class {
589+
@tracked results = [];
590+
};
548591
549592
let value;
550-
this.set('fetchResults', (data) => {
593+
let fetchResults = (data) => {
551594
value = data;
552-
this.set('results', stubResults);
553-
});
595+
data.results = stubResults;
596+
};
554597
555-
await render(<template><DelayedTypeahead @fetchResults={{this.fetchResults}} @results={{this.results}} /></template>);
598+
await render(<template><DelayedTypeahead @fetchResults={{fetchResults}} @results={{data.results}} /></template>);
556599
await fillIn('input', 'test')
557600
this.element.querySelector('input').dispatchEvent(new Event('keyup'));
558601
559602
await settled();
560-
assert.equal(value, 'test', 'fetch closure action called with search value');
603+
assert.strictEqual(value, 'test', 'fetch closure action called with search value');
561604
562-
assert.equal(this.element.querySelectorAll('.result').length, 2, 'two results rendered');
605+
assert.strictEqual(this.element.querySelectorAll('.result').length, 2, 'two results rendered');
563606
});
564607
});
565608
```
566609

567-
<!-- eof - needed for pages that end in a code block -->
610+
Notice that we don't need to call `await rerender()` in this test to make sure the template has updated. This is because the work done in `await rerender()` is fully encapsulated in the `await settled()` so we don't need to call both.

0 commit comments

Comments
 (0)