From 8a893e7b6fdc57fcee2fe6409c6a90f307740af7 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 10 Oct 2025 09:13:31 +0200 Subject: [PATCH 01/26] fix events_engine (T1307313) --- .../js/__internal/events/utils/m_event_nodes_disposing.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts index 4db01c1452c5..4acccc5986e5 100644 --- a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts +++ b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts @@ -2,12 +2,18 @@ import eventsEngine from '@js/common/core/events/core/events_engine'; import { removeEvent } from '@js/common/core/events/remove'; function nodesByEvent(event) { + const insideDxParent = (element) => { + const el = element?.closest?.('[class^="dx-"], [class*=" dx-"]'); + + return !!el; + }; + return event && [ event.target, event.delegateTarget, event.relatedTarget, event.currentTarget, - ].filter((node) => !!node); + ].filter((node) => !!node && insideDxParent(node)); } export const subscribeNodesDisposing = (event, callback) => { From 68f8c19a799f24d5fcae97ca643380e407804303 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 13 Oct 2025 23:24:27 +0200 Subject: [PATCH 02/26] fix events_engine (T1307313) --- .../js/__internal/events/utils/m_event_nodes_disposing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts index 4acccc5986e5..3abb896e5968 100644 --- a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts +++ b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts @@ -3,7 +3,7 @@ import { removeEvent } from '@js/common/core/events/remove'; function nodesByEvent(event) { const insideDxParent = (element) => { - const el = element?.closest?.('[class^="dx-"], [class*=" dx-"]'); + const el = element?.closest?.('[class^="dx-"]:not(body), [class*=" dx-"]:not(body)'); return !!el; }; From 1729cf9061f439b588cc6e37bfe3bd0462850540 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Tue, 23 Dec 2025 23:11:51 +0100 Subject: [PATCH 03/26] Revert "fix events_engine (T1307313)" This reverts commit 68f8c19a799f24d5fcae97ca643380e407804303. --- .../js/__internal/events/utils/m_event_nodes_disposing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts index 3abb896e5968..4acccc5986e5 100644 --- a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts +++ b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts @@ -3,7 +3,7 @@ import { removeEvent } from '@js/common/core/events/remove'; function nodesByEvent(event) { const insideDxParent = (element) => { - const el = element?.closest?.('[class^="dx-"]:not(body), [class*=" dx-"]:not(body)'); + const el = element?.closest?.('[class^="dx-"], [class*=" dx-"]'); return !!el; }; From da0241295347c6b390103023fd7d96a61ce466b3 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Tue, 23 Dec 2025 23:12:31 +0100 Subject: [PATCH 04/26] Revert "fix events_engine (T1307313)" This reverts commit 8a893e7b6fdc57fcee2fe6409c6a90f307740af7. --- .../js/__internal/events/utils/m_event_nodes_disposing.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts index 4acccc5986e5..4db01c1452c5 100644 --- a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts +++ b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts @@ -2,18 +2,12 @@ import eventsEngine from '@js/common/core/events/core/events_engine'; import { removeEvent } from '@js/common/core/events/remove'; function nodesByEvent(event) { - const insideDxParent = (element) => { - const el = element?.closest?.('[class^="dx-"], [class*=" dx-"]'); - - return !!el; - }; - return event && [ event.target, event.delegateTarget, event.relatedTarget, event.currentTarget, - ].filter((node) => !!node && insideDxParent(node)); + ].filter((node) => !!node); } export const subscribeNodesDisposing = (event, callback) => { From c80a2078a003f2415ce14ccc616362f1368a2165 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Tue, 23 Dec 2025 23:58:02 +0100 Subject: [PATCH 05/26] fix click subscription in events_engine (T1307313) --- .../devextreme/js/__internal/events/m_click.ts | 15 +++++++++++++-- .../events/utils/m_event_nodes_disposing.ts | 14 +++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/events/m_click.ts b/packages/devextreme/js/__internal/events/m_click.ts index 03bc4dc7d685..dfea4f52c0a0 100644 --- a/packages/devextreme/js/__internal/events/m_click.ts +++ b/packages/devextreme/js/__internal/events/m_click.ts @@ -17,6 +17,7 @@ const misc = { requestAnimationFrame, cancelAnimationFrame }; let prevented: boolean | null = null; let lastFiredEvent = null; +const subscriptions = new Map(); const onNodeRemove = () => { lastFiredEvent = null; @@ -32,9 +33,19 @@ const clickHandler = function (e) { originalEvent.DXCLICK_FIRED = true; } - unsubscribeNodesDisposing(lastFiredEvent, onNodeRemove); + if (subscriptions.has(lastFiredEvent)) { + const { nodes, callback } = subscriptions.get(lastFiredEvent); + + unsubscribeNodesDisposing(lastFiredEvent, callback, nodes); + + subscriptions.delete(lastFiredEvent); + } + lastFiredEvent = originalEvent; - subscribeNodesDisposing(lastFiredEvent, onNodeRemove); + + const subscriptionData = subscribeNodesDisposing(lastFiredEvent, onNodeRemove); + + subscriptions.set(lastFiredEvent, subscriptionData); fireEvent({ type: CLICK_EVENT_NAME, diff --git a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts index 4db01c1452c5..e3d4a304b5e3 100644 --- a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts +++ b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts @@ -11,9 +11,17 @@ function nodesByEvent(event) { } export const subscribeNodesDisposing = (event, callback) => { - eventsEngine.one(nodesByEvent(event), removeEvent, callback); + const nodes = nodesByEvent(event); + const onceCallback = function (...args) { + eventsEngine.off(nodes, removeEvent, onceCallback); + return callback(...args); + }; + + eventsEngine.on(nodes, removeEvent, onceCallback); + + return { callback: onceCallback, nodes }; }; -export const unsubscribeNodesDisposing = (event, callback) => { - eventsEngine.off(nodesByEvent(event), removeEvent, callback); +export const unsubscribeNodesDisposing = (event, callback, nodes) => { + eventsEngine.off(nodes || nodesByEvent(event), removeEvent, callback); }; From a355bc080fd84be8e4238ba37eb81d3c7d047a62 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 26 Dec 2025 11:04:26 +0100 Subject: [PATCH 06/26] add test fix click subscription in events_engine (T1307313) --- .../tests/src/ui/data-grid.spec.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts b/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts index 0d167a1e0663..c5a7e020934f 100644 --- a/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts +++ b/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts @@ -558,4 +558,36 @@ describe('Nested DxDataGrid', () => { }, 1000); }, 1000); }); + + it('should not memory leak after click if dx-data-grid is on page (T1307313)', () => { + TestBed.overrideComponent(TestContainerComponent, { + set: { + template: ``, + }, + }); + + const fixture = TestBed.createComponent(TestContainerComponent); + fixture.detectChanges(); + + for(let i = 0; i < 100; i++) { + document.body.click() + } + + fixture.detectChanges(); + globalThis.gc(); + + let memoryBefore = (performance as any).memory?.usedJSHeapSize + + for(let i = 0; i < 100; i++) { + document.body.click() + } + + fixture.detectChanges(); + globalThis.gc(); + + const memoryDiff = (performance as any).memory?.usedJSHeapSize - memoryBefore; + console.log('------memory---->', memoryDiff / 1024); + + expect(memoryDiff <= 0).toBeTruthy(); + }) }); From 5ca768b188c887b9eab90bd493edacc5ee7f9154 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 26 Dec 2025 13:53:11 +0100 Subject: [PATCH 07/26] add test fix click subscription in events_engine (T1307313) --- packages/devextreme-angular/karma.conf.js | 14 +++++++++- .../tests/src/ui/data-grid.spec.ts | 26 +++++++++---------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/devextreme-angular/karma.conf.js b/packages/devextreme-angular/karma.conf.js index 08a15e7c9e34..508a28ff2821 100644 --- a/packages/devextreme-angular/karma.conf.js +++ b/packages/devextreme-angular/karma.conf.js @@ -18,7 +18,19 @@ module.exports = function (config) { autoWatch: true, - browsers: ['ChromeHeadless'], + browsers: ['ChromeHeadlessWithGC'], + + customLaunchers: { + ChromeHeadlessWithGC: { + base: 'ChromeHeadless', + flags: [ + '--js-flags=--expose-gc', + '--no-sandbox', + '--disable-gpu', + '--enable-precise-memory-info' + ] + } + }, reporters: [ 'progress', diff --git a/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts b/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts index c5a7e020934f..e4bdb7448435 100644 --- a/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts +++ b/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts @@ -562,32 +562,32 @@ describe('Nested DxDataGrid', () => { it('should not memory leak after click if dx-data-grid is on page (T1307313)', () => { TestBed.overrideComponent(TestContainerComponent, { set: { - template: ``, + template: '', }, }); const fixture = TestBed.createComponent(TestContainerComponent); fixture.detectChanges(); - for(let i = 0; i < 100; i++) { - document.body.click() + for (let i = 0; i < 100; i++) { + document.body.click(); } - + fixture.detectChanges(); globalThis.gc(); - - let memoryBefore = (performance as any).memory?.usedJSHeapSize - for(let i = 0; i < 100; i++) { - document.body.click() + const memory = (performance as any).memory; + const jsHeapSizeBefore = memory.usedJSHeapSize + + for (let i = 0; i < 100; i++) { + document.body.click(); } - + fixture.detectChanges(); globalThis.gc(); - - const memoryDiff = (performance as any).memory?.usedJSHeapSize - memoryBefore; - console.log('------memory---->', memoryDiff / 1024); - + + const memoryDiff = memory.usedJSHeapSize - jsHeapSizeBefore; + expect(memoryDiff <= 0).toBeTruthy(); }) }); From c750384a056ff5359885ca7dfc5e8f4ee4a6633a Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 26 Dec 2025 14:27:00 +0100 Subject: [PATCH 08/26] fix test fix click subscription in events_engine (T1307313) --- packages/devextreme-angular/karma.conf.js | 6 +++--- packages/devextreme-angular/tests/src/ui/data-grid.spec.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/devextreme-angular/karma.conf.js b/packages/devextreme-angular/karma.conf.js index 508a28ff2821..de45ea4b51fd 100644 --- a/packages/devextreme-angular/karma.conf.js +++ b/packages/devextreme-angular/karma.conf.js @@ -27,9 +27,9 @@ module.exports = function (config) { '--js-flags=--expose-gc', '--no-sandbox', '--disable-gpu', - '--enable-precise-memory-info' - ] - } + '--enable-precise-memory-info', + ], + }, }, reporters: [ diff --git a/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts b/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts index e4bdb7448435..25b911dc3f2c 100644 --- a/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts +++ b/packages/devextreme-angular/tests/src/ui/data-grid.spec.ts @@ -576,8 +576,8 @@ describe('Nested DxDataGrid', () => { fixture.detectChanges(); globalThis.gc(); - const memory = (performance as any).memory; - const jsHeapSizeBefore = memory.usedJSHeapSize + const { memory } = performance as any; + const jsHeapSizeBefore = memory.usedJSHeapSize; for (let i = 0; i < 100; i++) { document.body.click(); @@ -589,5 +589,5 @@ describe('Nested DxDataGrid', () => { const memoryDiff = memory.usedJSHeapSize - jsHeapSizeBefore; expect(memoryDiff <= 0).toBeTruthy(); - }) + }); }); From f5f3d6ff9a27deb92aef299c39333f540c06ff23 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 26 Dec 2025 17:17:13 +0100 Subject: [PATCH 09/26] add qunit test fix click subscription in events_engine (T1307313) --- packages/devextreme/testing/launch | 6 +++- .../event_nodes_disposing.tests.js | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js diff --git a/packages/devextreme/testing/launch b/packages/devextreme/testing/launch index 04e9421f6c32..3620283c41e5 100755 --- a/packages/devextreme/testing/launch +++ b/packages/devextreme/testing/launch @@ -41,7 +41,11 @@ function waitForRunner() { function openBrowser() { spawn( getBrowserCommand(), - [ 'http://localhost:' + PORT ], + [ + '--js-flags=--expose-gc', + '--enable-precise-memory-info', + 'http://localhost:' + PORT, + ], { shell: true, detached: true } ); } diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js new file mode 100644 index 000000000000..2f31384533f1 --- /dev/null +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -0,0 +1,32 @@ +import $ from 'jquery'; + +QUnit.testStart(function() { + const markup = '
'; + $('#qunit-fixture').html(markup); +}); + +QUnit.module('event nodes disposing'); + +QUnit.test('should not leak memory when clicking on body with DxDataGrid on page', function(assert) { + $('#dataGrid').dxDataGrid({ + dataSource: [] + }); + + globalThis.gc(); + + const initialMemory = performance.memory.usedJSHeapSize; + + for(let i = 0; i < 100; i++) { + $('body').trigger('click'); + } + + globalThis.gc(); + + const finalMemory = performance.memory.usedJSHeapSize; + const memoryDiff = finalMemory - initialMemory; + + assert.ok( + memoryDiffMB <= 0, + `Memory should not leak. Memory diff: ${memoryDiff}B` + ); +}); From 314abed03967fd2ba7594c4a898811f317db26ea Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 26 Dec 2025 20:05:08 +0100 Subject: [PATCH 10/26] add qunit test fix click subscription in events_engine (T1307313) --- .../event_nodes_disposing.tests.js | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 2f31384533f1..a195d34d7454 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -1,16 +1,22 @@ import $ from 'jquery'; +import eventsEngine from 'common/core/events/core/events_engine'; +import domAdapter from '__internal/core/m_dom_adapter'; QUnit.testStart(function() { - const markup = '
'; + const markup = ''; $('#qunit-fixture').html(markup); }); -QUnit.module('event nodes disposing'); +QUnit.module('event nodes disposing', { + afterEach: function() { + const document = domAdapter.getDocument(); + eventsEngine.off(document, 'dxclick'); + } +}); -QUnit.test('should not leak memory when clicking on body with DxDataGrid on page', function(assert) { - $('#dataGrid').dxDataGrid({ - dataSource: [] - }); +QUnit.test('should not leak memory when clicking on body with dxclick subscription on document', function(assert) { + const document = domAdapter.getDocument(); + $(document).on('dxclick', function() {}); globalThis.gc(); @@ -26,7 +32,7 @@ QUnit.test('should not leak memory when clicking on body with DxDataGrid on page const memoryDiff = finalMemory - initialMemory; assert.ok( - memoryDiffMB <= 0, + memoryDiff <= 0, `Memory should not leak. Memory diff: ${memoryDiff}B` ); }); From 37bcfc5a72191a61cf1cfdc5254363ac522f1d9d Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 26 Dec 2025 21:37:44 +0100 Subject: [PATCH 11/26] add qunit test fix click subscription in events_engine (T1307313) --- packages/devextreme/testing/launch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/devextreme/testing/launch b/packages/devextreme/testing/launch index 3620283c41e5..08bcae36d543 100755 --- a/packages/devextreme/testing/launch +++ b/packages/devextreme/testing/launch @@ -41,7 +41,7 @@ function waitForRunner() { function openBrowser() { spawn( getBrowserCommand(), - [ + [ '""', '--js-flags=--expose-gc', '--enable-precise-memory-info', 'http://localhost:' + PORT, @@ -53,13 +53,13 @@ function openBrowser() { function getBrowserCommand() { switch(platform()) { case 'win32': - return 'start'; + return 'start chrome'; case 'darwin': return 'open'; case 'linux': - return 'xdg-open'; + return 'google-chrome'; } throw 'Not implemented'; From eaf8de3cc25a4b66a5fdaa43caa78812539a5d04 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 26 Dec 2025 22:13:16 +0100 Subject: [PATCH 12/26] add qunit test fix click subscription in events_engine (T1307313) --- .../tests/DevExpress.ui.events/event_nodes_disposing.tests.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index a195d34d7454..3eef64709397 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -18,16 +18,12 @@ QUnit.test('should not leak memory when clicking on body with dxclick subscripti const document = domAdapter.getDocument(); $(document).on('dxclick', function() {}); - globalThis.gc(); - const initialMemory = performance.memory.usedJSHeapSize; for(let i = 0; i < 100; i++) { $('body').trigger('click'); } - globalThis.gc(); - const finalMemory = performance.memory.usedJSHeapSize; const memoryDiff = finalMemory - initialMemory; From cfc4a23b21b8f4329f347297aa603b538bcd7802 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 27 Dec 2025 01:11:33 +0100 Subject: [PATCH 13/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../js/__internal/events/utils/m_event_nodes_disposing.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts index e3d4a304b5e3..6182f69d3ac6 100644 --- a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts +++ b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts @@ -22,6 +22,6 @@ export const subscribeNodesDisposing = (event, callback) => { return { callback: onceCallback, nodes }; }; -export const unsubscribeNodesDisposing = (event, callback, nodes) => { - eventsEngine.off(nodes || nodesByEvent(event), removeEvent, callback); +export const unsubscribeNodesDisposing = (event, callback /* , nodes */) => { + eventsEngine.off(/* nodes || */ nodesByEvent(event), removeEvent, callback); }; From 1c191084dd7497d511263ad960e70afd17dd598a Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 27 Dec 2025 01:35:02 +0100 Subject: [PATCH 14/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../js/__internal/events/utils/m_event_nodes_disposing.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts index 6182f69d3ac6..cd7cc9718292 100644 --- a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts +++ b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts @@ -22,6 +22,8 @@ export const subscribeNodesDisposing = (event, callback) => { return { callback: onceCallback, nodes }; }; -export const unsubscribeNodesDisposing = (event, callback /* , nodes */) => { - eventsEngine.off(/* nodes || */ nodesByEvent(event), removeEvent, callback); +export const unsubscribeNodesDisposing = (event, callback, nodes) => { + if (nodes || !nodes) { + eventsEngine.off(/* nodes || */ nodesByEvent(event), removeEvent, callback); + } }; From 1b8b3e20d5494dc920a25205b811b8ac0cf75589 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 27 Dec 2025 01:35:46 +0100 Subject: [PATCH 15/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../js/__internal/events/utils/m_event_nodes_disposing.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts index cd7cc9718292..87c6f8b8e5d4 100644 --- a/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts +++ b/packages/devextreme/js/__internal/events/utils/m_event_nodes_disposing.ts @@ -23,7 +23,5 @@ export const subscribeNodesDisposing = (event, callback) => { }; export const unsubscribeNodesDisposing = (event, callback, nodes) => { - if (nodes || !nodes) { - eventsEngine.off(/* nodes || */ nodesByEvent(event), removeEvent, callback); - } + eventsEngine.off(/* nodes || */ nodesByEvent(event) || nodes, removeEvent, callback); }; From 2b439a5deae46578828dca61b2aca76202b6d1ad Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 27 Dec 2025 10:25:16 +0100 Subject: [PATCH 16/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../DevExpress.ui.events/event_nodes_disposing.tests.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 3eef64709397..38c89874bd23 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -1,12 +1,6 @@ -import $ from 'jquery'; import eventsEngine from 'common/core/events/core/events_engine'; import domAdapter from '__internal/core/m_dom_adapter'; -QUnit.testStart(function() { - const markup = ''; - $('#qunit-fixture').html(markup); -}); - QUnit.module('event nodes disposing', { afterEach: function() { const document = domAdapter.getDocument(); From 0c94bde9eb4ee4165df5c6cb7a78d5b51205f3b5 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 29 Dec 2025 16:40:46 +0100 Subject: [PATCH 17/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../event_nodes_disposing.tests.js | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 38c89874bd23..fdbfc603c075 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -1,6 +1,14 @@ import eventsEngine from 'common/core/events/core/events_engine'; import domAdapter from '__internal/core/m_dom_adapter'; +QUnit.testStart(function() { + const markup = ''; + const fixture = document.getElementById('qunit-fixture'); + if(fixture) { + fixture.innerHTML = markup; + } +}); + QUnit.module('event nodes disposing', { afterEach: function() { const document = domAdapter.getDocument(); @@ -10,12 +18,22 @@ QUnit.module('event nodes disposing', { QUnit.test('should not leak memory when clicking on body with dxclick subscription on document', function(assert) { const document = domAdapter.getDocument(); - $(document).on('dxclick', function() {}); + const body = document.body; + + eventsEngine.on(document, 'dxclick', function() {}); + + if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { + globalThis.gc(); + } const initialMemory = performance.memory.usedJSHeapSize; for(let i = 0; i < 100; i++) { - $('body').trigger('click'); + eventsEngine.trigger(body, 'click'); + } + + if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { + globalThis.gc(); } const finalMemory = performance.memory.usedJSHeapSize; From 32c7b1c91426a6fda67ac4c63010140a45513406 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 29 Dec 2025 20:52:58 +0100 Subject: [PATCH 18/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../tests/DevExpress.ui.events/event_nodes_disposing.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index fdbfc603c075..cb395517f3a3 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -40,7 +40,7 @@ QUnit.test('should not leak memory when clicking on body with dxclick subscripti const memoryDiff = finalMemory - initialMemory; assert.ok( - memoryDiff <= 0, + memoryDiff > 0, `Memory should not leak. Memory diff: ${memoryDiff}B` ); }); From c4ce5b0c05df44c8e4570a464b3f753f8a6ea272 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 29 Dec 2025 21:03:14 +0100 Subject: [PATCH 19/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../event_nodes_disposing.tests.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index cb395517f3a3..689789a7b179 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -2,8 +2,9 @@ import eventsEngine from 'common/core/events/core/events_engine'; import domAdapter from '__internal/core/m_dom_adapter'; QUnit.testStart(function() { - const markup = ''; + const markup = ''; const fixture = document.getElementById('qunit-fixture'); + if(fixture) { fixture.innerHTML = markup; } @@ -18,7 +19,7 @@ QUnit.module('event nodes disposing', { QUnit.test('should not leak memory when clicking on body with dxclick subscription on document', function(assert) { const document = domAdapter.getDocument(); - const body = document.body; + const button = document.body.querySelector('#btn_test'); eventsEngine.on(document, 'dxclick', function() {}); @@ -29,7 +30,7 @@ QUnit.test('should not leak memory when clicking on body with dxclick subscripti const initialMemory = performance.memory.usedJSHeapSize; for(let i = 0; i < 100; i++) { - eventsEngine.trigger(body, 'click'); + eventsEngine.trigger(button, 'click'); } if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { @@ -40,7 +41,7 @@ QUnit.test('should not leak memory when clicking on body with dxclick subscripti const memoryDiff = finalMemory - initialMemory; assert.ok( - memoryDiff > 0, - `Memory should not leak. Memory diff: ${memoryDiff}B` + finalMemory <= 0, + `Memory should not leak. Memory diff: ${initialMemory} ${finalMemory}B` ); }); From f342809849d10f80e8b550ababa11076e2897796 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 29 Dec 2025 21:56:20 +0100 Subject: [PATCH 20/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../event_nodes_disposing.tests.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 689789a7b179..9ad79b2cb455 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -2,9 +2,8 @@ import eventsEngine from 'common/core/events/core/events_engine'; import domAdapter from '__internal/core/m_dom_adapter'; QUnit.testStart(function() { - const markup = ''; + const markup = ''; const fixture = document.getElementById('qunit-fixture'); - if(fixture) { fixture.innerHTML = markup; } @@ -17,9 +16,9 @@ QUnit.module('event nodes disposing', { } }); -QUnit.test('should not leak memory when clicking on body with dxclick subscription on document', function(assert) { +QUnit.test('should not leak memory when subscribing to dxclick on document and clicking elements', function(assert) { const document = domAdapter.getDocument(); - const button = document.body.querySelector('#btn_test'); + const testElement = document.getElementById('test-element'); eventsEngine.on(document, 'dxclick', function() {}); @@ -30,7 +29,7 @@ QUnit.test('should not leak memory when clicking on body with dxclick subscripti const initialMemory = performance.memory.usedJSHeapSize; for(let i = 0; i < 100; i++) { - eventsEngine.trigger(button, 'click'); + eventsEngine.trigger(testElement, 'click'); } if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { @@ -42,6 +41,6 @@ QUnit.test('should not leak memory when clicking on body with dxclick subscripti assert.ok( finalMemory <= 0, - `Memory should not leak. Memory diff: ${initialMemory} ${finalMemory}B` + `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${finalMemory - initialMemory} ${finalMemory}B` ); }); From 6ad190f8e80fccb76db461a41c14635fbae74d59 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 29 Dec 2025 21:57:30 +0100 Subject: [PATCH 21/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../tests/DevExpress.ui.events/event_nodes_disposing.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 9ad79b2cb455..086d8e6e38c2 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -41,6 +41,6 @@ QUnit.test('should not leak memory when subscribing to dxclick on document and c assert.ok( finalMemory <= 0, - `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${finalMemory - initialMemory} ${finalMemory}B` + `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${memoryDiff}B` ); }); From 9b891ec44fe90e8b88f71f9b7605b192b365645b Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 29 Dec 2025 22:49:22 +0100 Subject: [PATCH 22/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../DevExpress.ui.events/event_nodes_disposing.tests.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 086d8e6e38c2..fe0cc80a10dd 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -1,5 +1,7 @@ import eventsEngine from 'common/core/events/core/events_engine'; import domAdapter from '__internal/core/m_dom_adapter'; +// import clickEventName from 'common/core/events/m_click'; +const clickEventName = 'dxclick.dxDataGridEditorFactory focusin.dxDataGridEditorFactory'; QUnit.testStart(function() { const markup = ''; @@ -12,7 +14,7 @@ QUnit.testStart(function() { QUnit.module('event nodes disposing', { afterEach: function() { const document = domAdapter.getDocument(); - eventsEngine.off(document, 'dxclick'); + eventsEngine.off(document, clickEventName); } }); @@ -20,7 +22,7 @@ QUnit.test('should not leak memory when subscribing to dxclick on document and c const document = domAdapter.getDocument(); const testElement = document.getElementById('test-element'); - eventsEngine.on(document, 'dxclick', function() {}); + eventsEngine.on(document, clickEventName, function() {}); if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { globalThis.gc(); From 3d32046cea23047c43e9a96983caf1cd226ffae5 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 29 Dec 2025 23:31:02 +0100 Subject: [PATCH 23/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../tests/DevExpress.ui.events/event_nodes_disposing.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index fe0cc80a10dd..1e95eba2516e 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -43,6 +43,6 @@ QUnit.test('should not leak memory when subscribing to dxclick on document and c assert.ok( finalMemory <= 0, - `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${memoryDiff}B` + `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${memoryDiff}B ${testElement}` ); }); From 79463ede225ec57aac5eaaca4041e4a96e5750ee Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 29 Dec 2025 23:39:37 +0100 Subject: [PATCH 24/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../event_nodes_disposing.tests.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 1e95eba2516e..6d5c5ae1e082 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -19,6 +19,7 @@ QUnit.module('event nodes disposing', { }); QUnit.test('should not leak memory when subscribing to dxclick on document and clicking elements', function(assert) { + const done = assert.async(); const document = domAdapter.getDocument(); const testElement = document.getElementById('test-element'); @@ -31,18 +32,21 @@ QUnit.test('should not leak memory when subscribing to dxclick on document and c const initialMemory = performance.memory.usedJSHeapSize; for(let i = 0; i < 100; i++) { - eventsEngine.trigger(testElement, 'click'); + testElement.click(); } if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { globalThis.gc(); } - const finalMemory = performance.memory.usedJSHeapSize; - const memoryDiff = finalMemory - initialMemory; + setTimeout(() => { + const finalMemory = performance.memory.usedJSHeapSize; + const memoryDiff = finalMemory - initialMemory; - assert.ok( - finalMemory <= 0, - `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${memoryDiff}B ${testElement}` - ); + assert.ok( + finalMemory <= 0, + `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${memoryDiff}B ${testElement}` + ); + done(); + }, 0); }); From 04c73582ac64600a788df50c0ca947ed529f2d9d Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Tue, 30 Dec 2025 00:08:21 +0100 Subject: [PATCH 25/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../event_nodes_disposing.tests.js | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 6d5c5ae1e082..981d00606f1c 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -1,7 +1,6 @@ import eventsEngine from 'common/core/events/core/events_engine'; import domAdapter from '__internal/core/m_dom_adapter'; -// import clickEventName from 'common/core/events/m_click'; -const clickEventName = 'dxclick.dxDataGridEditorFactory focusin.dxDataGridEditorFactory'; +import clickEventName from 'common/core/events/m_click'; QUnit.testStart(function() { const markup = ''; @@ -22,8 +21,11 @@ QUnit.test('should not leak memory when subscribing to dxclick on document and c const done = assert.async(); const document = domAdapter.getDocument(); const testElement = document.getElementById('test-element'); + let count = 0; - eventsEngine.on(document, clickEventName, function() {}); + eventsEngine.on(document, clickEventName, function() { + count++; + }); if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { globalThis.gc(); @@ -31,22 +33,29 @@ QUnit.test('should not leak memory when subscribing to dxclick on document and c const initialMemory = performance.memory.usedJSHeapSize; - for(let i = 0; i < 100; i++) { - testElement.click(); - } - - if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { - globalThis.gc(); - } + let i = 0; - setTimeout(() => { - const finalMemory = performance.memory.usedJSHeapSize; - const memoryDiff = finalMemory - initialMemory; - - assert.ok( - finalMemory <= 0, - `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${memoryDiff}B ${testElement}` - ); - done(); - }, 0); + const interval = setInterval(() => { + testElement.click(); + i++; + + if(i > 99) { + setTimeout(() => { + if(typeof globalThis !== 'undefined' && typeof globalThis.gc === 'function') { + globalThis.gc(); + } + + const finalMemory = performance.memory.usedJSHeapSize; + const memoryDiff = finalMemory - initialMemory; + + assert.ok( + finalMemory <= 0, + `Memory should not leak. Memory before: ${initialMemory}B, Memory after: ${finalMemory}B, Memory diff: ${memoryDiff}B ${count}` + ); + done(); + }, 50); + + clearInterval(interval); + } + }, 50); }); From 149aa8df1e0c345fab8a03271978dbc03ccd4140 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Tue, 30 Dec 2025 00:24:45 +0100 Subject: [PATCH 26/26] WIP. changes for check test fix click subscription in events_engine (T1307313) --- .../tests/DevExpress.ui.events/event_nodes_disposing.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js index 981d00606f1c..7e0e70c2798d 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.events/event_nodes_disposing.tests.js @@ -1,6 +1,6 @@ import eventsEngine from 'common/core/events/core/events_engine'; import domAdapter from '__internal/core/m_dom_adapter'; -import clickEventName from 'common/core/events/m_click'; +import clickEventName from 'common/core/events/click'; QUnit.testStart(function() { const markup = '';