Skip to content

Commit bfe34b5

Browse files
committed
test(vue): cover vue.plugins factory form and render isolation
1 parent 9779dd3 commit bfe34b5

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

src/tests/render/config.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
22
import { rmSync, writeFileSync } from 'node:fs'
33
import { join } from 'node:path'
44
import { render } from '../../render/index.ts'
5+
import { createRenderer } from '../../render/createRenderer.ts'
6+
import { resolveConfig } from '../../config/index.ts'
57
import { createTempProject } from './_helpers.ts'
68

79
describe('render', () => {
@@ -341,5 +343,83 @@ describe('render', () => {
341343
expect(result.html).toContain('data-marked')
342344
expect(result.html).toContain('Everything works')
343345
})
346+
347+
it('accepts plugins as a factory and calls it on every render', async () => {
348+
const resolvedConfig = await resolveConfig({
349+
root: tempDir,
350+
})
351+
352+
const renderer = await createRenderer({ root: tempDir })
353+
354+
try {
355+
let factoryCalls = 0
356+
const seenLabels: string[] = []
357+
358+
const config = {
359+
...resolvedConfig,
360+
vue: {
361+
plugins: () => {
362+
factoryCalls++
363+
return [{
364+
install(app: any) {
365+
app.config.globalProperties.$label = `instance-${factoryCalls}`
366+
},
367+
}]
368+
},
369+
},
370+
}
371+
372+
const a = await renderer.render(`<template><div>{{ $label }}</div></template>`, config)
373+
const b = await renderer.render(`<template><div>{{ $label }}</div></template>`, config)
374+
375+
expect(factoryCalls).toBe(2)
376+
seenLabels.push(a.html, b.html)
377+
expect(seenLabels[0]).toContain('instance-1')
378+
expect(seenLabels[1]).toContain('instance-2')
379+
} finally {
380+
await renderer.close()
381+
}
382+
})
383+
384+
it('isolates stateful plugins across renders (no shared state leak)', async () => {
385+
const resolvedConfig = await resolveConfig({
386+
root: tempDir,
387+
})
388+
389+
const renderer = await createRenderer({ root: tempDir })
390+
391+
try {
392+
// A stateful plugin: each instance owns its own mutable counter
393+
// exposed via $counter. If the framework reused one instance
394+
// across renders, the second render would observe `1` instead of `0`.
395+
const config = {
396+
...resolvedConfig,
397+
vue: {
398+
plugins: () => {
399+
const state = { value: 0 }
400+
return [{
401+
install(app: any) {
402+
app.config.globalProperties.$counter = state
403+
app.config.globalProperties.$bump = () => { state.value++ }
404+
},
405+
}]
406+
},
407+
},
408+
}
409+
410+
const first = await renderer.render(`
411+
<template><div>{{ ($bump(), $counter.value) }}</div></template>
412+
`, config)
413+
414+
const second = await renderer.render(`
415+
<template><div>{{ $counter.value }}</div></template>
416+
`, config)
417+
418+
expect(first.html).toContain('>1<')
419+
expect(second.html).toContain('>0<')
420+
} finally {
421+
await renderer.close()
422+
}
423+
})
344424
})
345425
})

0 commit comments

Comments
 (0)