]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test(compiler): tests for ast transform
authorEvan You <yyx990803@gmail.com>
Thu, 19 Sep 2019 16:20:59 +0000 (12:20 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 19 Sep 2019 17:24:02 +0000 (13:24 -0400)
packages/compiler-core/__tests__/parse.spec.ts
packages/compiler-core/__tests__/transform.spec.ts [new file with mode: 0644]
packages/compiler-core/src/transform.ts

index 348dbd9e45005a8479c59085006ac46968bf0bbd..d8945f9deb25de15ca4bb3b72e9078fde0f428a3 100644 (file)
@@ -12,7 +12,7 @@ import {
   AttributeNode
 } from '../src/ast'
 
-describe('base parser', () => {
+describe('compiler: parse', () => {
   describe('Text', () => {
     test('simple text', () => {
       const ast = parse('some text')
diff --git a/packages/compiler-core/__tests__/transform.spec.ts b/packages/compiler-core/__tests__/transform.spec.ts
new file mode 100644 (file)
index 0000000..2875e09
--- /dev/null
@@ -0,0 +1,128 @@
+import { parse } from '../src/parse'
+import { transform, Transform } from '../src/transform'
+import { ElementNode, NodeTypes } from '../src/ast'
+import { ErrorCodes, createCompilerError } from '../src/errors'
+
+describe('compiler: transform', () => {
+  test('context state', () => {
+    const ast = parse(`<div>hello {{ world }}</div>`)
+
+    // manually store call arguments because context is mutable and shared
+    // across calls
+    const calls: any[] = []
+    const plugin: Transform = (node, context) => {
+      calls.push([node, Object.assign({}, context)])
+    }
+
+    transform(ast, {
+      transforms: [plugin]
+    })
+
+    const div = ast.children[0] as ElementNode
+    expect(calls.length).toBe(3)
+    expect(calls[0]).toMatchObject([
+      div,
+      {
+        parent: ast,
+        ancestors: [ast],
+        childIndex: 0
+      }
+    ])
+    expect(calls[1]).toMatchObject([
+      div.children[0],
+      {
+        parent: div,
+        ancestors: [ast, div],
+        childIndex: 0
+      }
+    ])
+    expect(calls[2]).toMatchObject([
+      div.children[1],
+      {
+        parent: div,
+        ancestors: [ast, div],
+        childIndex: 1
+      }
+    ])
+  })
+
+  test('context.replaceNode', () => {
+    const ast = parse(`<div/><span/>`)
+    const plugin: Transform = (node, context) => {
+      if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
+        // change the node to <p>
+        context.replaceNode(
+          Object.assign({}, node, {
+            tag: 'p',
+            children: [
+              {
+                type: NodeTypes.TEXT,
+                content: 'hello',
+                isEmpty: false
+              }
+            ]
+          })
+        )
+      }
+    }
+    const spy = jest.fn(plugin)
+    transform(ast, {
+      transforms: [spy]
+    })
+
+    expect(ast.children.length).toBe(2)
+    const newElement = ast.children[0] as ElementNode
+    expect(newElement.tag).toBe('p')
+    expect(spy).toHaveBeenCalledTimes(3)
+    // should traverse the children of replaced node
+    expect(spy.mock.calls[1][0]).toBe(newElement.children[0])
+    // should traverse the node after the replaced node
+    expect(spy.mock.calls[2][0]).toBe(ast.children[1])
+  })
+
+  test('context.removeNode', () => {
+    const ast = parse(`<span/><div/><span/>`)
+    const c1 = ast.children[0]
+    const c2 = ast.children[2]
+
+    const plugin: Transform = (node, context) => {
+      if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
+        context.removeNode()
+      }
+    }
+    const spy = jest.fn(plugin)
+    transform(ast, {
+      transforms: [spy]
+    })
+
+    expect(ast.children.length).toBe(2)
+    expect(ast.children[0]).toBe(c1)
+    expect(ast.children[1]).toBe(c2)
+
+    expect(spy).toHaveBeenCalledTimes(3)
+    // should traverse nodes around removed
+    expect(spy.mock.calls[0][0]).toBe(c1)
+    expect(spy.mock.calls[2][0]).toBe(c2)
+  })
+
+  test('onError option', () => {
+    const ast = parse(`<div/>`)
+    const loc = ast.children[0].loc.start
+    const plugin: Transform = (node, context) => {
+      context.onError(
+        createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
+      )
+    }
+    const spy = jest.fn()
+    transform(ast, {
+      transforms: [plugin],
+      onError: spy
+    })
+    expect(spy.mock.calls[0]).toMatchObject([
+      {
+        code: ErrorCodes.X_INVALID_END_TAG,
+        loc
+      }
+    ])
+  })
+})
index 8ef56927f572b2b0e9562d3a140c358c214087a3..c7615e391e0fcc01f620acad5497b5b1706705a7 100644 (file)
@@ -46,9 +46,12 @@ function createTransformContext(
     },
     ...options,
     parent: root,
-    ancestors: [root],
+    ancestors: [],
     childIndex: 0,
     replaceNode(node) {
+      if (__DEV__ && context.nodeRemoved) {
+        throw new Error(`node being replaced is already removed`)
+      }
       context.parent.children[context.childIndex] = node
     },
     removeNode() {
@@ -85,9 +88,9 @@ function traverseNode(
   // apply transform plugins
   const transforms = context.transforms
   for (let i = 0; i < transforms.length; i++) {
-    const transform = transforms[i]
+    const plugin = transforms[i]
     context.nodeRemoved = false
-    transform(node, context)
+    plugin(node, context)
     if (context.nodeRemoved) {
       return
     } else {