]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: add tests
authordaiwei <daiwei521@126.com>
Fri, 25 Jul 2025 09:09:09 +0000 (17:09 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 25 Jul 2025 09:09:09 +0000 (17:09 +0800)
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
packages/compiler-vapor/src/generators/prop.ts
packages/runtime-vapor/__tests__/dom/mathML.spec.ts [new file with mode: 0644]
packages/runtime-vapor/__tests__/dom/svg.spec.ts [new file with mode: 0644]

index 7aa56aa9c2f880745a40a482fc1fbdfe419e2dcd..5a3c13946bb57b06d333eaff560fc45640f9bf49 100644 (file)
@@ -1,5 +1,15 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
+exports[`compiler: element transform > MathML 1`] = `
+"import { template as _template } from 'vue';
+const t0 = _template("<math><mrow><mi>x</mi></mrow></math>", true, 2)
+
+export function render(_ctx) {
+  const n0 = t0()
+  return n0
+}"
+`;
+
 exports[`compiler: element transform > component > cache v-on expression with unique handler name 1`] = `
 "import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
@@ -407,6 +417,16 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`compiler: element transform > svg 1`] = `
+"import { template as _template } from 'vue';
+const t0 = _template("<svg><circle r=\\"40\\"></circle></svg>", true, 1)
+
+export function render(_ctx) {
+  const n0 = t0()
+  return n0
+}"
+`;
+
 exports[`compiler: element transform > v-bind="obj" 1`] = `
 "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
index 4ea0db55fe5172aad4c608d1cd6e8d1e59b05704..edac6aef96ac15838faa5615c87af84e022be2fe 100644 (file)
@@ -465,6 +465,17 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`compiler v-bind > :class w/ svg elements 1`] = `
+"import { setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<svg></svg>", true, 1)
+
+export function render(_ctx) {
+  const n0 = t0()
+  _renderEffect(() => _setAttr(n0, "class", _ctx.cls))
+  return n0
+}"
+`;
+
 exports[`compiler v-bind > :innerHTML 1`] = `
 "import { setHtml as _setHtml, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
index a693db4ad3961f85cd9367650b565217880c4479..4c9e5c5c2e9dde8848c444dc4338ba364c3087d9 100644 (file)
@@ -956,4 +956,26 @@ describe('compiler: element transform', () => {
     expect(code).toMatchSnapshot()
     expect(code).contain('return null')
   })
+
+  test('svg', () => {
+    const t = `<svg><circle r="40"></circle></svg>`
+    const { code, ir } = compileWithElementTransform(t)
+    expect(code).toMatchSnapshot()
+    expect(code).contains(
+      '_template("<svg><circle r=\\"40\\"></circle></svg>", true, 1)',
+    )
+    expect(ir.template).toMatchObject([t])
+    expect(ir.templateNS.get(t)).toBe(1)
+  })
+
+  test('MathML', () => {
+    const t = `<math><mrow><mi>x</mi></mrow></math>`
+    const { code, ir } = compileWithElementTransform(t)
+    expect(code).toMatchSnapshot()
+    expect(code).contains(
+      '_template("<math><mrow><mi>x</mi></mrow></math>", true, 2)',
+    )
+    expect(ir.template).toMatchObject([t])
+    expect(ir.templateNS.get(t)).toBe(2)
+  })
 })
index e96186c275c608ca8a64b5fae3027567800cde96..a36a76d8385b9294ddeccdb4c33508e5948dc1fc 100644 (file)
@@ -656,6 +656,14 @@ describe('compiler v-bind', () => {
     expect(code).contains('_setProp(n0, "value", _ctx.foo)')
   })
 
+  test(':class w/ svg elements', () => {
+    const { code } = compileWithVBind(`
+      <svg :class="cls"/>
+    `)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setAttr(n0, "class", _ctx.cls))')
+  })
+
   test('number value', () => {
     const { code } = compileWithVBind(`<Comp :depth="0" />`)
     expect(code).matchSnapshot()
index 42f063331fc1090ffc8669b04a54490e234ef2da..ef1ebba2ae4984f54f469f4160a066d8c20dd010 100644 (file)
@@ -169,16 +169,17 @@ function getRuntimeHelper(
   modifier: '.' | '^' | undefined,
 ): HelperConfig {
   const tagName = tag.toUpperCase()
+  const isSVG = isSVGTag(tag)
   if (modifier) {
     if (modifier === '.') {
-      return getSpecialHelper(key, tagName) || helpers.setDOMProp
+      return getSpecialHelper(key, tagName, isSVG) || helpers.setDOMProp
     } else {
       return helpers.setAttr
     }
   }
 
   // 1. special handling for value / style / class / textContent /  innerHTML
-  const helper = getSpecialHelper(key, tagName)
+  const helper = getSpecialHelper(key, tagName, isSVG)
   if (helper) {
     return helper
   }
@@ -190,7 +191,7 @@ function getRuntimeHelper(
   }
 
   // 3. SVG: always attribute
-  if (isSVGTag(tag)) {
+  if (isSVG) {
     // TODO pass svg flag
     return helpers.setAttr
   }
@@ -209,12 +210,13 @@ function getRuntimeHelper(
 function getSpecialHelper(
   keyName: string,
   tagName: string,
+  isSVG: boolean,
 ): HelperConfig | undefined {
   // special case for 'value' property
   if (keyName === 'value' && canSetValueDirectly(tagName)) {
     return helpers.setValue
   } else if (keyName === 'class') {
-    return helpers.setClass
+    return isSVG ? helpers.setAttr : helpers.setClass
   } else if (keyName === 'style') {
     return helpers.setStyle
   } else if (keyName === 'innerHTML') {
diff --git a/packages/runtime-vapor/__tests__/dom/mathML.spec.ts b/packages/runtime-vapor/__tests__/dom/mathML.spec.ts
new file mode 100644 (file)
index 0000000..148d4c3
--- /dev/null
@@ -0,0 +1 @@
+describe.todo('MathML support', () => {})
diff --git a/packages/runtime-vapor/__tests__/dom/svg.spec.ts b/packages/runtime-vapor/__tests__/dom/svg.spec.ts
new file mode 100644 (file)
index 0000000..5f0f79d
--- /dev/null
@@ -0,0 +1,65 @@
+import { makeRender } from '../_utils'
+import { template } from '../../src/dom/template'
+import { child } from '../../src/dom/node'
+import { setAttr, setClass } from '../../src/dom/prop'
+import { renderEffect } from '../../src'
+import { nextTick, ref } from '@vue/runtime-dom'
+
+const define = makeRender()
+
+describe('SVG support', () => {
+  afterEach(() => {
+    document.body.innerHTML = ''
+  })
+
+  test('should mount elements with correct html namespace', () => {
+    define({
+      setup() {
+        const t0 = template(
+          '<div id="e0"><svg id="e1"><foreignObject id="e2"><div id="e3"></div><svg id="e4"></svg><math id="e5"></math></foreignObject></svg></div>',
+          true,
+        )
+        return t0()
+      },
+    }).render()
+
+    const e0 = document.getElementById('e0')!
+    expect(e0.namespaceURI).toMatch('xhtml')
+    expect(e0.querySelector('#e1')!.namespaceURI).toMatch('svg')
+    expect(e0.querySelector('#e2')!.namespaceURI).toMatch('svg')
+    expect(e0.querySelector('#e3')!.namespaceURI).toMatch('xhtml')
+    expect(e0.querySelector('#e4')!.namespaceURI).toMatch('svg')
+    expect(e0.querySelector('#e5')!.namespaceURI).toMatch('Math')
+  })
+
+  test('should patch elements with correct namespaces', async () => {
+    const cls = ref('foo')
+    define({
+      setup() {
+        const t0 = template(
+          '<div><svg id="f1"><foreignObject><div id="f2">hi</div></foreignObject></svg></div>',
+          true,
+        )
+        const n2 = t0() as HTMLElement
+        const n1 = child(n2) as HTMLElement
+        const p0 = child(n1) as HTMLElement
+        const n0 = child(p0) as HTMLElement
+        renderEffect(() => {
+          const _cls = cls.value
+          setAttr(n1, 'class', _cls)
+          setClass(n0, _cls)
+        })
+        return n2
+      },
+    }).render()
+    const f1 = document.querySelector('#f1')!
+    const f2 = document.querySelector('#f2')!
+    expect(f1.getAttribute('class')).toBe('foo')
+    expect(f2.className).toBe('foo')
+
+    cls.value = 'bar'
+    await nextTick()
+    expect(f1.getAttribute('class')).toBe('bar')
+    expect(f2.className).toBe('bar')
+  })
+})