]> source.dussan.org Git - svg.js.git/commitdiff
fix defs and reference, tests for Element
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Wed, 8 Apr 2020 00:08:36 +0000 (10:08 +1000)
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Wed, 8 Apr 2020 00:08:36 +0000 (10:08 +1000)
CHANGELOG.md
spec/spec/elements/Element.js [new file with mode: 0644]
spec/spec/types/Box.js
src/elements/Element.js
src/types/Box.js

index 83af8ea6c8860cd4ab0664e97469e4b7350f3e4e..54cb5bf5a95e295267c7243258872434bbf7f1c6 100644 (file)
@@ -23,6 +23,8 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:
  - fixed `add()` which correctly removes namespaces of non-root svg elements now when added to another svg element (#1086)
  - fixed `isRoot()` which correctly returns false, if the element is in a document-fragment
  - fixed `replace()` which works without a parent now, too
+ - fixed `defs()` which correctly returns `null` when called on a detached node that is not a root node
+ - fixed `reference()` which correctly returns `null` instead of throwing when specifying an attribute which holds a number
 
 ### Added
  - added second Parameter to `SVG(el, isHTML)` which allows to explicitely create elements in the HTML namespace (#1058)
diff --git a/spec/spec/elements/Element.js b/spec/spec/elements/Element.js
new file mode 100644 (file)
index 0000000..0d7fa55
--- /dev/null
@@ -0,0 +1,276 @@
+/* globals describe, expect, it, beforeEach, spyOn, jasmine, container */
+
+import { Element, create, Rect, G, SVG } from '../../../src/main.js'
+const { any, objectContaining } = jasmine
+
+describe('Element.js', function () {
+  let element
+
+  beforeEach(() => {
+    element = new Element(create('rect'))
+  })
+
+  describe('()', () => {
+    it('creates a new object of type Element', () => {
+      expect(element).toEqual(any(Element))
+    })
+
+    it('sets passed attributes on the element', () => {
+      expect(new Element(create('rect'), { id: 'foo' }).id()).toBe('foo')
+    })
+
+    it('references the instance on the passed node', () => {
+      expect(element.node.instance).toBe(element)
+    })
+
+    it('sets the dom property to an empty object', () => {
+      expect(element.dom).toEqual({})
+    })
+
+    it('hydrates the dom property with data found in the dom', () => {
+      element.dom = { foo: 'bar' }
+      element.writeDataToDom()
+      expect(new Element(element.node).dom).toEqual({ foo: 'bar' })
+    })
+
+    it('falls back to empty object when attribute is null', () => {
+      element.node.setAttribute('svgjs:data', 'null')
+      expect(new Element(element.node).dom).toEqual({})
+    })
+  })
+
+  describe('center()', () => {
+    it('calls cx and cy with passed parameters and returns itself', () => {
+      const spyCx = spyOn(element, 'cx').and.callThrough()
+      const spyCy = spyOn(element, 'cy').and.callThrough()
+      expect(element.center(1, 2)).toBe(element)
+      expect(spyCx).toHaveBeenCalledWith(1)
+      expect(spyCy).toHaveBeenCalledWith(2)
+    })
+  })
+
+  describe('cx()', () => {
+    it('gets the elements center along the x axis', () => {
+      element.attr({ x: 10, width: 100 })
+      expect(element.cx()).toBe(60)
+    })
+
+    it('centers the element along the x axis and returns itself', () => {
+      element.attr({ x: 10, width: 100 })
+      expect(element.cx(100)).toBe(element)
+      expect(element.attr('x')).toBe(50)
+    })
+  })
+
+  describe('cy()', () => {
+    it('gets the elements center along the y axis', () => {
+      element.attr({ y: 10, height: 100 })
+      expect(element.cy()).toBe(60)
+    })
+
+    it('centers the element along the y axis and returns itself', () => {
+      element.attr({ y: 10, height: 100 })
+      expect(element.cy(100)).toBe(element)
+      expect(element.attr('y')).toBe(50)
+    })
+  })
+
+  describe('defs()', () => {
+    it('returns null if detached', () => {
+      expect(new Rect().defs()).toBe(null)
+      expect(new G().put(new Rect()).defs()).toBe(null)
+    })
+
+    it('calls defs on root node', () => {
+      const canvas = SVG()
+      const rect = canvas.rect(100, 100)
+      const spy = spyOn(canvas, 'defs').and.callThrough()
+      expect(rect.defs()).toBe(canvas.defs())
+      expect(spy.calls.count()).toBe(2)
+    })
+  })
+
+  describe('dmove()', () => {
+    it('calls dx and dy with passed parameters and returns itself', () => {
+      const spyDx = spyOn(element, 'dx').and.callThrough()
+      const spyDy = spyOn(element, 'dy').and.callThrough()
+      expect(element.dmove(1, 2)).toBe(element)
+      expect(spyDx).toHaveBeenCalledWith(1)
+      expect(spyDy).toHaveBeenCalledWith(2)
+    })
+  })
+
+  describe('dx()', () => {
+    it('moves by zero by default', () => {
+      element.attr({ x: 10, width: 100 })
+      expect(element.dx().x()).toBe(10)
+    })
+
+    it('moves the element along the x axis relatively and returns itself', () => {
+      element.attr({ x: 10, width: 100 })
+      expect(element.dx(100)).toBe(element)
+      expect(element.attr('x')).toBe(110)
+    })
+  })
+
+  describe('dy()', () => {
+    it('moves by zero by default', () => {
+      element.attr({ y: 10, height: 100 })
+      expect(element.dy().y()).toBe(10)
+    })
+
+    it('moves the element along the x axis relatively and returns itself', () => {
+      element.attr({ y: 10, height: 100 })
+      expect(element.dy(100)).toBe(element)
+      expect(element.attr('y')).toBe(110)
+    })
+  })
+
+  describe('root()', () => {
+    it('returns the root of this element', () => {
+      const canvas = SVG()
+      const rect = canvas.rect()
+      expect(rect.root()).toBe(canvas)
+    })
+
+    it('returns null if element is detached', () => {
+      expect(new G().put(new Rect()).root()).toBe(null)
+    })
+  })
+
+  describe('getEventHolder()', () => {
+    it('returns itself', () => {
+      expect(element.getEventHolder()).toBe(element)
+    })
+  })
+
+  describe('height()', () => {
+    it('calls attr with height', () => {
+      const spy = spyOn(element, 'attr')
+      element.height(123)
+      expect(spy).toHaveBeenCalledWith('height', 123)
+    })
+  })
+
+  describe('move()', () => {
+    it('calls x and y with passed parameters and returns itself', () => {
+      const spyx = spyOn(element, 'x').and.callThrough()
+      const spyy = spyOn(element, 'y').and.callThrough()
+      expect(element.move(1, 2)).toBe(element)
+      expect(spyx).toHaveBeenCalledWith(1)
+      expect(spyy).toHaveBeenCalledWith(2)
+    })
+  })
+
+  describe('parents()', () => {
+    it('returns array of parents until the passed element or root svg', () => {
+      const canvas = SVG().addTo(container)
+      const group1 = canvas.group().addClass('test')
+      const group2 = group1.group()
+      const group3 = group2.group()
+      const rect = group3.rect(100, 100)
+
+      expect(rect.parents('.test')).toEqual([ group3, group2, group1 ])
+      expect(rect.parents(group2)).toEqual([ group3, group2 ])
+      expect(rect.parents(group1).length).toBe(3)
+      expect(rect.parents()).toEqual([ group3, group2, group1, canvas ])
+    })
+  })
+
+  describe('reference()', () => {
+    it('gets a referenced element from a given attribute', () => {
+      const canvas = SVG().addTo(container)
+      const rect = canvas.defs().rect(100, 100)
+      const use = canvas.use(rect)
+      const mark = canvas.marker(10, 10)
+      const path = canvas.path('M0 0 50 50').marker('end', mark)
+
+      expect(use.reference('href')).toBe(rect)
+      expect(path.reference('marker-end')).toBe(mark)
+      expect(rect.reference('width')).toBe(null)
+    })
+  })
+
+  describe('setData()', () => {
+    it('sets the given data to the dom property and returns itself', () => {
+      expect(element.setData({ foo: 'bar' })).toBe(element)
+      expect(element.dom).toEqual({ foo: 'bar' })
+    })
+  })
+
+  describe('size()', () => {
+    it('calls width and height with passed parameters and returns itself', () => {
+      const spyWidth = spyOn(element, 'width').and.callThrough()
+      const spyHeight = spyOn(element, 'height').and.callThrough()
+      expect(element.size(1, 2)).toBe(element)
+      expect(spyWidth).toHaveBeenCalledWith(objectContaining({ value: 1 }))
+      expect(spyHeight).toHaveBeenCalledWith(objectContaining({ value: 2 }))
+    })
+
+    it('changes height proportionally if null', () => {
+      const canvas = SVG().addTo(container)
+      const element = canvas.rect(100, 100)
+      const spyWidth = spyOn(element, 'width').and.callThrough()
+      const spyHeight = spyOn(element, 'height').and.callThrough()
+      expect(element.size(200, null)).toBe(element)
+      expect(spyWidth).toHaveBeenCalledWith(objectContaining({ value: 200 }))
+      expect(spyHeight).toHaveBeenCalledWith(objectContaining({ value: 200 }))
+    })
+
+    it('changes width proportionally if null', () => {
+      const canvas = SVG().addTo(container)
+      const element = canvas.rect(100, 100)
+      const spyWidth = spyOn(element, 'width').and.callThrough()
+      const spyHeight = spyOn(element, 'height').and.callThrough()
+      expect(element.size(null, 200)).toBe(element)
+      expect(spyWidth).toHaveBeenCalledWith(objectContaining({ value: 200 }))
+      expect(spyHeight).toHaveBeenCalledWith(objectContaining({ value: 200 }))
+    })
+  })
+
+  describe('width()', () => {
+    it('calls attr with width', () => {
+      const spy = spyOn(element, 'attr')
+      element.width(123)
+      expect(spy).toHaveBeenCalledWith('width', 123)
+    })
+  })
+
+  describe('writeDataToDom()', () => {
+    it('removes previously set data', () => {
+      element.node.setAttribute('svgjs:data', JSON.stringify({ foo: 'bar' }))
+      element.writeDataToDom()
+      expect(element.node.getAttribute('svgjs:data')).toBe(null)
+    })
+
+    it('writes data from the dom property into the dom', () => {
+      element.dom = { foo: 'bar' }
+      element.writeDataToDom()
+      expect(element.node.getAttribute('svgjs:data')).toBe(JSON.stringify({ foo: 'bar' }))
+    })
+
+    it('recursively calls writeDataToDom on all children', () => {
+      const g = new G()
+      const rect = g.rect(100, 100)
+      const spy = spyOn(rect, 'writeDataToDom')
+      g.writeDataToDom()
+      expect(spy).toHaveBeenCalled()
+    })
+  })
+
+  describe('x()', () => {
+    it('calls attr with x', () => {
+      const spy = spyOn(element, 'attr')
+      element.x(123)
+      expect(spy).toHaveBeenCalledWith('x', 123)
+    })
+  })
+
+  describe('y()', () => {
+    it('calls attr with y', () => {
+      const spy = spyOn(element, 'attr')
+      element.y(123)
+      expect(spy).toHaveBeenCalledWith('y', 123)
+    })
+  })
+})
index 9735304fb16a0a7355350e524c9afef1c0ccf909..34b1e200838d50338d1dddeba9fb870beb1d467f 100644 (file)
@@ -174,6 +174,15 @@ describe('Box.js', () => {
       })
     })
 
