Jan2026 fixes (#1077)
* fix release script * fix svgcanvas edge cases * Update path-actions.js * add modern js * update deps * Update CHANGES.md
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import 'pathseg'
|
||||
import { NS } from '../../packages/svgcanvas/core/namespaces.js'
|
||||
import * as utilities from '../../packages/svgcanvas/core/utilities.js'
|
||||
import { convertPath as convertPathActions } from '../../packages/svgcanvas/core/path-actions.js'
|
||||
import * as pathModule from '../../packages/svgcanvas/core/path.js'
|
||||
import { Path, Segment } from '../../packages/svgcanvas/core/path-method.js'
|
||||
import { init as unitsInit } from '../../packages/svgcanvas/core/units.js'
|
||||
@@ -41,6 +42,12 @@ describe('path', function () {
|
||||
]
|
||||
}
|
||||
|
||||
it('Test svgedit.path.init exposes recalcRotatedPath', function () {
|
||||
const [mockPathContext] = getMockContexts()
|
||||
pathModule.init(mockPathContext)
|
||||
assert.equal(typeof mockPathContext.recalcRotatedPath, 'function')
|
||||
})
|
||||
|
||||
it('Test svgedit.path.replacePathSeg', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 L10,11 L20,21Z')
|
||||
@@ -137,6 +144,63 @@ describe('path', function () {
|
||||
assert.equal(path.pathSegList.getItem(1).y, 15)
|
||||
})
|
||||
|
||||
it('Test svgedit.path.Segment.move for quadratic curve', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 Q11,12 15,16')
|
||||
|
||||
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
||||
pathModule.init(mockPathContext)
|
||||
utilities.init(mockUtilitiesContext)
|
||||
const pathObj = new Path(path)
|
||||
|
||||
pathObj.segs[1].move(-3, 4)
|
||||
const seg = path.pathSegList.getItem(1)
|
||||
|
||||
assert.equal(seg.pathSegTypeAsLetter, 'Q')
|
||||
assert.equal(seg.x, 12)
|
||||
assert.equal(seg.y, 20)
|
||||
assert.equal(seg.x1, 11)
|
||||
assert.equal(seg.y1, 12)
|
||||
})
|
||||
|
||||
it('Test svgedit.path.Segment.move for smooth cubic curve', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 S13,14 15,16')
|
||||
|
||||
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
||||
pathModule.init(mockPathContext)
|
||||
utilities.init(mockUtilitiesContext)
|
||||
const pathObj = new Path(path)
|
||||
|
||||
pathObj.segs[1].move(5, -6)
|
||||
const seg = path.pathSegList.getItem(1)
|
||||
|
||||
assert.equal(seg.pathSegTypeAsLetter, 'S')
|
||||
assert.equal(seg.x, 20)
|
||||
assert.equal(seg.y, 10)
|
||||
assert.equal(seg.x2, 18)
|
||||
assert.equal(seg.y2, 8)
|
||||
})
|
||||
|
||||
it('Test moving start point moves next quadratic control point', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 Q10,0 20,0')
|
||||
|
||||
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
|
||||
pathModule.init(mockPathContext)
|
||||
utilities.init(mockUtilitiesContext)
|
||||
const pathObj = new Path(path)
|
||||
|
||||
pathObj.segs[0].move(5, 5)
|
||||
const seg = path.pathSegList.getItem(1)
|
||||
|
||||
assert.equal(seg.pathSegTypeAsLetter, 'Q')
|
||||
assert.equal(seg.x, 20)
|
||||
assert.equal(seg.y, 0)
|
||||
assert.equal(seg.x1, 15)
|
||||
assert.equal(seg.y1, 5)
|
||||
})
|
||||
|
||||
it('Test svgedit.path.Segment.moveCtrl', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 C11,12 13,14 15,16 Z')
|
||||
@@ -179,4 +243,269 @@ describe('path', function () {
|
||||
const rel = pathModule.convertPath(path, true)
|
||||
assert.equal(rel, 'm40,55l20,0l0,20')
|
||||
})
|
||||
|
||||
it('Test convertPath resets after closepath when relative', function () {
|
||||
unitsInit({
|
||||
getRoundDigits () { return 5 }
|
||||
})
|
||||
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M10,10 L20,10 Z L15,10')
|
||||
const expected = 'm10,10l10,0zl5,0'
|
||||
|
||||
assert.equal(pathModule.convertPath(path, true), expected)
|
||||
assert.equal(convertPathActions(path, true), expected)
|
||||
})
|
||||
|
||||
it('Test recalcRotatedPath preserves zero control points', function () {
|
||||
const svg = document.createElementNS(NS.SVG, 'svg')
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 C0,10 0,20 30,30')
|
||||
path.setAttribute('transform', 'rotate(45 0 0)')
|
||||
svg.append(path)
|
||||
|
||||
const [mockPathContext, mockUtilitiesContext] = getMockContexts(svg)
|
||||
pathModule.init(mockPathContext)
|
||||
utilities.init(mockUtilitiesContext)
|
||||
const pathObj = new Path(path)
|
||||
pathObj.oldbbox = utilities.getBBox(path)
|
||||
|
||||
pathModule.recalcRotatedPath()
|
||||
|
||||
const seg = path.pathSegList.getItem(1)
|
||||
assert.equal(seg.pathSegTypeAsLetter, 'C')
|
||||
assert.closeTo(seg.x1, 0, 1e-6)
|
||||
assert.closeTo(seg.y1, 10, 1e-6)
|
||||
assert.closeTo(seg.x2, 0, 1e-6)
|
||||
assert.closeTo(seg.y2, 20, 1e-6)
|
||||
assert.closeTo(seg.x, 30, 1e-6)
|
||||
assert.closeTo(seg.y, 30, 1e-6)
|
||||
})
|
||||
|
||||
it('Test convertPath handles relative arcs', function () {
|
||||
unitsInit({
|
||||
getRoundDigits () { return 5 }
|
||||
})
|
||||
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 a10,20 30 0 1 40,50')
|
||||
|
||||
const abs = pathModule.convertPath(path)
|
||||
assert.ok(abs.includes('A10,20 30 0 1 40,50'))
|
||||
|
||||
const rel = pathModule.convertPath(path, true)
|
||||
assert.ok(rel.includes('a10,20 30 0 1 40,50'))
|
||||
})
|
||||
|
||||
it('Test recalcRotatedPath with no current path', function () {
|
||||
const [mockPathContext] = getMockContexts()
|
||||
pathModule.init(mockPathContext)
|
||||
// path is null initially after init
|
||||
pathModule.recalcRotatedPath()
|
||||
// Should not throw
|
||||
})
|
||||
|
||||
it('Test recalcRotatedPath with path without rotation', function () {
|
||||
const svg = document.createElementNS(NS.SVG, 'svg')
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 L10,10')
|
||||
svg.append(path)
|
||||
|
||||
const [mockPathContext, mockUtilitiesContext] = getMockContexts(svg)
|
||||
pathModule.init(mockPathContext)
|
||||
utilities.init(mockUtilitiesContext)
|
||||
const pathObj = new Path(path)
|
||||
pathObj.oldbbox = utilities.getBBox(path)
|
||||
|
||||
pathModule.recalcRotatedPath()
|
||||
// Should not throw, and path should remain unchanged
|
||||
assert.equal(path.getAttribute('d'), 'M0,0 L10,10')
|
||||
})
|
||||
|
||||
it('Test recalcRotatedPath with path without oldbbox', function () {
|
||||
const svg = document.createElementNS(NS.SVG, 'svg')
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 L10,10')
|
||||
path.setAttribute('transform', 'rotate(45 0 0)')
|
||||
svg.append(path)
|
||||
|
||||
const [mockPathContext] = getMockContexts(svg)
|
||||
pathModule.init(mockPathContext)
|
||||
const pathObj = new Path(path)
|
||||
pathObj.oldbbox = null
|
||||
|
||||
pathModule.recalcRotatedPath()
|
||||
// Should not throw
|
||||
})
|
||||
|
||||
it('Test Segment class with various pathSegTypes', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 H10 V10 Z')
|
||||
|
||||
const seg1 = new Segment(0, path.pathSegList.getItem(0))
|
||||
assert.equal(seg1.index, 0)
|
||||
|
||||
const seg2 = new Segment(1, path.pathSegList.getItem(1))
|
||||
assert.equal(seg2.type, 12) // PATHSEG_LINETO_HORIZONTAL_ABS
|
||||
|
||||
const seg3 = new Segment(2, path.pathSegList.getItem(2))
|
||||
assert.equal(seg3.type, 14) // PATHSEG_LINETO_VERTICAL_ABS
|
||||
})
|
||||
|
||||
it('Test convertPath with smooth cubic curve', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 S10,10 20,20')
|
||||
|
||||
const result = pathModule.convertPath(path, false)
|
||||
assert.ok(result.includes('S'))
|
||||
})
|
||||
|
||||
it('Test convertPath with quadratic curve', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 Q10,10 20,20')
|
||||
|
||||
const result = pathModule.convertPath(path, false)
|
||||
assert.ok(result.includes('Q'))
|
||||
})
|
||||
|
||||
it('Test Path.update with no pathSegList', function () {
|
||||
const svg = document.createElementNS(NS.SVG, 'svg')
|
||||
const rect = document.createElementNS(NS.SVG, 'rect')
|
||||
svg.append(rect)
|
||||
|
||||
const [mockPathContext, mockUtilitiesContext] = getMockContexts(svg)
|
||||
utilities.init(mockUtilitiesContext)
|
||||
pathModule.init(mockPathContext)
|
||||
|
||||
try {
|
||||
const pathObj = new Path(rect)
|
||||
pathObj.update()
|
||||
} catch (e) {
|
||||
// Expected for non-path elements
|
||||
assert.ok(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('Test convertPath with smooth quadratic curve', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 Q10,10 20,0 T40,0')
|
||||
|
||||
const result = pathModule.convertPath(path, false)
|
||||
assert.ok(result.includes('T'))
|
||||
})
|
||||
|
||||
it('Test convertPath with mixed absolute and relative commands', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 L10,10 l5,5 L20,20')
|
||||
|
||||
const abs = pathModule.convertPath(path, false)
|
||||
assert.ok(abs.includes('L'))
|
||||
|
||||
const rel = pathModule.convertPath(path, true)
|
||||
assert.ok(rel.includes('l'))
|
||||
})
|
||||
|
||||
it('Test convertPath with horizontal and vertical lines', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 H10 V10 h5 v5')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test Segment with arc command', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 A10,10 0 0 1 20,20')
|
||||
|
||||
const seg = new Segment(1, path.pathSegList.getItem(1))
|
||||
assert.equal(seg.type, 10) // PATHSEG_ARC_ABS
|
||||
})
|
||||
|
||||
it('Test convertPath with quadratic bezier', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 Q10,10 20,0')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test convertPath with smooth quadratic', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 Q10,10 20,0 T30,0')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test convertPath with arc sweep flags', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 A10,10 0 1 0 20,20')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test convertPath with relative arc', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M10,10 a5,5 0 0 1 10,10')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test convertPath with close path', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 L10,0 L10,10 Z')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.includes('Z') || result.includes('z'))
|
||||
})
|
||||
|
||||
it('Test convertPath with mixed case commands', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 L10,10 l5,5 C20,20 25,25 30,20')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test Segment getItem', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 L10,10 L20,20')
|
||||
|
||||
const seg = new Segment(1, path.pathSegList.getItem(1))
|
||||
assert.ok(seg.type)
|
||||
})
|
||||
|
||||
it('Test convertPath with relative smooth cubic', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0,0 C10,10 20,10 30,0 s10,10 20,0')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test convertPath with negative coordinates', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M-10,-10 L-20,-20 L-30,-15')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test convertPath with decimal coordinates', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M0.5,0.5 L10.25,10.75')
|
||||
|
||||
const result = pathModule.convertPath(path)
|
||||
assert.ok(result.length > 0)
|
||||
})
|
||||
|
||||
it('Test Segment with move command', function () {
|
||||
const path = document.createElementNS(NS.SVG, 'path')
|
||||
path.setAttribute('d', 'M10,10 L20,20')
|
||||
|
||||
const seg = new Segment(0, path.pathSegList.getItem(0))
|
||||
assert.equal(seg.type, 2) // PATHSEG_MOVETO_ABS
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user