Files
svgedit/tests/e2e/unit/svgcore-recalculate-extra.spec.js
JFH fa380402e1 increase test coverage
extend test coverage
2025-12-07 10:56:29 +01:00

308 lines
10 KiB
JavaScript

import { test, expect } from '../fixtures.js'
test.describe('SVG core recalculate extra cases', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/tests/unit-harness.html')
await page.waitForFunction(() => Boolean(window.svgHarness))
})
test('scales elements and flips gradients/matrices', async ({ page }) => {
const result = await page.evaluate(() => {
const { utilities, coords, recalculate } = window.svgHarness
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')
const grad = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient')
grad.id = 'grad1'
grad.setAttribute('x1', '0')
grad.setAttribute('x2', '1')
grad.setAttribute('y1', '0')
grad.setAttribute('y2', '0')
defs.append(grad)
svg.append(defs)
document.body.append(svg)
const dataStorage = {
store: new WeakMap(),
put (el, key, value) {
if (!this.store.has(el)) this.store.set(el, new Map())
this.store.get(el).set(key, value)
},
get (el, key) {
return this.store.get(el)?.get(key)
},
has (el, key) {
return this.store.has(el) && this.store.get(el).has(key)
},
remove (el, key) {
const bucket = this.store.get(el)
if (!bucket) return false
const deleted = bucket.delete(key)
if (!bucket.size) this.store.delete(el)
return deleted
}
}
const canvasStub = {
getSvgRoot: () => svg,
getStartTransform: () => '',
setStartTransform: () => {},
getDataStorage: () => dataStorage,
getCurrentDrawing: () => ({ getNextId: () => 'g1' })
}
utilities.init({
getSvgRoot: () => svg,
getDOMDocument: () => document,
getDOMContainer: () => svg,
getDataStorage: () => dataStorage
})
coords.init({
getGridSnapping: () => false,
getDrawing: () => ({ getNextId: () => 'id2' }),
getDataStorage: () => dataStorage
})
recalculate.init(canvasStub)
// Scale about center via translate/scale/translate sequence
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
rect.setAttribute('x', '5')
rect.setAttribute('y', '6')
rect.setAttribute('width', '10')
rect.setAttribute('height', '8')
rect.setAttribute('transform', 'translate(5 5) scale(2 3) translate(-5 -5)')
svg.append(rect)
// Flip with gradient fill using matrix
const rectFlip = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
rectFlip.setAttribute('x', '0')
rectFlip.setAttribute('y', '0')
rectFlip.setAttribute('width', '4')
rectFlip.setAttribute('height', '4')
rectFlip.setAttribute('fill', 'url(#grad1)')
rectFlip.setAttribute('transform', 'matrix(-1 0 0 1 0 0)')
svg.append(rectFlip)
recalculate.recalculateDimensions(rect)
recalculate.recalculateDimensions(rectFlip)
return {
rect: {
width: rect.getAttribute('width'),
height: rect.getAttribute('height'),
transformRemoved: rect.hasAttribute('transform')
},
flip: {
width: rectFlip.getAttribute('width'),
height: rectFlip.getAttribute('height'),
fill: rectFlip.getAttribute('fill')
}
}
})
expect(Number(result.rect.width)).toBeGreaterThan(10)
expect(Number(result.rect.height)).toBeGreaterThan(8)
expect(result.rect.transformRemoved).toBe(false) // scaling keeps transform list
expect(result.flip.fill.startsWith('url(')).toBe(true)
})
test('recalculateDimensions reapplies rotations and updates clip paths', async ({ page }) => {
const result = await page.evaluate(() => {
const NS = 'http://www.w3.org/2000/svg'
const { utilities, coords, recalculate } = window.svgHarness
const svg = document.createElementNS(NS, 'svg')
const defs = document.createElementNS(NS, 'defs')
svg.append(defs)
document.body.append(svg)
const dataStorage = {
store: new WeakMap(),
put (el, key, value) {
if (!this.store.has(el)) this.store.set(el, new Map())
this.store.get(el).set(key, value)
},
get (el, key) {
return this.store.get(el)?.get(key)
},
has (el, key) {
return this.store.has(el) && this.store.get(el).has(key)
}
}
const drawing = {
next: 0,
getNextId () {
this.next += 1
return 'd' + this.next
}
}
const canvasStub = {
getSvgRoot: () => svg,
getSvgContent: () => svg,
getDOMDocument: () => document,
getDOMContainer: () => svg,
getDataStorage: () => dataStorage,
getStartTransform: () => '',
setStartTransform: () => {},
getCurrentDrawing: () => drawing
}
utilities.init(canvasStub)
coords.init({
getGridSnapping: () => false,
getDrawing: () => drawing,
getDataStorage: () => dataStorage,
getCurrentDrawing: () => drawing,
getSvgRoot: () => svg
})
recalculate.init(canvasStub)
const rect = document.createElementNS(NS, 'rect')
rect.setAttribute('x', '0')
rect.setAttribute('y', '0')
rect.setAttribute('width', '10')
rect.setAttribute('height', '10')
rect.setAttribute('transform', 'translate(10 5) rotate(30)')
svg.append(rect)
const clipPath = document.createElementNS(NS, 'clipPath')
clipPath.id = 'clip1'
const clipRect = document.createElementNS(NS, 'rect')
clipRect.setAttribute('x', '0')
clipRect.setAttribute('y', '0')
clipRect.setAttribute('width', '4')
clipRect.setAttribute('height', '4')
clipPath.append(clipRect)
defs.append(clipPath)
const cmd = recalculate.recalculateDimensions(rect)
recalculate.updateClipPath('url(#clip1)', 3, -2)
return {
rect: {
x: rect.getAttribute('x'),
y: rect.getAttribute('y'),
transform: rect.getAttribute('transform'),
hasCommand: Boolean(cmd)
},
clip: {
x: clipRect.getAttribute('x'),
y: clipRect.getAttribute('y'),
transforms: clipRect.transform.baseVal.numberOfItems
}
}
})
expect(result.rect.x).toBe('10')
expect(result.rect.y).toBe('5')
expect(result.rect.transform).toContain('rotate(')
expect(result.rect.transform).not.toContain('translate')
expect(result.rect.hasCommand).toBe(true)
expect(result.clip.x).toBe('3')
expect(result.clip.y).toBe('-2')
expect(result.clip.transforms).toBe(0)
})
test('recalculateDimensions remaps polygons and matrix transforms', async ({ page }) => {
const result = await page.evaluate(() => {
const NS = 'http://www.w3.org/2000/svg'
const { utilities, coords, recalculate } = window.svgHarness
const svg = document.createElementNS(NS, 'svg')
document.body.append(svg)
const dataStorage = {
store: new WeakMap(),
put (el, key, value) {
if (!this.store.has(el)) this.store.set(el, new Map())
this.store.get(el).set(key, value)
},
get (el, key) {
return this.store.get(el)?.get(key)
},
has (el, key) {
return this.store.has(el) && this.store.get(el).has(key)
}
}
const drawing = {
next: 0,
getNextId () {
this.next += 1
return 'p' + this.next
}
}
const canvasStub = {
getSvgRoot: () => svg,
getSvgContent: () => svg,
getDOMDocument: () => document,
getDOMContainer: () => svg,
getDataStorage: () => dataStorage,
getStartTransform: () => '',
setStartTransform: () => {},
getCurrentDrawing: () => drawing
}
utilities.init(canvasStub)
coords.init({
getGridSnapping: () => false,
getDrawing: () => drawing,
getDataStorage: () => dataStorage,
getCurrentDrawing: () => drawing,
getSvgRoot: () => svg
})
recalculate.init(canvasStub)
const poly = document.createElementNS(NS, 'polygon')
poly.setAttribute('points', '0,0 10,0 10,10')
poly.setAttribute('transform', 'translate(5 5) scale(-1 2) translate(-5 -5)')
svg.append(poly)
const path = document.createElementNS(NS, 'path')
path.setAttribute('d', 'M0 0 L1 0 L1 1 z')
path.setAttribute('transform', 'matrix(1 0 0 1 7 8)')
svg.append(path)
const rect = document.createElementNS(NS, 'rect')
rect.setAttribute('x', '1')
rect.setAttribute('y', '2')
rect.setAttribute('width', '5')
rect.setAttribute('height', '4')
rect.setAttribute('transform', 'matrix(1 0 0 1 7 8)')
svg.append(rect)
const useElem = document.createElementNS(NS, 'use')
useElem.setAttribute('href', '#missing')
useElem.setAttribute('transform', 'translate(3 4)')
svg.append(useElem)
const cmdPoly = recalculate.recalculateDimensions(poly)
const cmdPath = recalculate.recalculateDimensions(path)
recalculate.recalculateDimensions(rect)
const cmdUse = recalculate.recalculateDimensions(useElem)
return {
poly: {
points: poly.getAttribute('points'),
hasTransform: poly.hasAttribute('transform'),
hasCommand: Boolean(cmdPoly)
},
path: {
d: path.getAttribute('d'),
transform: path.getAttribute('transform') || '',
hasCommand: Boolean(cmdPath)
},
rect: {
x: rect.getAttribute('x'),
transform: rect.getAttribute('transform')
},
useResult: cmdUse
}
})
expect(result.poly.hasTransform).toBe(false)
expect(result.poly.points).toContain('-5')
expect(result.poly.hasCommand).toBe(true)
expect(result.path.d.startsWith('M7,8')).toBe(true)
expect(result.path.transform).toBe('')
expect(result.path.hasCommand).toBe(true)
expect(result.rect.x).toBe('1')
expect(result.rect.transform).toContain('matrix')
expect(result.useResult).toBeNull()
})
})