+    describe('inside()', () => {
+      it('checks if a point is in the elements borders', () => {
+        const canvas = SVG().addTo(container)
+        const rect = canvas.rect(100, 100)
+        expect(rect.inside(50, 50)).toBe(true)
+        expect(rect.inside(101, 101)).toBe(false)
+      })
+    })
+
     describe('viewbox()', () => {
       it('sets the viewbox of the element', () => {
         const canvas = viewbox.call(SVG().addTo(container), 10, 10, 200, 200)
index d75db17036d9e25d70668bae67ae5e24848949b0..bb1161156e814ac51c0a3a9405561b66079af732 100644 (file)
@@ -1,4 +1,4 @@
-import { bbox, rbox } from '../types/Box.js'
+import { bbox, rbox, inside } from '../types/Box.js'
 import { ctm, screenCTM } from '../types/Matrix.js'
 import {
   extend,
@@ -52,7 +52,8 @@ export default class Element extends Dom {
 
   // Get defs
   defs () {
-    return this.root().defs()
+    const root = this.root()
+    return root && root.defs()
   }
 
   // Relative move over x and y axes
@@ -85,16 +86,6 @@ export default class Element extends Dom {
     return this.attr('height', height)
   }
 
-  // Checks whether the given point inside the bounding box of the element
-  inside (x, y) {
-    const box = this.bbox()
-
-    return x > box.x
-      && y > box.y
-      && x < box.x + box.width
-      && y < box.y + box.height
-  }
-
   // Move element to given x and y values
   move (x, y) {
     return this.x(x).y(y)
@@ -126,7 +117,7 @@ export default class Element extends Dom {
     attr = this.attr(attr)
     if (!attr) return null
 
-    const m = attr.match(reference)
+    const m = (attr + '').match(reference)
     return m ? makeInstance(m[1]) : null
   }
 
@@ -174,7 +165,7 @@ export default class Element extends Dom {
 }
 
 extend(Element, {
-  bbox, rbox, point, ctm, screenCTM
+  bbox, rbox, inside, point, ctm, screenCTM
 })
 
 register(Element, 'Element')
index 6d7d8a98cde7f25cee9d9e859bc5239e5016a6c8..ae0f2cd3543036dc4b7e8a472285a4299c8d5e41 100644 (file)
@@ -148,6 +148,16 @@ export function rbox (el) {
   return box.addOffset()
 }
 
+// Checks whether the given point is inside the bounding box
+export function inside (x, y) {
+  const box = this.bbox()
+
+  return x > box.x
+    && y > box.y
+    && x < box.x + box.width
+    && y < box.y + box.height
+}
+
 registerMethods({
   viewbox: {
     viewbox (x, y, width, height) {