* fix release script * fix svgcanvas edge cases * Update path-actions.js * add modern js * update deps * Update CHANGES.md
571 lines
19 KiB
JavaScript
571 lines
19 KiB
JavaScript
import { describe, expect, it } from 'vitest'
|
|
import Paint from '../../packages/svgcanvas/core/paint.js'
|
|
|
|
const createLinear = (id) => {
|
|
const grad = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient')
|
|
if (id) grad.id = id
|
|
return grad
|
|
}
|
|
|
|
const createRadial = (id) => {
|
|
const grad = document.createElementNS('http://www.w3.org/2000/svg', 'radialGradient')
|
|
if (id) grad.id = id
|
|
grad.setAttribute('cx', '0.5')
|
|
grad.setAttribute('cy', '0.5')
|
|
return grad
|
|
}
|
|
|
|
describe('Paint', () => {
|
|
it('defaults to an empty paint when no options are provided', () => {
|
|
const paint = new Paint()
|
|
expect(paint.type).toBe('none')
|
|
expect(paint.alpha).toBe(100)
|
|
expect(paint.solidColor).toBeNull()
|
|
expect(paint.linearGradient).toBeNull()
|
|
expect(paint.radialGradient).toBeNull()
|
|
})
|
|
|
|
it('normalizes solid colors and copies alpha', () => {
|
|
const base = new Paint({ solidColor: '#00ff00', alpha: 65 })
|
|
const copy = new Paint({ copy: base })
|
|
|
|
expect(copy.type).toBe('solidColor')
|
|
expect(copy.alpha).toBe(65)
|
|
expect(copy.solidColor).toBe('00ff00')
|
|
expect(copy.linearGradient).toBeNull()
|
|
expect(copy.radialGradient).toBeNull()
|
|
})
|
|
|
|
it('copies gradients by cloning the underlying nodes', () => {
|
|
const linear = createLinear('lin1')
|
|
const base = new Paint({ linearGradient: linear })
|
|
const clone = new Paint({ copy: base })
|
|
|
|
expect(clone.type).toBe('linearGradient')
|
|
expect(clone.linearGradient).not.toBe(base.linearGradient)
|
|
expect(clone.linearGradient?.isEqualNode(base.linearGradient)).toBe(true)
|
|
})
|
|
|
|
it('resolves linked linear gradients via href/xlink:href', () => {
|
|
const referenced = createLinear('refGrad')
|
|
referenced.setAttribute('gradientUnits', 'userSpaceOnUse')
|
|
const stop0 = document.createElementNS('http://www.w3.org/2000/svg', 'stop')
|
|
stop0.setAttribute('offset', '0')
|
|
stop0.setAttribute('stop-color', '#000000')
|
|
stop0.setAttribute('stop-opacity', '1')
|
|
const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop')
|
|
stop1.setAttribute('offset', '1')
|
|
stop1.setAttribute('stop-color', '#ffffff')
|
|
stop1.setAttribute('stop-opacity', '1')
|
|
referenced.append(stop0, stop1)
|
|
document.body.append(referenced)
|
|
const referencing = createLinear('linkGrad')
|
|
referencing.setAttribute('xlink:href', '#refGrad')
|
|
referencing.setAttribute('x2', '0.5')
|
|
|
|
const paint = new Paint({ linearGradient: referencing })
|
|
expect(paint.type).toBe('linearGradient')
|
|
expect(paint.linearGradient).not.toBeNull()
|
|
expect(paint.linearGradient?.getAttribute('gradientUnits')).toBe('userSpaceOnUse')
|
|
expect(paint.linearGradient?.getAttribute('x2')).toBe('0.5')
|
|
expect(paint.linearGradient?.querySelectorAll('stop')).toHaveLength(2)
|
|
expect(paint.linearGradient?.hasAttribute('xlink:href')).toBe(false)
|
|
})
|
|
|
|
it('creates radial gradients from provided element when no href is set', () => {
|
|
const radial = createRadial('rad1')
|
|
const paint = new Paint({ radialGradient: radial })
|
|
|
|
expect(paint.type).toBe('radialGradient')
|
|
expect(paint.radialGradient).not.toBe(radial)
|
|
expect(paint.radialGradient?.id).toBe('rad1')
|
|
expect(paint.linearGradient).toBeNull()
|
|
})
|
|
|
|
it('resolves multi-level gradient chains and strips href', () => {
|
|
const base = createLinear('baseGrad')
|
|
base.setAttribute('gradientUnits', 'userSpaceOnUse')
|
|
base.setAttribute('y2', '0.75')
|
|
const baseStop = document.createElementNS('http://www.w3.org/2000/svg', 'stop')
|
|
baseStop.setAttribute('offset', '0')
|
|
baseStop.setAttribute('stop-color', '#111111')
|
|
base.append(baseStop)
|
|
|
|
const mid = createLinear('midGrad')
|
|
mid.setAttribute('href', '#baseGrad')
|
|
mid.setAttribute('x1', '0.2')
|
|
document.body.append(base, mid)
|
|
|
|
const top = createLinear('topGrad')
|
|
top.setAttribute('xlink:href', '#midGrad')
|
|
top.setAttribute('x2', '0.9')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.getAttribute('x2')).toBe('0.9')
|
|
expect(paint.linearGradient?.getAttribute('x1')).toBe('0.2')
|
|
expect(paint.linearGradient?.getAttribute('y2')).toBe('0.75')
|
|
expect(paint.linearGradient?.getAttribute('gradientUnits')).toBe('userSpaceOnUse')
|
|
expect(paint.linearGradient?.querySelectorAll('stop')).toHaveLength(1)
|
|
expect(paint.linearGradient?.hasAttribute('href')).toBe(false)
|
|
expect(paint.linearGradient?.hasAttribute('xlink:href')).toBe(false)
|
|
|
|
base.remove()
|
|
mid.remove()
|
|
})
|
|
|
|
it('should handle paint with null linearGradient', () => {
|
|
const paint = new Paint({ linearGradient: null })
|
|
expect(paint.type).toBe('none')
|
|
expect(paint.linearGradient).toBe(null)
|
|
})
|
|
|
|
it('should handle paint with undefined radialGradient', () => {
|
|
const paint = new Paint({ radialGradient: undefined })
|
|
expect(paint.type).toBe('none')
|
|
})
|
|
|
|
it('should handle paint with solidColor', () => {
|
|
const paint = new Paint({ solidColor: '#ff0000' })
|
|
expect(paint.type).toBe('solidColor')
|
|
})
|
|
|
|
it('should handle paint with alpha value', () => {
|
|
const paint = new Paint({ alpha: 0.5 })
|
|
expect(paint.alpha).toBe(0.5)
|
|
})
|
|
|
|
it('should handle radialGradient with href chain', () => {
|
|
const base = createRadial('baseRadialGrad')
|
|
base.setAttribute('cx', '0.5')
|
|
base.setAttribute('cy', '0.5')
|
|
base.setAttribute('r', '0.5')
|
|
document.body.append(base)
|
|
|
|
const top = createRadial('topRadialGrad')
|
|
top.setAttribute('href', '#baseRadialGrad')
|
|
top.setAttribute('fx', '0.3')
|
|
|
|
const paint = new Paint({ radialGradient: top })
|
|
expect(paint.radialGradient?.getAttribute('fx')).toBe('0.3')
|
|
expect(paint.radialGradient?.getAttribute('cx')).toBe('0.5')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle linearGradient with no stops', () => {
|
|
const grad = createLinear('noStopsGrad')
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.querySelectorAll('stop')).toHaveLength(0)
|
|
})
|
|
|
|
it('should copy paint object with type none', () => {
|
|
const original = new Paint({})
|
|
const copy = new Paint({ copy: original })
|
|
expect(copy.type).toBe('none')
|
|
expect(copy.solidColor).toBe(null)
|
|
})
|
|
|
|
it('should copy paint object with solidColor', () => {
|
|
const original = new Paint({ solidColor: '#ff0000' })
|
|
const copy = new Paint({ copy: original, alpha: 75 })
|
|
expect(copy.type).toBe('solidColor')
|
|
expect(copy.solidColor).toBe('ff0000')
|
|
expect(copy.alpha).toBe(original.alpha)
|
|
})
|
|
|
|
it('should copy paint object with linearGradient', () => {
|
|
const grad = createLinear('copyLinearGrad')
|
|
const original = new Paint({ linearGradient: grad })
|
|
const copy = new Paint({ copy: original })
|
|
expect(copy.type).toBe('linearGradient')
|
|
expect(copy.linearGradient).not.toBe(original.linearGradient)
|
|
expect(copy.linearGradient?.id).toBe('copyLinearGrad')
|
|
})
|
|
|
|
it('should copy paint object with radialGradient', () => {
|
|
const grad = createRadial('copyRadialGrad')
|
|
document.body.append(grad)
|
|
const original = new Paint({ radialGradient: grad })
|
|
const copy = new Paint({ copy: original })
|
|
expect(copy.type).toBe('radialGradient')
|
|
expect(copy.radialGradient).not.toBe(original.radialGradient)
|
|
expect(copy.radialGradient?.id).toBe('copyRadialGrad')
|
|
grad.remove()
|
|
})
|
|
|
|
it('should handle gradient with invalid href reference', () => {
|
|
const grad = createLinear('invalidHrefGrad')
|
|
grad.setAttribute('href', '#nonExistentGradient')
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('invalidHrefGrad')
|
|
})
|
|
|
|
it('should normalize alpha values correctly', () => {
|
|
const paint1 = new Paint({ alpha: 150 })
|
|
expect(paint1.alpha).toBe(100)
|
|
const paint2 = new Paint({ alpha: -10 })
|
|
expect(paint2.alpha).toBe(0)
|
|
const paint3 = new Paint({ alpha: 'invalid' })
|
|
expect(paint3.alpha).toBe(100)
|
|
})
|
|
|
|
it('should handle solidColor with none value', () => {
|
|
const paint = new Paint({ solidColor: 'none' })
|
|
expect(paint.type).toBe('solidColor')
|
|
expect(paint.solidColor).toBe('none')
|
|
})
|
|
|
|
it('should normalize solidColor without hash', () => {
|
|
const paint = new Paint({ solidColor: 'red' })
|
|
expect(paint.type).toBe('solidColor')
|
|
expect(paint.solidColor).toBe('red')
|
|
})
|
|
|
|
it('should handle linearGradient with url() format in href', () => {
|
|
const base = createLinear('baseUrlGrad')
|
|
base.setAttribute('x1', '0')
|
|
base.setAttribute('x2', '1')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topUrlGrad')
|
|
top.setAttribute('href', 'url(#baseUrlGrad)')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.getAttribute('x1')).toBe('0')
|
|
expect(paint.linearGradient?.getAttribute('x2')).toBe('1')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle gradient with empty string attributes', () => {
|
|
const base = createLinear('baseEmptyGrad')
|
|
base.setAttribute('x1', '0.5')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topEmptyGrad')
|
|
top.setAttribute('href', '#baseEmptyGrad')
|
|
top.setAttribute('x1', '')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
// Empty attribute should be replaced by inherited value
|
|
expect(paint.linearGradient?.getAttribute('x1')).toBe('0.5')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle gradient with stops inheritance', () => {
|
|
const base = createLinear('baseStopsGrad')
|
|
const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop')
|
|
stop1.setAttribute('offset', '0')
|
|
const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop')
|
|
stop2.setAttribute('offset', '1')
|
|
base.append(stop1, stop2)
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topNoStopsGrad')
|
|
top.setAttribute('href', '#baseStopsGrad')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.querySelectorAll('stop')).toHaveLength(2)
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle mismatched gradient types', () => {
|
|
const base = createLinear('baseMismatchGrad')
|
|
document.body.append(base)
|
|
|
|
const top = createRadial('topMismatchGrad')
|
|
top.setAttribute('href', '#baseMismatchGrad')
|
|
|
|
const paint = new Paint({ radialGradient: top })
|
|
// Should not inherit from mismatched type
|
|
expect(paint.radialGradient?.id).toBe('topMismatchGrad')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle circular gradient references', () => {
|
|
const grad1 = createLinear('circularGrad1')
|
|
grad1.setAttribute('href', '#circularGrad2')
|
|
document.body.append(grad1)
|
|
|
|
const grad2 = createLinear('circularGrad2')
|
|
grad2.setAttribute('href', '#circularGrad1')
|
|
document.body.append(grad2)
|
|
|
|
const paint = new Paint({ linearGradient: grad1 })
|
|
// Should handle circular reference without infinite loop
|
|
expect(paint.linearGradient?.id).toBe('circularGrad1')
|
|
|
|
grad1.remove()
|
|
grad2.remove()
|
|
})
|
|
|
|
it('should normalize alpha with null value', () => {
|
|
const paint = new Paint({ alpha: null })
|
|
expect(paint.alpha).toBe(0)
|
|
})
|
|
|
|
it('should normalize alpha with undefined', () => {
|
|
const paint = new Paint({ alpha: undefined })
|
|
expect(paint.alpha).toBe(100)
|
|
})
|
|
|
|
it('should normalize solidColor with empty string', () => {
|
|
const paint = new Paint({ solidColor: '' })
|
|
expect(paint.type).toBe('none')
|
|
expect(paint.solidColor).toBe(null)
|
|
})
|
|
|
|
it('should normalize solidColor with whitespace', () => {
|
|
const paint = new Paint({ solidColor: ' ' })
|
|
expect(paint.type).toBe('solidColor')
|
|
expect(paint.solidColor).toBe(null)
|
|
})
|
|
|
|
it('should handle extractHrefId with path in URL', () => {
|
|
const grad = createLinear('pathGrad')
|
|
grad.setAttribute('href', 'file.svg#targetGrad')
|
|
document.body.append(grad)
|
|
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('pathGrad')
|
|
|
|
grad.remove()
|
|
})
|
|
|
|
it('should handle gradient without ownerDocument', () => {
|
|
const grad = createLinear('noDocGrad')
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('noDocGrad')
|
|
})
|
|
|
|
it('should copy paint with null linearGradient', () => {
|
|
const original = new Paint({ linearGradient: null })
|
|
const copy = new Paint({ copy: original })
|
|
expect(copy.type).toBe('none')
|
|
expect(copy.linearGradient).toBe(null)
|
|
})
|
|
|
|
it('should handle href with double quotes in url()', () => {
|
|
const base = createLinear('doubleQuoteGrad')
|
|
base.setAttribute('x1', '0.25')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topDoubleQuoteGrad')
|
|
top.setAttribute('href', 'url("#doubleQuoteGrad")')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.getAttribute('x1')).toBe('0.25')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle href with single quotes in url()', () => {
|
|
const base = createLinear('singleQuoteGrad')
|
|
base.setAttribute('y1', '0.75')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topSingleQuoteGrad')
|
|
top.setAttribute('href', "url('#singleQuoteGrad')")
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.getAttribute('y1')).toBe('0.75')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle gradient with non-matching tagName case', () => {
|
|
const base = createLinear('baseCaseGrad')
|
|
document.body.append(base)
|
|
|
|
const top = createRadial('topCaseGrad')
|
|
top.setAttribute('href', '#baseCaseGrad')
|
|
|
|
const paint = new Paint({ radialGradient: top })
|
|
// Should not inherit from wrong gradient type
|
|
expect(paint.radialGradient?.id).toBe('topCaseGrad')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle gradient href with just hash', () => {
|
|
const base = createLinear('hashOnlyGrad')
|
|
base.setAttribute('x2', '1')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topHashGrad')
|
|
top.setAttribute('href', '#hashOnlyGrad')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.getAttribute('x2')).toBe('1')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle invalid alpha values', () => {
|
|
const paint1 = new Paint({ alpha: NaN })
|
|
expect(paint1.alpha).toBe(100)
|
|
|
|
const paint2 = new Paint({ alpha: Infinity })
|
|
expect(paint2.alpha).toBe(100)
|
|
|
|
const paint3 = new Paint({ alpha: -Infinity })
|
|
expect(paint3.alpha).toBe(100)
|
|
})
|
|
|
|
it('should handle copy with missing clone method', () => {
|
|
const original = new Paint({ linearGradient: createLinear('copyGrad') })
|
|
original.linearGradient = { id: 'fake', cloneNode: null }
|
|
const copy = new Paint({ copy: original })
|
|
expect(copy.linearGradient).toBe(null)
|
|
})
|
|
|
|
it('should handle alpha at exact boundaries', () => {
|
|
const paint1 = new Paint({ alpha: 0 })
|
|
expect(paint1.alpha).toBe(0)
|
|
|
|
const paint2 = new Paint({ alpha: 100 })
|
|
expect(paint2.alpha).toBe(100)
|
|
|
|
const paint3 = new Paint({ alpha: 50 })
|
|
expect(paint3.alpha).toBe(50)
|
|
})
|
|
|
|
it('should handle gradient with null getAttribute', () => {
|
|
const grad = createLinear('nullAttrGrad')
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('nullAttrGrad')
|
|
})
|
|
|
|
it('should handle referenced gradient with no attributes', () => {
|
|
const base = createLinear('emptyAttrGrad')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topEmptyAttrGrad')
|
|
top.setAttribute('href', '#emptyAttrGrad')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.id).toBe('topEmptyAttrGrad')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle href with spaces in url()', () => {
|
|
const base = createLinear('spacesGrad')
|
|
base.setAttribute('gradientUnits', 'userSpaceOnUse')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topSpacesGrad')
|
|
top.setAttribute('href', 'url( #spacesGrad )')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.getAttribute('gradientUnits')).toBe('userSpaceOnUse')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle solidColor with hash prefix', () => {
|
|
const paint = new Paint({ solidColor: '#ff0000' })
|
|
expect(paint.type).toBe('solidColor')
|
|
expect(paint.solidColor).toBe('ff0000')
|
|
})
|
|
|
|
it('should handle solidColor without hash prefix', () => {
|
|
const paint = new Paint({ solidColor: 'blue' })
|
|
expect(paint.type).toBe('solidColor')
|
|
expect(paint.solidColor).toBe('blue')
|
|
})
|
|
|
|
it('should handle gradient with id attribute skip', () => {
|
|
const base = createLinear('idTestGrad')
|
|
base.setAttribute('x1', '0.1')
|
|
base.setAttribute('id', 'differentId')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topIdTestGrad')
|
|
top.setAttribute('href', '#idTestGrad')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
// Should not copy id attribute
|
|
expect(paint.linearGradient?.id).not.toBe('differentId')
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle gradient with xlink:href attribute skip', () => {
|
|
const base = createLinear('xlinkTestGrad')
|
|
base.setAttribute('y1', '0.2')
|
|
document.body.append(base)
|
|
|
|
const top = createLinear('topXlinkTestGrad')
|
|
top.setAttribute('xlink:href', '#xlinkTestGrad')
|
|
|
|
const paint = new Paint({ linearGradient: top })
|
|
expect(paint.linearGradient?.getAttribute('y1')).toBe('0.2')
|
|
// xlink:href should be removed
|
|
expect(paint.linearGradient?.hasAttribute('xlink:href')).toBe(false)
|
|
|
|
base.remove()
|
|
})
|
|
|
|
it('should handle href pointing to path with hash', () => {
|
|
const grad = createLinear('pathHashGrad')
|
|
grad.setAttribute('href', 'images/file.svg#someGrad')
|
|
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('pathHashGrad')
|
|
})
|
|
|
|
it('should handle href ending with just hash', () => {
|
|
const grad = createLinear('trailingHashGrad')
|
|
grad.setAttribute('href', 'file.svg#')
|
|
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('trailingHashGrad')
|
|
})
|
|
|
|
it('should handle href with no hash', () => {
|
|
const grad = createLinear('noHashGrad')
|
|
grad.setAttribute('href', 'file.svg')
|
|
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('noHashGrad')
|
|
})
|
|
|
|
it('should handle empty href attribute', () => {
|
|
const grad = createLinear('emptyHrefGrad')
|
|
grad.setAttribute('href', '')
|
|
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('emptyHrefGrad')
|
|
})
|
|
|
|
it('should handle gradient with null ownerDocument fallback', () => {
|
|
const grad = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient')
|
|
grad.setAttribute('id', 'nullDocGrad2')
|
|
// Don't append to document
|
|
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('nullDocGrad2')
|
|
})
|
|
|
|
it('should handle radialGradient with xlink:href', () => {
|
|
const grad = createRadial('xlinkRadial')
|
|
grad.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#baseRadial')
|
|
|
|
const paint = new Paint({ radialGradient: grad })
|
|
expect(paint.radialGradient?.id).toBe('xlinkRadial')
|
|
})
|
|
|
|
it('should handle gradient with both href and xlink:href', () => {
|
|
const grad = createLinear('dualHref')
|
|
grad.setAttribute('href', '#newer')
|
|
grad.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#older')
|
|
|
|
const paint = new Paint({ linearGradient: grad })
|
|
expect(paint.linearGradient?.id).toBe('dualHref')
|
|
})
|
|
})
|