From cb1c4b8afe832cf86298c6a6b11d33f97fa73516 Mon Sep 17 00:00:00 2001 From: Eric Hwang Date: Fri, 21 May 2021 17:50:46 -0500 Subject: [PATCH] test-utils `.to.render` assertion - Reset page/component state between each render pass --- test-utils/assertions.js | 22 ++++++++++++++++++++-- test/dom/ComponentHarness.mocha.js | 25 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/test-utils/assertions.js b/test-utils/assertions.js index 080ed496..d5d20ec3 100644 --- a/test-utils/assertions.js +++ b/test-utils/assertions.js @@ -41,6 +41,20 @@ module.exports = function(dom, Assertion) { return el.innerHTML; } + // Executes the parts of `Page#destroy` pertaining to the model, which get + // re-done when a new Page gets created on the same model. Normally, using + // `Page#destroy` would be fine, but the `.to.render` assertion wants to do + // 3 rendering passes on the same data, so we can't completely clear the + // model's state between the rendering passes. + function resetPageModel(page) { + page._removeModelListeners(); + for (var componentId in page._components) { + var component = page._components[componentId]; + component.destroy(); + } + page.model.silent().destroy('$components'); + } + if (Assertion) { Assertion.addMethod('html', function(expected, options) { var obj = this._obj; @@ -76,7 +90,8 @@ module.exports = function(dom, Assertion) { new Assertion(harness).instanceOf(ComponentHarness); // Render to a HTML string. - var htmlString = harness.renderHtml(options).html; + var renderResult = harness.renderHtml(options); + var htmlString = renderResult.html; // Normalize `htmlString` into the same form as the DOM would give for `element.innerHTML`. // @@ -105,6 +120,8 @@ module.exports = function(dom, Assertion) { } } + resetPageModel(renderResult.page); + // Check DOM rendering is also equivalent. // This uses the harness "pageRendered" event to grab the rendered DOM *before* any component // `create()` methods are called, as `create()` methods can do DOM mutations. @@ -121,7 +138,8 @@ module.exports = function(dom, Assertion) { } } }); - harness.renderDom(options); + renderResult = harness.renderDom(options); + resetPageModel(renderResult.page); // Try attaching. Attachment will throw an error if HTML doesn't match var el = domDocument.createElement(parentTag); diff --git a/test/dom/ComponentHarness.mocha.js b/test/dom/ComponentHarness.mocha.js index 5d04409f..69376d9d 100644 --- a/test/dom/ComponentHarness.mocha.js +++ b/test/dom/ComponentHarness.mocha.js @@ -215,6 +215,31 @@ describe('ComponentHarness', function() { expect(harness).to.render(); expect(harness).to.render('< ">'); }); + + it('cleans up component state between render passes', function() { + function Box() {} + Box.view = { + is: 'box', + source: '
{{greeting}}
' + }; + Box.prototype.init = function() { + var initialName = this.model.scope('_page.initialName').get(); + expect(initialName).to.equal('Spot'); + this.model.set('name', initialName); + this.model.start('greeting', ['name'], function(name) { + // This assertion ensures that the reactive function isn't called after + // the component gets destroyed. + expect(name).to.equal('Spot'); + return 'Hello, ' + name; + }); + }; + var harness = runner.createHarness('', Box); + // Have the test depend on state in `_page` to make sure it's not cleared + // between rendering passes in `.to.render`. + harness.model.set('_page.initialName', 'Spot'); + + expect(harness).to.render('
Hello, Spot
'); + }); }); describe('fake app.history implementation', function() {