From e8a20638adb835497d091dcd01980f79f175a5bf Mon Sep 17 00:00:00 2001 From: BobrImperator Date: Tue, 25 Jun 2024 19:34:14 +0200 Subject: [PATCH] feat(memory-leak): add elements cleanup for did-intersect modifier --- addon/modifiers/did-intersect.js | 5 ++ addon/services/observer-manager.js | 5 ++ .../modifiers/real-did-intersect-test.js | 48 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 tests/integration/modifiers/real-did-intersect-test.js diff --git a/addon/modifiers/did-intersect.js b/addon/modifiers/did-intersect.js index ecbb3f5a..b88784ca 100644 --- a/addon/modifiers/did-intersect.js +++ b/addon/modifiers/did-intersect.js @@ -8,6 +8,11 @@ export const DEFAULT_OBSERVER_OPTIONS = {}; function cleanup(instance) { instance.unobserve.call(instance); + instance.observerManager.removeElement(instance.element); + instance.observerManager.removeElement(window); + instance.onEnter = null; + instance.onExit = null; + instance.element = null; } export default class DidIntersectModifier extends Modifier { diff --git a/addon/services/observer-manager.js b/addon/services/observer-manager.js index 5bdb3b44..dc16be9d 100644 --- a/addon/services/observer-manager.js +++ b/addon/services/observer-manager.js @@ -23,4 +23,9 @@ export default class ObserverManagerService extends Service { willDestroy() { this._admin.destroy(); } + + removeElement(element) { + this._admin.elementRegistry.removeElement(element); + this._admin.registry.removeElement(element); + } } diff --git a/tests/integration/modifiers/real-did-intersect-test.js b/tests/integration/modifiers/real-did-intersect-test.js new file mode 100644 index 00000000..4c8e60c2 --- /dev/null +++ b/tests/integration/modifiers/real-did-intersect-test.js @@ -0,0 +1,48 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; +import sinon from 'sinon'; + +module( + 'Integration | Modifier | did-intersect without mocks', + function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + this.enterStub = sinon.stub(); + this.exitStub = sinon.stub(); + this.maxEnter = 1; + this.maxExit = 1; + + this.observerManagerService = this.owner.lookup( + 'service:ember-scroll-modifiers@observer-manager', + ); + }); + + test('it removes elements from the registry on modifier cleanup', async function (assert) { + let removedElementsCount = 0; + let originalRemoveElement = this.observerManagerService.removeElement; + const observerManagerService = this.observerManagerService; + + observerManagerService.removeElement = function removeElementExt( + ...args + ) { + removedElementsCount++; + originalRemoveElement.call(observerManagerService, ...args); + }; + for (let index = 0; index < 50; index++) { + await render( + hbs`
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +
`, + ); + } + + assert.strictEqual( + removedElementsCount, + 49 * 2, // once for the element, once for a window object + ); + }); + }, +);