From 163ee956cb872fa3771dd1f67c9cab63a238f82e Mon Sep 17 00:00:00 2001 From: daiwei Date: Sun, 20 Jul 2025 10:47:44 +0800 Subject: [PATCH] test(e2e): add memory leak test for cached text vnodes --- .../vue/__tests__/e2e/memory-leak.spec.ts | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 packages/vue/__tests__/e2e/memory-leak.spec.ts diff --git a/packages/vue/__tests__/e2e/memory-leak.spec.ts b/packages/vue/__tests__/e2e/memory-leak.spec.ts new file mode 100644 index 000000000..2412cea2b --- /dev/null +++ b/packages/vue/__tests__/e2e/memory-leak.spec.ts @@ -0,0 +1,85 @@ +import { E2E_TIMEOUT, setupPuppeteer } from './e2eUtils' +import path from 'node:path' + +const { page, html, click } = setupPuppeteer() + +beforeEach(async () => { + await page().setContent(`
`) + await page().addScriptTag({ + path: path.resolve(__dirname, '../../dist/vue.global.js'), + }) +}) + +describe('not leaking', async () => { + // #13661 + test( + 'cached text vnodes should not retaining detached DOM nodes', + async () => { + const client = await page().createCDPSession() + await page().evaluate(async () => { + const { createApp, ref } = (window as any).Vue + createApp({ + components: { + Comp1: { + template: ` +

+
{{ test.length }}
+ `, + setup() { + const test = ref([...Array(3000)].map((_, i) => ({ i }))) + // @ts-expect-error + window.__REF__ = new WeakRef(test) + + return { test } + }, + }, + Comp2: { + template: `

comp2

`, + }, + }, + template: ` + + +
+ + text node +
+
+ `, + setup() { + const toggle = ref(true) + const click = () => (toggle.value = !toggle.value) + return { toggle, click } + }, + }).mount('#app') + }) + + expect(await html('#app')).toBe( + `` + + `

` + + `
` + + `

comp2

` + + ` text node ` + + `
` + + `

` + + `
3000
`, + ) + + await click('#toggleBtn') + expect(await html('#app')).toBe( + ``, + ) + + const isCollected = async () => + // @ts-expect-error + await page().evaluate(() => window.__REF__.deref() === undefined) + + while ((await isCollected()) === false) { + await client.send('HeapProfiler.collectGarbage') + } + + expect(await isCollected()).toBe(true) + }, + E2E_TIMEOUT, + ) +})