diff --git a/packages/spacebars-tests/template_tests.html b/packages/spacebars-tests/template_tests.html index 5aa0f609a1..5fd785697f 100644 --- a/packages/spacebars-tests/template_tests.html +++ b/packages/spacebars-tests/template_tests.html @@ -880,3 +880,15 @@ Hi there! + + + + diff --git a/packages/spacebars-tests/template_tests.js b/packages/spacebars-tests/template_tests.js index 133b620499..bd520ff218 100644 --- a/packages/spacebars-tests/template_tests.js +++ b/packages/spacebars-tests/template_tests.js @@ -2411,7 +2411,7 @@ Tinytest.add( }); Tinytest.add( - "spacebars-tests - template_tests - focus/blur get cleaned up", + "spacebars-tests - template_tests - focus/blur with clean-up", function (test) { var tmpl = Template.spacebars_test_focus_blur_outer; var cond = ReactiveVar(true); @@ -2438,11 +2438,25 @@ Tinytest.add( focusElement(input = div.querySelector('input')); // We don't get focus events when the Chrome Dev Tools are focused, // unfortunately, as of Chrome 35. I think this is a regression in - // Chrome 34. - if (buf.length === 0 && document.activeElement === input) - test.fail("NOTE: You might need to defocus the Chrome Dev Tools!"); + // Chrome 34. So, the goal is to work whether or not focus is + // "borken," where "working" means always failing if DOMBackend isn't + // correctly unbinding the old event handlers when we switch the IF, + // and always passing if it is. To cause the problem in DOMBackend, + // delete the '**' argument to jQuery#off in + // DOMBackend.Events.undelegateEvents. The only compromise we are + // making here is that if some unrelated bug in Blaze makes + // focus/blur not work, the failure might be masked while the Dev + // Tools are open. + var borken = false; + if (buf.length === 0 && document.activeElement === input) { + test.ok({note:"You might need to defocus the Chrome Dev Tools to get a more accurate run of this test!"}); + borken = true; + $(input).trigger('focus'); + } test.equal(buf.join(), 'FOCUS'); blurElement(div.querySelector('input')); + if (buf.length === 1) + $(input).trigger('blur'); test.equal(buf.join(), 'FOCUS,BLUR'); // now switch the IF and check again. The failure mode @@ -2456,10 +2470,44 @@ Tinytest.add( buf.length = 0; Deps.flush(); test.equal(div.querySelectorAll('input').length, 1); - focusElement(div.querySelector('input')); + focusElement(input = div.querySelector('input')); + if (borken) + $(input).trigger('focus'); test.equal(buf.join(), 'FOCUS'); blurElement(div.querySelector('input')); - test.equal(buf.join(), 'FOCUS,BLUR'); + if (! borken) + test.equal(buf.join(), 'FOCUS,BLUR'); + + document.body.removeChild(div); + }); + +// We used to remove event handlers on DOMRange detached, but when +// tearing down a view, we don't "detach" all the DOMRanges recursively. +// Mainly, we destroy the View. Destroying a View should remove its +// event listeners. (In practice, however, it's hard to think of +// consequences to not removing event handlers on removed DOM nodes, +// which will probably be GCed anyway.) +Tinytest.add( + "spacebars-tests - template_tests - event cleanup on destroyed", + function (test) { + var tmpl = Template.spacebars_test_event_cleanup_on_destroyed_outer; + var cond = ReactiveVar(true); + tmpl.cond = function () { + return cond.get(); + }; + + Template.spacebars_test_event_cleanup_on_destroyed_inner.events({ + 'click span': function () {}}); + + var div = renderToDiv(tmpl); + document.body.appendChild(div); + + var eventDiv = div.querySelector('div'); + test.equal(eventDiv.$blaze_events.click.handlers.length, 1); + + cond.set(false); + Deps.flush(); + test.equal(eventDiv.$blaze_events.click.handlers.length, 0); document.body.removeChild(div); });