1
0
Fork 0

chore(core): More unit tests

This commit is contained in:
Joost De Cock 2022-09-19 18:04:47 +02:00
parent 269b1a36f1
commit d5eb2946d3
13 changed files with 662 additions and 809 deletions

View file

@ -34,4 +34,5 @@ export const __loadPatternDefaults = () => ({
debug: false,
options: {},
absoluteOptions: {},
measurements: {}
})

View file

@ -95,7 +95,6 @@ Part.prototype.setHidden = function (hidden = false) {
return this
}
/** Returns an object with shorthand access for pattern design */
/**
* Returns an object that will be passed to draft method to be destructured
*
@ -152,13 +151,13 @@ Part.prototype.shorthand = function () {
shorthand.Snippet.prototype = Object.create(Snippet.prototype)
// Proxy points, paths, snippets, measurements, options, and absoluteOptions
shorthand.points = new Proxy(this.points || {}, pointsProxy(self.points, self.context.store.log))
shorthand.paths = new Proxy(this.paths || {}, pathsProxy(self.paths, self.context.store.log))
shorthand.points = new Proxy(this.points, pointsProxy(self.points, self.context.store.log))
shorthand.paths = new Proxy(this.paths, pathsProxy(self.paths, self.context.store.log))
shorthand.snippets = new Proxy(
this.snippets || {},
this.snippets,
snippetsProxy(self.snippets, self.context.store.log)
)
shorthand.measurements = new Proxy(this.context.settings.measurements || {}, {
shorthand.measurements = new Proxy(this.context.settings.measurements, {
get: function (measurements, name) {
if (typeof measurements[name] === 'undefined')
self.context.store.log.warning(
@ -168,7 +167,7 @@ Part.prototype.shorthand = function () {
},
set: (measurements, name, value) => (self.context.settings.measurements[name] = value),
})
shorthand.options = new Proxy(this.context.settings.options || {}, {
shorthand.options = new Proxy(this.context.settings.options, {
get: function (options, name) {
if (typeof options[name] === 'undefined')
self.context.store.log.warning(
@ -178,7 +177,7 @@ Part.prototype.shorthand = function () {
},
set: (options, name, value) => (self.context.settings.options[name] = value),
})
shorthand.absoluteOptions = new Proxy(this.context.settings.absoluteOptions || {}, {
shorthand.absoluteOptions = new Proxy(this.context.settings.absoluteOptions, {
get: function (absoluteOptions, name) {
if (typeof absoluteOptions[name] === 'undefined')
self.context.store.log.warning(
@ -239,9 +238,6 @@ Part.prototype.__boundary = function () {
}
} catch (err) {
this.context.store.log.error(`Could not calculate boundary of \`paths.${key}\``)
this.context.store.log.debug(
`Since \`paths.${key}\` has no boundary, neither does \`parts.${this.name}\`. Ejecting part`
)
return false
}
}

View file

@ -642,7 +642,7 @@ Path.prototype.shiftAlong = function (distance, stepsPerMm = 10) {
let thisLen = bezier.length()
if (Math.abs(len + thisLen - distance) < 0.1) return op.to
if (len + thisLen > distance)
return shiftAlongBezier(distance - len, bezier, thisLen * stepsPerMm)
return __shiftAlongBezier(distance - len, bezier, thisLen * stepsPerMm)
len += thisLen
}
current = op.to
@ -1266,25 +1266,10 @@ function __pathOffset(path, distance, log) {
* @private
* @param {float} distance - The distance to shift along the cubic Bezier curve
* @param {Bezier} bezier - The BezierJs instance
* @param {int} steps - The numer of steps to walk the Bezier with
* @param {int} steps - The numer of steps per mm to walk the Bezier with
* @return {Point} point - The point at distance along the cubic Bezier curve
*/
function shiftAlongBezier(distance, bezier, steps = false) {
let rlen
if (!steps) {
rlen = new Path()
.move(new Point(...Object.values(bezier.points[0])))
.curve(
new Point(...Object.values(bezier.points[1])),
new Point(...Object.values(bezier.points[2])),
new Point(...Object.values(bezier.points[3]))
)
.roughLength()
if (rlen < 2) steps = 20
else if (rlen < 10) steps = 40
else if (rlen < 100) steps = 100
else steps = 200
}
function __shiftAlongBezier(distance, bezier, steps) {
let previous, next, t, thisLen
let len = 0
for (let i = 0; i <= steps; i++) {

View file

@ -334,7 +334,7 @@ Pattern.prototype.render = function () {
this.svg = new Svg(this)
this.svg.hooks = this.hooks
return this.__pack().svg.render(this)
return this.__pack().svg.render()
}
/**

View file

@ -29,7 +29,7 @@ export function Svg(pattern) {
this.attributes.add('xmlns', 'http://www.w3.org/2000/svg')
this.attributes.add('xmlns:svg', 'http://www.w3.org/2000/svg')
this.attributes.add('xmlns:xlink', 'http://www.w3.org/1999/xlink')
this.attributes.add('xml:lang', pattern.settings.locale)
this.attributes.add('xml:lang', pattern?.settings?.[0]?.locale || 'en')
this.attributes.add('xmlns:freesewing', 'http://freesewing.org/namespaces/freesewing')
this.attributes.add('freesewing', version)
}
@ -44,21 +44,24 @@ export function Svg(pattern) {
* @param {Pattern} pattern - The pattern to render
* @return {string} svg - The rendered SVG output
*/
Svg.prototype.render = function (pattern) {
this.idPrefix = pattern.settings.idPrefix
Svg.prototype.render = function () {
this.idPrefix = this.pattern?.settings?.[0]?.idPrefix || 'fs-'
this.__runHooks('preRender')
pattern.__runHooks('postLayout')
if (!pattern.settings.embed) {
this.attributes.add('width', round(pattern.width) + 'mm')
this.attributes.add('height', round(pattern.height) + 'mm')
this.pattern.__runHooks('postLayout')
if (!this.pattern.settings[0].embed) {
this.attributes.add('width', round(this.pattern.width) + 'mm')
this.attributes.add('height', round(this.pattern.height) + 'mm')
}
this.attributes.add('viewBox', `0 0 ${pattern.width} ${pattern.height}`)
this.attributes.add('viewBox', `0 0 ${round(this.pattern.width)} ${round(this.pattern.height)}`)
this.head = this.__renderHead()
this.tail = this.__renderTail()
this.svg = ''
this.layout = {} // Reset layout
for (let stackId in pattern.stacks) {
const stack = pattern.stacks[stackId]
this.activeStackIndex = 0
for (let stackId in this.pattern.stacks) {
this.activeStack = stackId
this.idPrefix = this.pattern.settings[this.activeStackIndex].idPrefix
const stack = this.pattern.stacks[stackId]
if (!stack.hidden) {
const stackSvg = this.__renderStack(stack)
this.layout[stackId] = {
@ -69,6 +72,7 @@ Svg.prototype.render = function (pattern) {
this.svg += stackSvg
this.svg += this.__closeGroup()
}
this.activeStackIndex++
}
this.svg = this.prefix + this.__renderSvgTag() + this.head + this.svg + this.tail
this.__runHooks('postRender')
@ -137,7 +141,7 @@ Svg.prototype.__indent = function () {
Svg.prototype.__insertText = function (text) {
if (this.hooks.insertText.length > 0) {
for (let hook of this.hooks.insertText)
text = hook.method(this.pattern.settings.locale, text, hook.data)
text = hook.method(this.pattern.settings[this.activeStackIndex].locale || 'en', text, hook.data)
}
return text
@ -223,7 +227,6 @@ Svg.prototype.__renderDefs = function () {
*/
Svg.prototype.__renderHead = function () {
let svg = this.__renderStyle()
svg += this.__renderScript()
svg += this.__renderDefs()
svg += this.__openGroup(this.idPrefix + 'container')
@ -279,8 +282,8 @@ Svg.prototype.__renderPathText = function (path) {
* @param {Part} part - The Part instance to render
* @return {string} svg - The SVG markup for the Part object
*/
Svg.prototype.__renderPart = function (part, partId) {
let svg = this.__openGroup(`${this.idPrefix}part-${partId}`, part.attributes)
Svg.prototype.__renderPart = function (part) {
let svg = this.__openGroup(`${this.idPrefix}stack-${this.activeStack}-part-${part.name}`, part.attributes)
for (let key in part.paths) {
let path = part.paths[key]
if (!path.hidden) svg += this.__renderPath(path)
@ -302,22 +305,6 @@ Svg.prototype.__renderPart = function (part, partId) {
return svg
}
/**
* Returns SVG markup for the script block
*
* @private
* @return {string} svg - The SVG markup for the script block
*/
Svg.prototype.__renderScript = function () {
let svg = '<script type="text/javascript"> <![CDATA['
this.__indent()
svg += this.__nl() + this.script
this.__outdent()
svg += this.__nl() + ']]>' + this.__nl() + '</script>' + this.__nl()
return svg
}
/**
* Returns SVG markup for a snippet
*

View file

@ -725,13 +725,8 @@ export const __addPartPlugins = (part, config, store) => {
store.log.debug(`🔌 __${plugin.name}__ plugin in \`${part.name}\``)
// Do not overwrite an existing plugin with a conditional plugin unless it is also conditional
if (plugin.plugin && plugin.condition) {
if (plugins[plugin.plugin.name]?.condition) {
store.log.info(
`Plugin \`${plugin.plugin.name}\` was re-requested conditionally. Overwriting earlier condition.`
)
plugins[plugin.plugin.name] = plugin
} else
store.log.info(
if (plugins[plugin.plugin.name]?.condition) plugins[plugin.plugin.name] = plugin
else store.log.info(
`Plugin \`${plugin.plugin.name}\` was requested conditionally, but is already loaded explicitly. Not loading.`
)
} else {

View file

@ -3,366 +3,22 @@ import pkg from '../../package.json' assert { type: 'json' }
const { version } = pkg
var render = {
boilerplate: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="0mm" height="0mm" viewBox="0 0 0 0"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
</g>
<!-- end of group #fs-container -->
</svg>`,
boilerplateNl: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="nl" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="0mm" height="0mm" viewBox="0 0 0 0"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
</g>
<!-- end of group #fs-container -->
</svg>`,
embed: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" viewBox="0 0 0 0"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
</g>
<!-- end of group #fs-container -->
</svg>`,
part: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="4mm" height="4mm" viewBox="0 0 4 4"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(2, 2)">
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
path: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="44mm" height="56.45mm" viewBox="0 0 44 56.451075975520425"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(2, 2)">
<path id="something" class="freesewing" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" />
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
text: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="4mm" height="4mm" viewBox="0 0 4 4"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(2, 2)">
<text class="text-lg" x="20" y="20"><tspan>This is a test</tspan>
</text>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
circle: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="104mm" height="104mm" viewBox="0 0 104 104"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(32, 32)"><circle
cx="20"
cy="20"
r="50"
></circle>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
multiText: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="4mm" height="4mm" viewBox="0 0 4 4"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform=" translate(2, 2)">
<text class="text-lg" x="20" y="20"><tspan>This is a test</tspan><tspan x="20" dy="8">with text on</tspan><tspan x="20" dy="8">multiple lines</tspan>
</text>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
multiTextDflt: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="4mm" height="4mm" viewBox="0 0 4 4"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform=" translate(2, 2)">
<text class="text-lg" x="20" y="20"><tspan>This is a test</tspan><tspan x="20" dy="6">with text on</tspan><tspan x="20" dy="6">multiple lines</tspan>
</text>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
textOnPath: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="44mm" height="56.45mm" viewBox="0 0 44 56.451075975520425"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(2, 2)">
<path data-text="This is another test" data-text-class="text-sm" class="freesewing" id="fs-1" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" />
<text><textPath xlink:href="#fs-1" ><tspan class="text-sm">This is another test</tspan></textPath>
</text>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
textOnPathCenter: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="44mm" height="56.45mm" viewBox="0 0 44 56.451075975520425"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(2, 2)">
<path data-text="This is another test" data-text-class="center" class="freesewing" id="fs-1" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" />
<text><textPath xlink:href="#fs-1" startOffset="50%" ><tspan class="center">This is another test</tspan></textPath>
</text>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
textOnPathRight: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="44mm" height="56.45mm" viewBox="0 0 44 56.451075975520425"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(2, 2)">
<path data-text="This is another test" data-text-class="right" class="freesewing" id="fs-1" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" />
<text><textPath xlink:href="#fs-1" startOffset="100%" ><tspan class="right">This is another test</tspan></textPath>
</text>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
snippet: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="4mm" height="4mm" viewBox="0 0 4 4"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(2, 2)">
<use x="20" y="20" xlink:href="#test" transform="translate(20, 20) scale(1) translate(-20, -20)"></use>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`,
rotatedSnippet: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="${version}" width="4mm" height="4mm" viewBox="0 0 4 4"
>
<style type="text/css"> <![CDATA[
]]>
</style>
<script type="text/javascript"> <![CDATA[
]]>
</script>
<defs>
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
<!-- Start of group #fs-part-test -->
<g id="fs-part-test" transform="translate(2, 2)">
<use x="20" y="20" xlink:href="#test" data-rotate="90" transform="translate(20, 20) scale(1) translate(-20, -20) rotate(90, 20, 20)"></use>
</g>
<!-- end of group #fs-part-test -->
</g>
<!-- end of group #fs-container -->
</svg>`
boilerplate: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svgxmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="3.0.0-alpha.0" width="44mm" height="56.45mm" viewBox="0 0 44 56.45"><style type="text/css"> <![CDATA[]]></style><defs></defs><!-- Start of group #fs-container --><g id="fs-container"><!-- Start of group #fs-stack-test --><g id="fs-stack-test" transform="translate(2, 2)"><!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><path id="something" class="freesewing" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" /></g><!-- end of group #fs-stack-test-part-test --></g><!-- end of group #fs-stack-test --></g><!-- end of group #fs-container --></svg>`,
boilerplateNl: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svgxmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="nl" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="3.0.0-alpha.0" width="44mm" height="56.45mm" viewBox="0 0 44 56.45"><style type="text/css"> <![CDATA[]]></style><defs></defs><!-- Start of group #fs-container --><g id="fs-container"><!-- Start of group #fs-stack-test --><g id="fs-stack-test" transform="translate(2, 2)"><!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><path id="something" class="freesewing" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" /></g><!-- end of group #fs-stack-test-part-test --></g><!-- end of group #fs-stack-test --></g><!-- end of group #fs-container --></svg>`,
embed: `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svgxmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" xmlns:freesewing="http://freesewing.org/namespaces/freesewing" freesewing="3.0.0-alpha.0" viewBox="0 0 44 56.45"><style type="text/css"> <![CDATA[]]></style><defs></defs><!-- Start of group #fs-container --><g id="fs-container"><!-- Start of group #fs-stack-test --><g id="fs-stack-test" transform="translate(2, 2)"><!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><path id="something" class="freesewing" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" /></g><!-- end of group #fs-stack-test-part-test --></g><!-- end of group #fs-stack-test --></g><!-- end of group #fs-container --></svg>`,
stack: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><path id="something" class="freesewing" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" /></g><!-- end of group #fs-stack-test-part-test -->`,
part: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><path id="something" class="freesewing" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" /></g><!-- end of group #fs-stack-test-part-test -->`,
path: `<path id="something" class="freesewing" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" />`,
text: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><text class="text-lg" x="20" y="20"><tspan>This is a test </tspan></text></g><!-- end of group #fs-stack-test-part-test -->`,
multiText: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><text class="text-lg" x="20" y="20"><tspan>This is a test</tspan><tspan x="20" dy="6">with text on</tspan><tspan x="20" dy="6">multiple lines </tspan></text></g><!-- end of group #fs-stack-test-part-test -->`,
multiTextDflt: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><text class="text-lg" x="20" y="20"><tspan>This is a test</tspan><tspan x="20" dy="6">with text on</tspan><tspan x="20" dy="6">multiple lines </tspan></text></g><!-- end of group #fs-stack-test-part-test -->`,
textOnPath: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><path data-text="This is another test" data-text-class="text-sm" class="freesewing" id="fs-1" d="M 0,0 L 40,20 C 12,34 56,78 21,32 z" /><text><textPath xlink:href="#fs-1" ><tspan class="text-sm">This is another test</tspan></textPath></text></g><!-- end of group #fs-stack-test-part-test -->`,
textOnPathCenter: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><path data-text="This is another test" data-text-class="center" class="freesewing" id="fs-1" d="" /><text><textPath xlink:href="#fs-1" startOffset="50%" ><tspan class="center">This is another test</tspan></textPath></text></g><!-- end of group #fs-stack-test-part-test -->`,
textOnPathRight: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><path data-text="This is another test" data-text-class="right" class="freesewing" id="fs-1" d="" /><text><textPath xlink:href="#fs-1" startOffset="100%" ><tspan class="right">This is another test</tspan></textPath></text></g><!-- end of group #fs-stack-test-part-test -->`,
circle: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><circle cx="20" cy="20" r="50" ></circle></g><!-- end of group #fs-stack-test-part-test -->`,
snippet: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><use x="20" y="20" xlink:href="#test" transform="translate(20, 20) scale(1) translate(-20, -20) translate(20, 20) scale(1) translate(-20, -20)"></use></g><!-- end of group #fs-stack-test-part-test -->`,
rotatedSnippet: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><use x="20" y="20" xlink:href="#test" data-rotate="90" transform="translate(20, 20) scale(1) translate(-20, -20) rotate(90, 20, 20) translate(20, 20) scale(1) translate(-20, -20) rotate(90, 20, 20)"></use></g><!-- end of group #fs-stack-test-part-test -->`,
scaledSnippet: `<!-- Start of group #fs-stack-test-part-test --><g id="fs-stack-test-part-test" ><use x="20" y="20" xlink:href="#test" data-scale="2" transform="translate(20, 20) scale(2) translate(-20, -20) translate(20, 20) scale(2) translate(-20, -20)"></use></g><!-- end of group #fs-stack-test-part-test -->`,
}
export default render

View file

@ -1,5 +1,5 @@
import chai from 'chai'
import { Design, Part } from '../src/index.mjs'
import { Design, Part, pctBasedOn } from '../src/index.mjs'
const expect = chai.expect
@ -75,6 +75,13 @@ describe('Part', () => {
expect(units(123.456)).to.equal('12.35cm')
})
it('Should convert units directly', () => {
const part = new Part()
part.context = { settings: { units: 'metric' } }
expect(part.units(123.456)).to.equal('12.35cm')
expect(part.units(123.456)).to.equal('12.35cm')
})
it('Should set part attributes', () => {
const part = new Part()
part.attr('foo', 'bar')
@ -136,74 +143,27 @@ describe('Part', () => {
name: 'test',
draft: ({ points, Point, paths, Path, part }) => {
points.from = new Point(123, 456)
points.to = new Point(19, 76)
points.to = new Point(19, 76).attr('data-circle', 12)
paths.test = new Path().move(points.from).line(points.to)
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft()
pattern.draft().render()
const boundary = pattern.parts[0].test.__boundary()
const { topLeft, bottomRight, width, height } = boundary
expect(topLeft.x).to.equal(19)
expect(topLeft.y).to.equal(76)
expect(topLeft.x).to.equal(7)
expect(topLeft.y).to.equal(64)
// Cover the cached branch
pattern.parts[0].test.__boundary()
expect(bottomRight.x).to.equal(123)
expect(bottomRight.y).to.equal(456)
expect(width).to.equal(104)
expect(height).to.equal(380)
expect(width).to.equal(116)
expect(height).to.equal(392)
})
/*
it('Should stack a part', () => {
const part = {
name: 'test',
draft: (part) => {
const { points, Point, paths, Path } = part.shorthand()
points.from = new Point(123, 456)
points.to = new Point(19, 76)
paths.test = new Path().move(points.from).line(points.to)
return aprt
}
}
const design = new Design({ parts: [ part ]})
const pattern = new design({ paperless: true })
pattern.draft()
pattern.parts.test.home()
console.log(pattern.parts.test.attributes)
expect(part.attributes.get('transform')).to.equal('translate(-17, -74)')
})
it('Should only stack a part if needed', () => {
let pattern = new Pattern()
pattern.settings.mode = 'draft'
let part = new pattern.Part()
let short = part.shorthand()
part.points.from = new short.Point(2, 2)
part.points.to = new short.Point(19, 76)
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
part.home()
expect(part.attributes.get('transform')).to.equal(false)
part.home()
expect(part.attributes.get('transform')).to.equal(false)
})
it('Should run hooks', () => {
let count = 0
const design = new Design()
const pattern = new design({ paperless: true })
const part = pattern.__createPartWithContext()
part.hooks.preDraft = [
{
method: function () {
count++
},
},
]
part.runHooks('preDraft')
expect(count).to.equal(1)
})
*/
it('Units closure should log a warning when passing a non-number', () => {
const part = {
name: 'test',
@ -213,79 +173,169 @@ describe('Part', () => {
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
// Let's also cover the branch where complete is false
const pattern = new design({ complete: false} )
pattern.draft()
expect(pattern.stores[0].logs.warning.length).to.equal(1)
expect(pattern.stores[0].logs.warning[0]).to.equal(
'Calling `units(value)` but `value` is not a number (`string`)'
)
})
/*
describe('isEmpty', () => {
it('Should return true if the part has no paths or snippets', () => {
const design = new Design()
it('Should (un)hide a part with hide()/unhide()', () => {
const part = new Part()
expect(part.hidden).to.equal(false)
part.hide()
expect(part.hidden).to.equal(true)
part.unhide()
expect(part.hidden).to.equal(false)
})
it('Should (un)hide a part with setHidden()', () => {
const part = new Part()
expect(part.hidden).to.equal(false)
part.setHidden(true)
expect(part.hidden).to.equal(true)
part.setHidden(false)
expect(part.hidden).to.equal(false)
})
it('Draft method should receive the Snippet constructor', () => {
let method
const part = {
name: 'test',
draft: ({ Point, snippets, Snippet, part }) => {
method = Snippet
snippets.test = new Snippet('notch', new Point(19,80))
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
const part = pattern.__createPartWithContext()
expect(part.isEmpty()).to.be.true
pattern.draft()
expect(typeof method).to.equal('function')
expect(pattern.parts[0].test.snippets.test.def).to.equal('notch')
expect(pattern.parts[0].test.snippets.test.name).to.equal('test')
expect(pattern.parts[0].test.snippets.test.anchor.x).to.equal(19)
expect(pattern.parts[0].test.snippets.test.anchor.y).to.equal(80)
})
it('Should return true if the part has paths but they have no length', () => {
const design = new Design()
it('Measurments proxy should allow setting a measurement', () => {
const part = {
name: 'test',
draft: ({ measurements, part }) => {
measurements.head = 120
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
const part = pattern.__createPartWithContext()
const { Path, paths } = part.shorthand()
paths.seam = new Path()
expect(part.isEmpty()).to.be.true
pattern.draft()
expect(pattern.settings[0].measurements.head).to.equal(120)
})
it("Should return true if the part has paths but they don't render", () => {
const design = new Design()
it('Options proxy should allow setting an option', () => {
const part = {
name: 'test',
draft: ({ options, part }) => {
options.test = 120
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
const part = pattern.__createPartWithContext()
const { Path, paths, Point } = part.shorthand()
paths.seam = new Path().move(new Point(0, 0)).line(new Point(2, 3)).setRender(false)
expect(part.isEmpty()).to.be.true
pattern.draft()
expect(pattern.settings[0].options.test).to.equal(120)
})
it('Should return false if the part has a path with length', () => {
const design = new Design()
it('AbsoluteOptions proxy should allow setting an absoluteOption', () => {
const part = {
name: 'test',
draft: ({ absoluteOptions, part }) => {
absoluteOptions.test = 120
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
const part = pattern.__createPartWithContext()
const { Path, paths, Point } = part.shorthand()
paths.seam = new Path().move(new Point(0, 0)).line(new Point(2, 3))
expect(part.isEmpty()).to.be.false
pattern.draft()
expect(pattern.settings[0].absoluteOptions.test).to.equal(120)
})
it('Should return false if the part has a snippet', () => {
const design = new Design()
it('Snapped percentage options should be available to the draft method', () => {
const part = {
name: 'test',
options: {
test: { pct: 10, min: 5, max: 25, snap: 5, ...pctBasedOn('head') }
},
draft: ({ paths, Path, Point, absoluteOptions, part }) => {
paths.test = new Path()
.move(new Point(0,0))
.line(new Point(absoluteOptions.test, 0))
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design({ measurements: { head: 200 } })
pattern.draft()
expect(pattern.parts[0].test.paths.test.ops[1].to.x).to.equal(20)
})
it('Accessing unknown option should log a warning', () => {
const part = {
name: 'test',
draft: ({ options, part }) => {
if (options.test || true) return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
const part = pattern.__createPartWithContext()
const { Point, snippets, Snippet } = part.shorthand()
snippets.test = new Snippet('test', new Point(0, 0))
expect(part.isEmpty()).to.be.false
pattern.draft()
expect(pattern.stores[0].logs.warning.length).to.equal(1)
expect(pattern.stores[0].logs.warning[0]).to.equal('Tried to access `options.test` but it is `undefined`')
})
it('Should return false if the part has a point that has text', () => {
const design = new Design()
it('Accessing unknown absoluteOption should log a warning', () => {
const part = {
name: 'test',
draft: ({ absoluteOptions, part }) => {
if (absoluteOptions.test || true) return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
const part = pattern.__createPartWithContext()
const { Point, points } = part.shorthand()
points.test = new Point(0, 0)
points.test.attributes.set('data-text', 'text')
expect(part.isEmpty()).to.be.false
pattern.draft()
expect(pattern.stores[0].logs.warning.length).to.equal(1)
expect(pattern.stores[0].logs.warning[0]).to.equal('Tried to access `absoluteOptions.test` but it is `undefined`')
})
it('Should return false if the part has a point that has a circle', () => {
const design = new Design()
it('Injecting a part should contain all data', () => {
const from = {
name: 'from',
draft: ({ points, Point, paths, Path, snippets, Snippet, part }) => {
points.from = new Point(0,0)
points.to = new Point(19,80)
points.start = new Point(100,100)
points.cp1 = new Point(100,200)
points.cp2 = new Point(200,100)
points.end = new Point(200,200)
paths.line = new Path().move(points.from).line(points.to)
paths.curve = new Path().move(points.start).curve(points.cp1, points.cp2, points.end)
snippets.test = new Snippet('notch', points.end)
return part
},
}
const to = {
from,
name: 'to',
draft: ({ part }) => part
}
const design = new Design({ parts: [from, to] })
const pattern = new design()
const part = pattern.__createPartWithContext()
const { Point, points } = part.shorthand()
points.test = new Point(0, 0)
points.test.attributes.set('data-circle', 10)
expect(part.isEmpty()).to.be.false
pattern.draft()
expect(pattern.parts[0].to.points.to.x).to.equal(19)
expect(pattern.parts[0].to.points.to.y).to.equal(80)
expect(typeof pattern.parts[0].to.paths.line).to.equal('object')
expect(pattern.parts[0].to.paths.curve.ops[1].cp2.x).to.equal(200)
})
})
*/
})

View file

@ -57,6 +57,63 @@ describe('Path', () => {
expect(round(pattern.parts[0].test.paths.test.ops[2].cp1.y)).to.equal(10)
})
it('Should log a warning when passing a non-Point to smurve()', () => {
const part = {
name: 'test',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20)
points.cp1 = new Point(40, 10)
points.cp2 = new Point(60, 30)
points.to = new Point(90, 20)
paths.test = new Path()
.move(points.from)
.curve(points.cp1, points.cp2, points.to)
.smurve('hi', 'there')
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft()
expect(pattern.stores[0].logs.warning.length).to.equal(2)
expect(pattern.stores[0].logs.warning[0]).to.equal('Called `Path.smurve(cp2, to)` but `to` is not a `Point` object')
})
it('Should log a warning when passing a non-Point to smurve_()', () => {
const part = {
name: 'test',
draft: ({ Point, Path, paths, part }) => {
paths.test = new Path().smurve_('hi')
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft()
expect(pattern.stores[0].logs.warning.length).to.equal(1)
expect(pattern.stores[0].logs.warning[0]).to.equal('Called `Path.smurve_(to)` but `to` is not a `Point` object')
})
it('Should log a warning when passing a non-Path to the paths proxy', () => {
const part = {
name: 'test',
draft: ({ paths, part }) => {
paths.test = 'Wriing code can get very lonely sometimes'
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft()
expect(pattern.stores[0].logs.warning.length).to.equal(2)
expect(pattern.stores[0].logs.warning[0]).to.equal('`paths.test` was set with a value that is not a `Path` object')
expect(pattern.stores[0].logs.warning[1]).to.equal('Could not set `name` property on `paths.test`')
})
it('Should offset a line', () => {
const part = {
name: 'test',
@ -161,13 +218,30 @@ describe('Path', () => {
paths.curve = new Path()
.move(new Point(0, 0))
.curve(new Point(0, 50), new Point(100, 50), new Point(100, 0))
.close()
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft().render()
expect(round(pattern.parts[0].test.paths.curve.roughLength())).to.equal(200)
expect(round(pattern.parts[0].test.paths.curve.roughLength())).to.equal(300)
})
it('Should return the rough length of a line', () => {
const part = {
name: 'test',
draft: ({ paths, Path, Point, part }) => {
paths.line = new Path()
.move(new Point(0, 0))
.line(new Point(0, 50))
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft().render()
expect(round(pattern.parts[0].test.paths.line.roughLength())).to.equal(50)
})
it('Should return the path start point', () => {
@ -615,6 +689,32 @@ describe('Path', () => {
expect(round(line.to.y)).to.equal(46.98)
})
it('Should split a path on a line joint', () => {
const a = new Point(45, 60)
const b = new Point(10, 30)
const c = new Point(90, 30)
const test = new Path().move(a).line(b).line(c)
let halves = test.split(b)
expect(halves[0].ops[1].to.x).to.equal(10)
expect(halves[0].ops[1].to.y).to.equal(30)
expect(halves[1].ops[0].to.x).to.equal(10)
expect(halves[1].ops[0].to.y).to.equal(30)
})
it('Should split a path on a curve joint', () => {
const a = new Point(45, 60)
const b = new Point(10, 30)
const c = new Point(90, 30)
const test = new Path().move(a)._curve(b,b)._curve(c,c)
let halves = test.split(b)
expect(halves[0].ops[1].to.x).to.equal(10)
expect(halves[0].ops[1].to.y).to.equal(30)
expect(halves[1].ops[0].to.x).to.equal(10)
expect(halves[1].ops[0].to.y).to.equal(30)
})
it('Should trim a path when lines overlap', () => {
const A = new Point(0, 0)
const B = new Point(100, 100)
@ -684,6 +784,16 @@ describe('Path', () => {
expect(test.ops[1].to.y).to.equal(20)
})
it('Calling translate with non-numbers should generate a warning', () => {
const log = []
const p = new Path()
p.log = { warning: msg => log.push(msg) }
p.translate('a', 'b')
expect(log.length).to.equal(2)
expect(log[0]).to.equal('Called `Path.translate(x, y)` but `x` is not a number')
expect(log[1]).to.equal('Called `Path.translate(x, y)` but `y` is not a number')
})
it('Should add a path attribute', () => {
const line = new Path()
.move(new Point(0, 0))
@ -1039,7 +1149,7 @@ describe('Path', () => {
it('Should log a warning when splitting a path on a non-point', () => {
const part = {
name: 'test',
draft: ({ Path, Point, points }) => {
draft: ({ Path, Point, points, part}) => {
points.a = new Path().move(new Point(0, 0)).line(new Point(0, 40)).split()
return part
},
@ -1051,4 +1161,31 @@ describe('Path', () => {
'Called `Path.split(point)` but `point` is not a `Point` object'
)
})
it('Should add a class', () => {
const part = {
name: 'test',
draft: ({ Path, paths, Point, points, part }) => {
paths.line = new Path()
.move(new Point(0,0))
.line(new Point(10,10))
.addClass('fabric banana')
return part
},
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft()
expect(pattern.parts[0].test.paths.line.attributes.get('class')).to.equal('fabric banana')
})
it('Should (un)hide a path with hide()/unhide()', () => {
const path = new Path()
expect(path.hidden).to.equal(false)
path.hide()
expect(path.hidden).to.equal(true)
path.unhide()
expect(path.hidden).to.equal(false)
})
})

View file

@ -48,7 +48,7 @@ describe('Pattern', () => {
name: 'test.partB',
measurements: ['head', 'knee'],
optionalMeasurements: ['knee'],
after: partA,
after: [ partA ],
plugins: [
{
name: 'testPlugin',

View file

@ -580,6 +580,51 @@ describe('Pattern', () => {
expect(pattern.hooks.preRender.length).to.equal(1)
})
it('Load conditional plugins that are also passing data', () => {
const plugin1 = {
name: 'example1',
version: 1,
hooks: {
preRender: function (svg) {
svg.attributes.add('freesewing:plugin-example1', 1)
},
},
}
const plugin2 = {
name: 'example2',
version: 2,
hooks: {
preRender: function (svg) {
svg.attributes.add('freesewing:plugin-example2', 1)
},
},
}
const condition1 = () => true
const condition2 = () => false
const part1 = {
name: 'part1',
plugins: [
[plugin1, {} ],
{ plugin: plugin2, condition: condition1 }
],
draft: ({ part }) => part
}
const part2 = {
name: 'part2',
plugins: [
plugin2,
{ plugin: plugin2, condition: condition2 },
],
draft: ({ part }) => part
}
const design = new Design({
parts: [ part1, part2 ]
})
const pattern = new design()
pattern.init()
expect(pattern.hooks.preRender.length).to.equal(2)
})
it('Pattern.init() should register a hook via on', () => {
const Pattern = new Design()
const pattern = new Pattern()

View file

@ -1,43 +1,45 @@
import chai from 'chai'
import chaiString from 'chai-string'
//import { Design, Pattern } from '../src/index.mjs'
//import pkg from '../package.json' assert { type: 'json' }
//import render from './fixtures/render.mjs'
import { Svg } from '../src/svg.mjs'
import { Design, Pattern, Attributes } from '../src/index.mjs'
import { version } from '../data.mjs'
import render from './fixtures/render.mjs'
chai.use(chaiString)
const expect = chai.expect
//const { version } = pkg
it('FIXME: Write some tests here', () => {
expect(true).to.equal(true)
})
/*
describe('Svg', () => {
const getPattern = (settings={}, draft=false) => {
const part = {
name: 'test',
draft: part => {
const { paths, Path, Point, points } = part.shorthand()
points.a = new Path()
draft: draft
? draft
: ({ paths, Path, Point, part }) => {
paths.test = new Path()
.move(new Point(0, 0))
.line(new Point(0, 40))
.shiftFractionAlong()
.line(new Point(40, 20))
.curve(new Point(12, 34), new Point(56, 78), new Point(21, 32))
.close()
.attr('id', 'something')
.attr('class', 'freesewing')
return part
}
}
const design = new Design({ parts: [ part ] })
const pattern = new design()
const Pattern = new Design({ parts: [ part ] })
return new Pattern(settings)
}
const trim = svg => svg.split("\n").map(line => line.trim()).join('')
describe('Svg', () => {
it('Svg constructor should initialize object', () => {
pattern.render()
let svg = pattern.svg
expect(svg.openGroups).to.eql([])
const svg = new Svg()
expect(svg.attributes instanceof Attributes).to.equal(true)
expect(svg.freeId).to.equal(0)
expect(svg.body).to.equal('')
expect(svg.style).to.equal('')
expect(svg.script).to.equal('')
expect(svg.defs).to.equal('')
expect(svg.pattern).to.eql(pattern)
expect(svg.prefix).to.equal('<?xml version="1.0" encoding="UTF-8" standalone="no"?>')
expect(svg.attributes.get('xmlns')).to.equal('http://www.w3.org/2000/svg')
expect(svg.attributes.get('xmlns:svg')).to.equal('http://www.w3.org/2000/svg')
@ -48,47 +50,233 @@ describe('Svg', () => {
expect(svg.attributes.get('freesewing')).to.equal(version)
})
it('Should render Svg boilerplate', () => {
let pattern = new Pattern()
expect(pattern.render()).to.equalIgnoreSpaces(render.boilerplate)
it('Svg constructor should use the object we pass it as pattern', () => {
const obj = {}
const svg = new Svg(obj)
expect(svg.pattern).to.eql(obj)
})
it('Should render language attribute', () => {
let pattern = new Pattern()
pattern.settings.locale = 'nl'
expect(pattern.render()).to.equalIgnoreSpaces(render.boilerplateNl)
it('Should render a pattern as SVG', () => {
const pattern = getPattern()
const svg = pattern.draft().render()
expect(trim(svg)).to.equalIgnoreSpaces(render.boilerplate)
})
it('Should render Svg boilerplate for embedding', () => {
let pattern = new Pattern()
pattern.settings.embed = true
expect(pattern.render()).to.equalIgnoreSpaces(render.embed)
it('Should render the SVG language attribute', () => {
const pattern = getPattern({ locale: 'nl' })
const svg = pattern.draft().render()
expect(svg).to.equalIgnoreSpaces(render.boilerplateNl)
})
it('Should render Svg part boilerplate', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
expect(pattern.render()).to.equalIgnoreSpaces(render.part)
pattern.parts.test.render = false
expect(pattern.render()).to.equalIgnoreSpaces(render.boilerplate)
it('Should render the SVG viewBox attribute for embedding', () => {
const pattern = getPattern({ embed: true })
const svg = pattern.draft().render()
expect(trim(svg)).to.equalIgnoreSpaces(render.embed)
})
it('Should render Svg path', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.paths.test = new p.Path()
.move(new p.Point(0, 0))
.line(new p.Point(40, 20))
.curve(new p.Point(12, 34), new p.Point(56, 78), new p.Point(21, 32))
it('Should render a stack as SVG', () => {
const pattern = getPattern()
pattern.draft().render()
const svg = pattern.svg.__renderStack(pattern.stacks.test)
expect(trim(svg)).to.equalIgnoreSpaces(render.part)
})
it('Should render a part as SVG', () => {
const pattern = getPattern()
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.part)
})
it('Should render a path as SVG', () => {
const pattern = getPattern()
pattern.draft().render()
const svg = pattern.svg.__renderPath(pattern.parts[0].test.paths.test)
expect(trim(svg)).to.equalIgnoreSpaces(render.path)
})
it('Should render Svg text', () => {
const pattern = getPattern({}, ({ points, Point, part }) => {
points.test = new Point(20, 20)
.attr('data-text', 'This is a test')
.attr('data-text-class', 'text-lg')
points.other = new Point(10, 10).attr('data-text', '')
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.text)
})
it('Should render Svg multi-line text', () => {
const pattern = getPattern({}, ({ points, Point, part }) => {
points.test = new Point(20, 20)
.attr('data-text', 'This is a test\nwith text on\nmultiple lines')
.attr('data-text-class', 'text-lg')
.attr('data-text-lineheight', 8)
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.multiText)
})
it('Should render Svg multi-line text with default lineheight', () => {
const pattern = getPattern({}, ({ points, Point, part }) => {
points.test = new Point(20, 20)
.attr('data-text', 'This is a test\nwith text on\nmultiple lines')
.attr('data-text-class', 'text-lg')
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.multiTextDflt)
})
it('Should render Svg text on path', () => {
const pattern = getPattern({}, ({ paths, Path, Point, part }) => {
paths.test = new Path()
.move(new Point(0, 0))
.line(new Point(40, 20))
.curve(new Point(12, 34), new Point(56, 78), new Point(21, 32))
.close()
.attr('id', 'something')
.attr('data-text', 'This is another test')
.attr('data-text-class', 'text-sm')
.attr('class', 'freesewing')
expect(pattern.render()).to.equalIgnoreSpaces(render.path)
return part
})
it('Should not render Svg path when render property is false', () => {
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.textOnPath)
})
it('Should render Svg text on path, center aligned', () => {
const pattern = getPattern({}, ({ paths, Path, Point, part }) => {
paths.test = new Path()
.attr('data-text', 'This is another test')
.attr('data-text-class', 'center')
.attr('class', 'freesewing')
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.textOnPathCenter)
})
it('Should render Svg text on path, right aligned', () => {
const pattern = getPattern({}, ({ paths, Path, Point, part }) => {
paths.test = new Path()
.attr('data-text', 'This is another test')
.attr('data-text-class', 'right')
.attr('class', 'freesewing')
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.textOnPathRight)
})
it('Should render an Svg circle', () => {
const pattern = getPattern({}, ({ points, Point, part }) => {
points.test = new Point(20, 20).attr('data-circle', '50')
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.circle)
})
it('Should render an Svg snippet', () => {
const pattern = getPattern({}, ({ snippets, Snippet, Point, part }) => {
snippets.test = new Snippet('test', new Point(20, 20), 'This is a snippet')
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.snippet)
})
it('Should render a rotated Svg snippet', () => {
const pattern = getPattern({}, ({ snippets, Snippet, Point, part }) => {
snippets.test = new Snippet('test', new Point(20, 20), 'This is a snippet')
.attr( 'data-rotate', 90)
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.rotatedSnippet)
})
it('Should replaced double quotes in Svg text', () => {
const svg = new Svg()
expect(svg.__escapeText('This is a "test" message')).to.equal(
'This is a &#8220;test&#8220; message'
)
})
it('Should scale an Svg snippet', () => {
const pattern = getPattern({}, ({ snippets, Snippet, Point, part }) => {
snippets.test = new Snippet('test', new Point(20, 20), 'This is a snippet')
.attr( 'data-scale', 2)
return part
})
pattern.draft().render()
const svg = pattern.svg.__renderPart(pattern.parts[0].test)
expect(trim(svg)).to.equalIgnoreSpaces(render.scaledSnippet)
})
it('Should run preRender hook', () => {
const pattern = getPattern()
pattern.on('preRender', (svg) => {
svg.attributes.set('data-hook', 'preRender')
})
pattern.draft().render()
expect(pattern.svg.attributes.get('data-hook')).to.equal('preRender')
})
it('Should run insertText hook', () => {
const pattern = getPattern({}, ({ points, Point, part }) => {
points.test = new Point(20, 20)
.attr('data-text', 'This is a test')
.attr('data-text-class', 'text-lg')
return part
})
pattern.on('insertText', (locale, text) => {
return text.toUpperCase()
})
pattern.draft()
expect(pattern.render()).to.contain('THIS IS A TEST')
})
it('Should run postRender hook', () => {
const pattern = getPattern()
pattern.on('postRender', (svg) => {
svg.svg = 'test'
})
expect(pattern.render()).to.equal('test')
})
it('Should tab in and out', () => {
const svg = new Svg()
svg.tabs = 2
expect(svg.__tab()).to.equal(' ')
})
/*
it('Should not render an Svg path when render property is false', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
@ -103,185 +291,5 @@ describe('Svg', () => {
p.paths.test.render = false
expect(pattern.render()).to.equalIgnoreSpaces(render.part)
})
it('Should render Svg text', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.points.test = new p.Point(20, 20)
.attr('data-text', 'This is a test')
.attr('data-text-class', 'text-lg')
p.points.other = new p.Point(10, 10).attr('data-text', '')
expect(pattern.render()).to.equalIgnoreSpaces(render.text)
})
it('Should render Svg multi-line text', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.points.test = new p.Point(20, 20)
.attr('data-text', 'This is a test\nwith text on\nmultiple lines')
.attr('data-text-class', 'text-lg')
.attr('data-text-lineheight', 8)
expect(pattern.render()).to.equalIgnoreSpaces(render.multiText)
})
it('Should render Svg multi-line text with default lineheight', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.points.test = new p.Point(20, 20)
.attr('data-text', 'This is a test\nwith text on\nmultiple lines')
.attr('data-text-class', 'text-lg')
expect(pattern.render()).to.equalIgnoreSpaces(render.multiTextDflt)
})
it('Should not render text when there is none', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.points.test = new p.Point(20, 20)
expect(pattern.render()).to.equalIgnoreSpaces(render.part)
})
it('Should render Svg text on path', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.paths.test = new p.Path()
.move(new p.Point(0, 0))
.line(new p.Point(40, 20))
.curve(new p.Point(12, 34), new p.Point(56, 78), new p.Point(21, 32))
.close()
.attr('data-text', 'This is another test')
.attr('data-text-class', 'text-sm')
.attr('class', 'freesewing')
expect(pattern.render()).to.equalIgnoreSpaces(render.textOnPath)
})
it('Should render Svg text on path, center aligned', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.paths.test = new p.Path()
.move(new p.Point(0, 0))
.line(new p.Point(40, 20))
.curve(new p.Point(12, 34), new p.Point(56, 78), new p.Point(21, 32))
.close()
.attr('data-text', 'This is another test')
.attr('data-text-class', 'center')
.attr('class', 'freesewing')
expect(pattern.render()).to.equalIgnoreSpaces(render.textOnPathCenter)
})
it('Should render Svg text on path, right aligned', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.paths.test = new p.Path()
.move(new p.Point(0, 0))
.line(new p.Point(40, 20))
.curve(new p.Point(12, 34), new p.Point(56, 78), new p.Point(21, 32))
.close()
.attr('data-text', 'This is another test')
.attr('data-text-class', 'right')
.attr('class', 'freesewing')
expect(pattern.render()).to.equalIgnoreSpaces(render.textOnPathRight)
})
it('Should render an Svg circle', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.points.test = new p.Point(20, 20).attr('data-circle', '50')
expect(pattern.render()).to.equalIgnoreSpaces(render.circle)
})
it('Should render an Svg snippet', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.snippets.test = new p.Snippet('test', new p.Point(20, 20), 'This is a snippet')
expect(pattern.render()).to.equalIgnoreSpaces(render.snippet)
})
it('Should render a rotated Svg snippet', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.snippets.test = new p.Snippet('test', new p.Point(20, 20), 'This is a snippet').attr(
'data-rotate',
90
)
expect(pattern.render()).to.equalIgnoreSpaces(render.rotatedSnippet)
})
it('Should replaced double quotes in Svg text', () => {
const pattern = new Pattern()
pattern.render()
expect(pattern.svg.escapeText('This is a "test" message')).to.equal(
'This is a &#8220;test&#8220; message'
)
})
it('Should scale an Svg snippet', () => {
let pattern = new Pattern()
pattern.render()
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.snippets.test = new p.Snippet('test', new p.Point(20, 20), 'This is a snippet').attr(
'data-scale',
2
)
expect(pattern.render()).to.contain('scale(2)')
})
it('Should run preRender hook', () => {
let pattern = new Pattern()
pattern.on('preRender', (svg) => {
svg.attributes.set('data-hook', 'preRender')
})
pattern.render()
expect(pattern.svg.attributes.get('data-hook')).to.equal('preRender')
})
it('Should run insertText hook', () => {
let pattern = new Pattern()
pattern.on('insertText', (locale, text) => {
return text.toUpperCase()
})
pattern.parts.test = new pattern.Part()
let p = pattern.parts.test
p.points.test = new p.Point(20, 20)
.attr('data-text', 'This is a test')
.attr('data-text-class', 'text-lg')
expect(pattern.render()).to.contain('THIS IS A TEST')
})
it('Should run postRender hook', () => {
let pattern = new Pattern()
pattern.on('postRender', (svg) => {
svg.svg = 'test'
})
expect(pattern.render()).to.equal('test')
})
it('Should tab in and out', () => {
let pattern = new Pattern()
pattern.render()
const svg = pattern.svg
svg.tabs = 2
expect(svg.tab()).to.equal(' ')
})
})
*/
})

View file

@ -1,6 +1,7 @@
import chai from 'chai'
import {
Point,
Design,
capitalize,
beamsIntersect,
linesIntersect,
@ -23,6 +24,7 @@ import {
deg2rad,
rad2deg,
pctBasedOn,
generateStackTransform,
} from '../src/index.mjs'
const { expect } = chai
@ -474,29 +476,20 @@ describe('Utils', () => {
expect(result.toAbs(0.0123, { measurements })).to.equal(12.3)
expect(result.fromAbs(12.3, { measurements })).to.equal(0.0123)
})
/*
it('Should generate a part transform', () => {
const part = {
it('Should generate a stack transform', () => {
const test = {
name: 'test',
draft: part => {
const { points, Point, paths, Path } = part.shorthand()
draft: ({ points, Point, paths, Path, part }) => {
points.from = new Point(2, 2)
points.to = new Point(19, 76)
paths.test = new Path().move(points.from).line(points.to)
return part
}
}
const design = new Design({ parts: [ part ]})
const design = new Design({ parts: [ test ]})
const pattern = new design()
pattern.draft().render()
const transform = generatePartTransform(30, 60, 90, true, true, pattern.__parts.test)
expect(transform.transform).to.equal(
`translate(${30 + part.topLeft.x + part.bottomRight.x} ${
60 + part.topLeft.y + part.bottomRight.y
}) scale(-1 -1) rotate(90 ${part.topLeft.x + part.width / 2} ${
part.topLeft.y + part.height / 2
})`
)
const props = pattern.draft().getRenderProps()
const transform = generateStackTransform(30, 60, 90, true, true, props.stacks.test)
expect(transform.transform).to.equal('translate(51 138) scale(-1 -1) rotate(90 10.5 39)')
})
*/
})