diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index 6c1254b9dad..08762b6880b 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -39,9 +39,9 @@ export function Pattern(config) { addNonEnumProp(this, '__hide', {}) // Enumerable properties - this.config = config // Design config - this.parts = {} // Parts container - this.store = new Store() // Store for sharing data across parts + this.config = config // Design config + this.parts = {} // Drafted parts container + this.store = new Store() // Store for sharing data across parts return this } @@ -200,7 +200,6 @@ Pattern.prototype.draft = function () { if (this.is !== 'sample') { this.is = 'draft' - this.cutList = {} this.store.log.debug(`Drafting pattern`) } // Handle snap for pct options @@ -215,17 +214,15 @@ Pattern.prototype.draft = function () { } this.runHooks('preDraft') - // Don't forget about parts without any dependencies - const allParts = [...new Set([...this.config.draftOrder, ...Object.keys(this.__parts)])] - for (const partName of allParts) { + for (const partName of this.config.draftOrder) { // Create parts this.store.log.debug(`Creating part \`${partName}\``) this.parts[partName] = this.__createPartWithContext(partName) // Handle inject/inheritance - if (typeof this.inject[partName] === 'string') { - this.store.log.debug(`Injecting part \`${this.inject[partName]}\` into part \`${partName}\``) + if (typeof this.__inject[partName] === 'string') { + this.store.log.debug(`Creating part \`${partName}\` from part \`${this.__inject[partName]}\``) try { - this.parts[partName].inject(this.parts[this.inject[partName]]) + this.parts[partName].inject(this.parts[this.__inject[partName]]) } catch (err) { this.store.log.error([ `Could not inject part \`${this.inject[partName]}\` into part \`${partName}\``, diff --git a/packages/core/tests/pattern-draft.mjs b/packages/core/tests/pattern-draft.mjs index c5d06a074af..4cb8a087be6 100644 --- a/packages/core/tests/pattern-draft.mjs +++ b/packages/core/tests/pattern-draft.mjs @@ -4,252 +4,81 @@ import { round, Pattern, Design, pctBasedOn } from '../src/index.mjs' const expect = chai.expect describe('Pattern', () => { + describe('Pattern.draft()', () => { - - it('Should merge settings with default settings', () => { - let pattern = new Pattern() - let settings = { - foo: 'bar', - deep: { - free: 'ze', - }, - } - pattern.apply(settings) - expect(pattern.settings.foo).to.equal('bar') - expect(pattern.settings.locale).to.equal('en') - expect(pattern.settings.margin).to.equal(2) - expect(pattern.settings.idPrefix).to.equal('fs-') - expect(pattern.settings.deep.free).to.equal('ze') - }) - - it('Should draft according to settings', () => { - let count = 0 - const back = { - name: 'back', - hide: true, - draft: function (part) { - count++ - return part - }, - } - const front = { - name: 'front', - from: back, - draft: function (part) { - count++ - return part - }, - } - const Test = new Design({ - name: 'test', - parts: [back, front], - }) - - const pattern = new Test() - pattern.draft() - expect(count).to.equal(2) - }) - - it('Should sample an option', () => { - let pattern = new Pattern({ + const partA = { + name: 'test.partA', + measurements: ['head', 'knee'], + optionalMeasurements: [ 'chest', 'waist'], options: { - len: { pct: 30, min: 10 }, - bonus: 10, + optA: { pct: 40, min: 20, max: 80 } }, - }) - pattern.draft = function () { - pattern.parts.a = new pattern.Part() - pattern.parts.b = new pattern.Part() - let a = pattern.parts.a - a.points.from = new a.Point(0, 0) - a.points.to = new a.Point( - 100 * a.context.settings.options.len, - a.context.settings.options.bonus - ) - a.paths.test = new a.Path().move(a.points.from).line(a.points.to) - pattern.parts.b.inject(a) + draft: () => { } } - pattern.settings.sample = { - type: 'option', - option: 'len', - } - pattern.sample() - expect(pattern.parts.a.paths.test_1.render).to.equal(true) - expect(pattern.parts.b.paths.test_10.ops[1].to.y).to.equal(10) - }) - /* - it("Should sample a list option", () => { - const front = { - name: 'front', + const partB = { + name: 'test.partB', + measurements: ['head', 'knee'], + optionalMeasurements: [ 'knee'], + after: partA, + plugins: [{ + name: 'testPlugin', + hooks: { + preRender: () => {} + } + }], options: { - len: { - dflt: 1, - list: [1,2,3] - } + optB: { deg: 40, min: 20, max: 80 } }, - draft: function(part) { - const { Point, points, Path, paths, options } = part.shorthand() - points.from = new Point(0, 0); - points.to = new Point( 100 * options.len, 0) - paths.line = new Path() - .move(points.from) - .line(points.to) - - return part - } + draft: () => { } } - const Test = new freesewing.Design({ - name: "test", - parts: [front], - }) - const pattern = new Test({ - sample: { - type: 'option', - option: 'len' - } - }) - pattern.sample(); - console.log(pattern.parts) - expect(pattern.parts.front.paths.line_1.ops[1].to.x).to.equal(100); - expect(pattern.parts.front.paths.line_2.ops[1].to.x).to.equal(200); - expect(pattern.parts.front.paths.line_3.ops[1].to.x).to.equal(300); - }); - - it("Should sample a measurement", () => { - const Test = new freesewing.Design({ - name: "test", - parts: ['front'], - measurements: ['head'] - }) - Test.prototype.draftFront = function(part) { - const { Point, points, Path, paths, measurements } = part.shorthand() - points.from = new Point(0, 0); - points.to = new Point( measurements.head, 0) - paths.line = new Path() - .move(points.from) - .line(points.to) - - return part - }; - const pattern = new Test({ - measurements: { - head: 100 + const partC = { + name: 'test.partC', + measurements: ['head', 'knee'], + optionalMeasurements: [ 'knee'], + from: partB, + options: { + optC: { pct: 20, min: 10, max: 30 } }, - sample: { - type: 'measurement', - measurement: 'head' - } - }) - pattern.sample(); - expect(pattern.is).to.equal('sample') - expect(pattern.events.debug[0]).to.equal('Sampling measurement `head`') - for (let i=0;i<10;i++) { - const j = i + 1 - expect(pattern.parts.front.paths[`line_${j}`].ops[1].to.x).to.equal(90 + 2*i); + draft: () => { } } - pattern.sampleMeasurement('nope') - expect(pattern.events.error.length).to.equal(1) - expect(pattern.events.error[0]).to.equal("Cannot sample measurement `nope` because it's `undefined`") - }); - it("Should sample models", () => { - const Test = new freesewing.Design({ - name: "test", - parts: ['front'], - measurements: ['head'] + const Pattern = new Design({ + data: { + name: 'test', + version: '1.2.3', + }, + parts: [ partC ] }) - Test.prototype.draftFront = function(part) { - const { Point, points, Path, paths, measurements } = part.shorthand() - points.from = new Point(0, 0); - points.to = new Point( measurements.head, 0) - paths.line = new Path() - .move(points.from) - .line(points.to) + const pattern = new Pattern() - return part - }; - let pattern = new Test({ - sample: { - type: 'models', - models : { - a: { head: 100 }, - b: { head: 50 }, - } - } - }) - pattern.sample(); - expect(pattern.is).to.equal('sample') - expect(pattern.events.debug[0]).to.equal('Sampling models') - expect(pattern.parts.front.paths[`line_0`].ops[1].to.x).to.equal(100); - expect(pattern.parts.front.paths[`line_1`].ops[1].to.x).to.equal(50); - pattern = new Test({ - sample: { - type: 'models', - models : { - a: { head: 100 }, - b: { head: 50 }, + it('Should draft according to settings', () => { + let count = 0 + const back = { + name: 'back', + hide: true, + draft: function (part) { + count++ + return part }, - focus: 'b' } + const front = { + name: 'front', + from: back, + draft: function (part) { + count++ + return part + }, + } + const Test = new Design({ + name: 'test', + parts: [back, front], + }) + + const pattern = new Test() + pattern.draft() + expect(count).to.equal(2) }) - pattern.sample(); - expect(pattern.is).to.equal('sample') - expect(pattern.parts.front.paths[`line_-1`].ops[1].to.x).to.equal(50); - expect(pattern.parts.front.paths[`line_0`].ops[1].to.x).to.equal(100); - }); - it("Should register a hook via on", () => { - let pattern = new Pattern(); - let count = 0; - pattern._draft = () => {}; - pattern.on("preDraft", function(pattern) { - count++; - }); - pattern.draft(); - expect(count).to.equal(1); - }); - - it("Should register a hook from a plugin", () => { - let pattern = new Pattern(); - let count = 0; - pattern._draft = () => {}; - let plugin = { - name: "test", - version: "0.1-test", - hooks: { - preDraft: function(pattern) { - count++; - } - } - }; - pattern.use(plugin); - pattern.draft(); - expect(count).to.equal(1); - }); - - it("Should register multiple methods on a single hook", () => { - let pattern = new Pattern(); - let count = 0; - pattern._draft = () => {}; - let plugin = { - name: "test", - version: "0.1-test", - hooks: { - preDraft: [ - function(pattern) { - count++; - }, - function(pattern) { - count++; - } - ] - } - }; - pattern.use(plugin); - pattern.draft(); - expect(count).to.equal(2); - }); - */ it('Should check whether a part is needed', () => { let config = { @@ -308,194 +137,12 @@ describe('Pattern', () => { expect(pattern.wants('side')).to.equal(true) }) - it('Should correctly resolve dependencies - string version', () => { - let config = { - name: 'test', - dependencies: { front: 'back', side: 'back', hood: 'front', stripe: 'hood' }, - } - const Test = new Design(config) - Test.prototype = Object.create(Pattern.prototype) - Test.prototype.constructor = Test - Test.prototype.draftBack = function (part) { - return part - } - Test.prototype.draftFront = function (part) { - return part - } - - let pattern = new Test().init() - expect(pattern.config.resolvedDependencies.front.length).to.equal(1) - expect(pattern.config.resolvedDependencies.front[0]).to.equal('back') - expect(pattern.config.resolvedDependencies.side.length).to.equal(1) - expect(pattern.config.resolvedDependencies.side[0]).to.equal('back') - expect(pattern.config.resolvedDependencies.hood.length).to.equal(2) - expect(pattern.config.resolvedDependencies.hood[0]).to.equal('front') - expect(pattern.config.resolvedDependencies.hood[1]).to.equal('back') - expect(pattern.config.resolvedDependencies.stripe.length).to.equal(3) - expect(pattern.config.resolvedDependencies.stripe[0]).to.equal('hood') - expect(pattern.config.resolvedDependencies.stripe[1]).to.equal('front') - expect(pattern.config.resolvedDependencies.stripe[2]).to.equal('back') - expect(pattern.config.resolvedDependencies.back.length).to.equal(0) - expect(pattern.config.draftOrder[0]).to.equal('back') - expect(pattern.config.draftOrder[1]).to.equal('front') - expect(pattern.config.draftOrder[2]).to.equal('side') - expect(pattern.config.draftOrder[3]).to.equal('hood') - }) - - it('Should correctly resolve dependencies - array version', () => { - let config = { - name: 'test', - dependencies: { front: ['back'], side: ['back'], hood: ['front'], stripe: ['hood'] }, - } - const Test = function (settings = false) { - Pattern.call(this, config) - return this - } - Test.prototype = Object.create(Pattern.prototype) - Test.prototype.constructor = Test - Test.prototype.draftBack = function (part) { - return part - } - Test.prototype.draftFront = function (part) { - return part - } - - let pattern = new Test().init() - expect(pattern.config.resolvedDependencies.front.length).to.equal(1) - expect(pattern.config.resolvedDependencies.front[0]).to.equal('back') - expect(pattern.config.resolvedDependencies.side.length).to.equal(1) - expect(pattern.config.resolvedDependencies.side[0]).to.equal('back') - expect(pattern.config.resolvedDependencies.hood.length).to.equal(2) - expect(pattern.config.resolvedDependencies.hood[0]).to.equal('front') - expect(pattern.config.resolvedDependencies.hood[1]).to.equal('back') - expect(pattern.config.resolvedDependencies.stripe.length).to.equal(3) - expect(pattern.config.resolvedDependencies.stripe[0]).to.equal('hood') - expect(pattern.config.resolvedDependencies.stripe[1]).to.equal('front') - expect(pattern.config.resolvedDependencies.stripe[2]).to.equal('back') - expect(pattern.config.resolvedDependencies.back.length).to.equal(0) - expect(pattern.config.draftOrder[0]).to.equal('back') - expect(pattern.config.draftOrder[1]).to.equal('front') - expect(pattern.config.draftOrder[2]).to.equal('side') - expect(pattern.config.draftOrder[3]).to.equal('hood') - }) - - it('Should correctly resolve dependencies - issue #971 - working version', () => { - let config = { - name: 'test', - dependencies: { front: ['back'], crotch: ['front', 'back'] }, - parts: ['back', 'front', 'crotch'], - } - const Test = function (settings = false) { - Pattern.call(this, config) - return this - } - Test.prototype = Object.create(Pattern.prototype) - Test.prototype.constructor = Test - Test.prototype.draftBack = function (part) { - return part - } - Test.prototype.draftFront = function (part) { - return part - } - - let pattern = new Test().init() - expect(pattern.config.draftOrder[0]).to.equal('back') - expect(pattern.config.draftOrder[1]).to.equal('front') - expect(pattern.config.draftOrder[2]).to.equal('crotch') - }) - - it('Should correctly resolve dependencies - issue #971 - broken version', () => { - let config = { - name: 'test', - dependencies: { front: 'back', crotch: ['front', 'back'] }, - parts: ['back', 'front', 'crotch'], - } - const Test = function (settings = false) { - Pattern.call(this, config) - return this - } - Test.prototype = Object.create(Pattern.prototype) - Test.prototype.constructor = Test - Test.prototype.draftBack = function (part) { - return part - } - Test.prototype.draftFront = function (part) { - return part - } - - let pattern = new Test().init() - expect(pattern.config.draftOrder[0]).to.equal('back') - expect(pattern.config.draftOrder[1]).to.equal('front') - expect(pattern.config.draftOrder[2]).to.equal('crotch') - }) - - it('Should correctly resolve dependencies - Handle uncovered code path', () => { - let config = { - name: 'test', - dependencies: { - front: 'side', - crotch: ['front', 'back'], - back: 1, - }, - inject: { - front: 'back', - }, - parts: ['back', 'front', 'crotch'], - } - const Test = function (settings = false) { - Pattern.call(this, config) - return this - } - Test.prototype = Object.create(Pattern.prototype) - Test.prototype.constructor = Test - Test.prototype.draftBack = function (part) { - return part - } - Test.prototype.draftFront = function (part) { - return part - } - - let pattern = new Test().init() - const deps = pattern.resolveDependencies() - expect(pattern.config.draftOrder[0]).to.equal('side') - expect(pattern.config.draftOrder[1]).to.equal('back') - expect(pattern.config.draftOrder[2]).to.equal('front') - }) - it('Should check whether created parts get the pattern context', () => { let pattern = new Pattern() let part = new pattern.Part() expect(part.context.settings).to.equal(pattern.settings) }) - it('Should correctly merge settings', () => { - let pattern = new Pattern() - let settings = { - complete: false, - only: [1, 2, 3], - margin: 5, - } - pattern.apply(settings) - expect(pattern.settings.complete).to.equal(false) - expect(pattern.settings.only[1]).to.equal(2) - expect(pattern.settings.margin).to.equal(5) - expect(pattern.settings.only.length).to.equal(3) - }) - - it('Should correctly merge settings for existing array', () => { - let pattern = new Pattern() - pattern.settings.only = [1] - let settings = { - complete: false, - only: [2, 3, 4], - margin: 5, - } - pattern.apply(settings) - expect(pattern.settings.complete).to.equal(false) - expect(pattern.settings.only.length).to.equal(4) - expect(pattern.settings.margin).to.equal(5) - }) - it('Should return all render props', () => { const front = { name: 'front', diff --git a/packages/core/tests/pattern-init.mjs b/packages/core/tests/pattern-init.mjs index 702252b3b53..59aefe8dfb0 100644 --- a/packages/core/tests/pattern-init.mjs +++ b/packages/core/tests/pattern-init.mjs @@ -181,8 +181,409 @@ describe('Pattern', () => { expect(pattern.config.draftOrder[2]).to.equal('test.partC') }) - }) + // I am aware this does too much for one unit test, but this is to simplify TDD + // we can split it up later + it('Pattern.init() should resolve nested injections', () => { + const partA = { + name: 'partA', + options: { optionA: { bool: true } }, + measurements: ['measieA'], + optionalMeasurements: ['optmeasieA'], + draft: (part) => { + const { points, Point, paths, Path } = part.shorthand() + points.a1 = new Point(1, 1) + points.a2 = new Point(11, 11) + paths.a = new Path().move(points.a1).line(points.a2) + return part + }, + } + const partB = { + name: 'partB', + from: partA, + options: { optionB: { pct: 12, min: 2, max: 20 } }, + measurements: ['measieB'], + optionalMeasurements: ['optmeasieB', 'measieA'], + draft: (part) => { + const { points, Point, paths, Path } = part.shorthand() + points.b1 = new Point(2, 2) + points.b2 = new Point(22, 22) + paths.b = new Path().move(points.b1).line(points.b2) + return part + }, + } + const partC = { + name: 'partC', + from: partB, + options: { optionC: { deg: 5, min: 0, max: 15 } }, + measurements: ['measieC'], + optionalMeasurements: ['optmeasieC', 'measieA'], + draft: (part) => { + const { points, Point, paths, Path } = part.shorthand() + points.c1 = new Point(3, 3) + points.c2 = new Point(33, 33) + paths.c = new Path().move(points.c1).line(points.c2) + return part + }, + } + const partR = { + // R for runtime, which is when this wil be attached + name: 'partR', + from: partA, + after: partC, + options: { optionR: { dflt: 'red', list: ['red', 'green', 'blue'] } }, + measurements: ['measieR'], + optionalMeasurements: ['optmeasieR', 'measieA'], + draft: (part) => { + const { points, Point, paths, Path } = part.shorthand() + points.r1 = new Point(4, 4) + points.r2 = new Point(44, 44) + paths.r = new Path().move(points.r1).line(points.r2) + return part + }, + } + const design = new Design({ parts: [partC] }) + const pattern = new design().addPart(partR).draft() + // Measurements + expect(pattern.config.measurements.length).to.equal(4) + expect(pattern.config.measurements.indexOf('measieA') === -1).to.equal(false) + expect(pattern.config.measurements.indexOf('measieB') === -1).to.equal(false) + expect(pattern.config.measurements.indexOf('measieC') === -1).to.equal(false) + expect(pattern.config.measurements.indexOf('measieR') === -1).to.equal(false) + // Optional measurements + expect(pattern.config.optionalMeasurements.length).to.equal(4) + expect(pattern.config.optionalMeasurements.indexOf('optmeasieA') === -1).to.equal(false) + expect(pattern.config.optionalMeasurements.indexOf('optmeasieB') === -1).to.equal(false) + expect(pattern.config.optionalMeasurements.indexOf('optmeasieC') === -1).to.equal(false) + expect(pattern.config.optionalMeasurements.indexOf('optmeasieR') === -1).to.equal(false) + expect(pattern.config.optionalMeasurements.indexOf('measieA') === -1).to.equal(true) + // Options + expect(pattern.config.options.optionA.bool).to.equal(true) + expect(pattern.config.options.optionB.pct).to.equal(12) + expect(pattern.config.options.optionB.min).to.equal(2) + expect(pattern.config.options.optionB.max).to.equal(20) + expect(pattern.config.options.optionC.deg).to.equal(5) + expect(pattern.config.options.optionC.min).to.equal(0) + expect(pattern.config.options.optionC.max).to.equal(15) + expect(pattern.config.options.optionR.dflt).to.equal('red') + expect(pattern.config.options.optionR.list[0]).to.equal('red') + expect(pattern.config.options.optionR.list[1]).to.equal('green') + expect(pattern.config.options.optionR.list[2]).to.equal('blue') + // Dependencies + expect(pattern.__dependencies.partB[0]).to.equal('partA') + expect(pattern.__dependencies.partC[0]).to.equal('partB') + expect(pattern.__dependencies.partR[0]).to.equal('partC') + expect(pattern.__dependencies.partR[1]).to.equal('partA') + // Inject + expect(pattern.__inject.partB).to.equal('partA') + expect(pattern.__inject.partC).to.equal('partB') + expect(pattern.__inject.partR).to.equal('partA') + // Draft order + expect(pattern.config.draftOrder[0]).to.equal('partA') + expect(pattern.config.draftOrder[1]).to.equal('partB') + expect(pattern.config.draftOrder[2]).to.equal('partC') + expect(pattern.config.draftOrder[3]).to.equal('partR') + // Points + expect(pattern.parts.partA.points.a1.x).to.equal(1) + expect(pattern.parts.partA.points.a1.y).to.equal(1) + expect(pattern.parts.partA.points.a2.x).to.equal(11) + expect(pattern.parts.partA.points.a2.y).to.equal(11) + expect(pattern.parts.partB.points.b1.x).to.equal(2) + expect(pattern.parts.partB.points.b1.y).to.equal(2) + expect(pattern.parts.partB.points.b2.x).to.equal(22) + expect(pattern.parts.partB.points.b2.y).to.equal(22) + expect(pattern.parts.partC.points.c1.x).to.equal(3) + expect(pattern.parts.partC.points.c1.y).to.equal(3) + expect(pattern.parts.partC.points.c2.x).to.equal(33) + expect(pattern.parts.partC.points.c2.y).to.equal(33) + expect(pattern.parts.partR.points.r1.x).to.equal(4) + expect(pattern.parts.partR.points.r1.y).to.equal(4) + expect(pattern.parts.partR.points.r2.x).to.equal(44) + expect(pattern.parts.partR.points.r2.y).to.equal(44) + // Paths in partA + expect(pattern.parts.partA.paths.a.ops[0].to.x).to.equal(1) + expect(pattern.parts.partA.paths.a.ops[0].to.y).to.equal(1) + expect(pattern.parts.partA.paths.a.ops[1].to.x).to.equal(11) + expect(pattern.parts.partA.paths.a.ops[1].to.y).to.equal(11) + // Paths in partB + expect(pattern.parts.partB.paths.a.ops[0].to.x).to.equal(1) + expect(pattern.parts.partB.paths.a.ops[0].to.y).to.equal(1) + expect(pattern.parts.partB.paths.a.ops[1].to.x).to.equal(11) + expect(pattern.parts.partB.paths.a.ops[1].to.y).to.equal(11) + expect(pattern.parts.partB.paths.b.ops[0].to.x).to.equal(2) + expect(pattern.parts.partB.paths.b.ops[0].to.y).to.equal(2) + expect(pattern.parts.partB.paths.b.ops[1].to.x).to.equal(22) + expect(pattern.parts.partB.paths.b.ops[1].to.y).to.equal(22) + // Paths in partC + expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1) + expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1) + expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11) + expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11) + expect(pattern.parts.partC.paths.b.ops[0].to.x).to.equal(2) + expect(pattern.parts.partC.paths.b.ops[0].to.y).to.equal(2) + expect(pattern.parts.partC.paths.b.ops[1].to.x).to.equal(22) + expect(pattern.parts.partC.paths.b.ops[1].to.y).to.equal(22) + expect(pattern.parts.partC.paths.c.ops[0].to.x).to.equal(3) + expect(pattern.parts.partC.paths.c.ops[0].to.y).to.equal(3) + expect(pattern.parts.partC.paths.c.ops[1].to.x).to.equal(33) + expect(pattern.parts.partC.paths.c.ops[1].to.y).to.equal(33) + // Paths in partR + expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1) + expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1) + expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11) + expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11) + expect(pattern.parts.partR.paths.r.ops[0].to.x).to.equal(4) + expect(pattern.parts.partR.paths.r.ops[0].to.y).to.equal(4) + expect(pattern.parts.partR.paths.r.ops[1].to.x).to.equal(44) + expect(pattern.parts.partR.paths.r.ops[1].to.y).to.equal(44) + }) + + it('Pattern.init() should resolve nested dependencies', () => { + const partA = { + name: 'partA', + options: { optionA: { bool: true } }, + measurements: ['measieA'], + optionalMeasurements: ['optmeasieA'], + draft: (part) => { + const { points, Point, paths, Path } = part.shorthand() + points.a1 = new Point(1, 1) + points.a2 = new Point(11, 11) + paths.a = new Path().move(points.a1).line(points.a2) + return part + }, + } + const partB = { + name: 'partB', + from: partA, + options: { optionB: { pct: 12, min: 2, max: 20 } }, + measurements: ['measieB'], + optionalMeasurements: ['optmeasieB', 'measieA'], + draft: (part) => { + const { points, Point, paths, Path } = part.shorthand() + points.b1 = new Point(2, 2) + points.b2 = new Point(22, 22) + paths.b = new Path().move(points.b1).line(points.b2) + return part + }, + } + const partC = { + name: 'partC', + from: partB, + options: { optionC: { deg: 5, min: 0, max: 15 } }, + measurements: ['measieC'], + optionalMeasurements: ['optmeasieC', 'measieA'], + draft: (part) => { + const { points, Point, paths, Path } = part.shorthand() + points.c1 = new Point(3, 3) + points.c2 = new Point(33, 33) + paths.c = new Path().move(points.c1).line(points.c2) + return part + }, + } + const partD = { + name: 'partD', + after: partC, + options: { optionD: { dflt: 'red', list: ['red', 'green', 'blue'] } }, + measurements: ['measieD'], + optionalMeasurements: ['optmeasieD', 'measieA'], + draft: (part) => { + const { points, Point, paths, Path } = part.shorthand() + points.d1 = new Point(4, 4) + points.d2 = new Point(44, 44) + paths.d = new Path().move(points.d1).line(points.d2) + return part + }, + } + const design = new Design({ parts: [partD] }) + const pattern = new design().draft() + // Measurements + expect(pattern.config.measurements.length).to.equal(4) + expect(pattern.config.measurements.indexOf('measieA') === -1).to.equal(false) + expect(pattern.config.measurements.indexOf('measieB') === -1).to.equal(false) + expect(pattern.config.measurements.indexOf('measieC') === -1).to.equal(false) + expect(pattern.config.measurements.indexOf('measieD') === -1).to.equal(false) + // Optional measurements + expect(pattern.config.optionalMeasurements.length).to.equal(4) + expect(pattern.config.optionalMeasurements.indexOf('optmeasieA') === -1).to.equal(false) + expect(pattern.config.optionalMeasurements.indexOf('optmeasieB') === -1).to.equal(false) + expect(pattern.config.optionalMeasurements.indexOf('optmeasieC') === -1).to.equal(false) + expect(pattern.config.optionalMeasurements.indexOf('optmeasieD') === -1).to.equal(false) + expect(pattern.config.optionalMeasurements.indexOf('measieA') === -1).to.equal(true) + // Options + expect(pattern.config.options.optionA.bool).to.equal(true) + expect(pattern.config.options.optionB.pct).to.equal(12) + expect(pattern.config.options.optionB.min).to.equal(2) + expect(pattern.config.options.optionB.max).to.equal(20) + expect(pattern.config.options.optionC.deg).to.equal(5) + expect(pattern.config.options.optionC.min).to.equal(0) + expect(pattern.config.options.optionC.max).to.equal(15) + expect(pattern.config.options.optionD.dflt).to.equal('red') + expect(pattern.config.options.optionD.list[0]).to.equal('red') + expect(pattern.config.options.optionD.list[1]).to.equal('green') + expect(pattern.config.options.optionD.list[2]).to.equal('blue') + // Dependencies + expect(pattern.__dependencies.partB[0]).to.equal('partA') + expect(pattern.__dependencies.partC[0]).to.equal('partB') + expect(pattern.__dependencies.partD[0]).to.equal('partC') + // Inject + expect(pattern.__inject.partB).to.equal('partA') + expect(pattern.__inject.partC).to.equal('partB') + // Draft order + expect(pattern.config.draftOrder[0]).to.equal('partA') + expect(pattern.config.draftOrder[1]).to.equal('partB') + expect(pattern.config.draftOrder[2]).to.equal('partC') + expect(pattern.config.draftOrder[3]).to.equal('partD') + // Points + expect(pattern.parts.partA.points.a1.x).to.equal(1) + expect(pattern.parts.partA.points.a1.y).to.equal(1) + expect(pattern.parts.partA.points.a2.x).to.equal(11) + expect(pattern.parts.partA.points.a2.y).to.equal(11) + expect(pattern.parts.partB.points.b1.x).to.equal(2) + expect(pattern.parts.partB.points.b1.y).to.equal(2) + expect(pattern.parts.partB.points.b2.x).to.equal(22) + expect(pattern.parts.partB.points.b2.y).to.equal(22) + expect(pattern.parts.partC.points.c1.x).to.equal(3) + expect(pattern.parts.partC.points.c1.y).to.equal(3) + expect(pattern.parts.partC.points.c2.x).to.equal(33) + expect(pattern.parts.partC.points.c2.y).to.equal(33) + expect(pattern.parts.partD.points.d1.x).to.equal(4) + expect(pattern.parts.partD.points.d1.y).to.equal(4) + expect(pattern.parts.partD.points.d2.x).to.equal(44) + expect(pattern.parts.partD.points.d2.y).to.equal(44) + // Paths in partA + expect(pattern.parts.partA.paths.a.ops[0].to.x).to.equal(1) + expect(pattern.parts.partA.paths.a.ops[0].to.y).to.equal(1) + expect(pattern.parts.partA.paths.a.ops[1].to.x).to.equal(11) + expect(pattern.parts.partA.paths.a.ops[1].to.y).to.equal(11) + // Paths in partB + expect(pattern.parts.partB.paths.a.ops[0].to.x).to.equal(1) + expect(pattern.parts.partB.paths.a.ops[0].to.y).to.equal(1) + expect(pattern.parts.partB.paths.a.ops[1].to.x).to.equal(11) + expect(pattern.parts.partB.paths.a.ops[1].to.y).to.equal(11) + expect(pattern.parts.partB.paths.b.ops[0].to.x).to.equal(2) + expect(pattern.parts.partB.paths.b.ops[0].to.y).to.equal(2) + expect(pattern.parts.partB.paths.b.ops[1].to.x).to.equal(22) + expect(pattern.parts.partB.paths.b.ops[1].to.y).to.equal(22) + // Paths in partC + expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1) + expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1) + expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11) + expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11) + expect(pattern.parts.partC.paths.b.ops[0].to.x).to.equal(2) + expect(pattern.parts.partC.paths.b.ops[0].to.y).to.equal(2) + expect(pattern.parts.partC.paths.b.ops[1].to.x).to.equal(22) + expect(pattern.parts.partC.paths.b.ops[1].to.y).to.equal(22) + expect(pattern.parts.partC.paths.c.ops[0].to.x).to.equal(3) + expect(pattern.parts.partC.paths.c.ops[0].to.y).to.equal(3) + expect(pattern.parts.partC.paths.c.ops[1].to.x).to.equal(33) + expect(pattern.parts.partC.paths.c.ops[1].to.y).to.equal(33) + // Paths in partR + expect(pattern.parts.partD.paths.d.ops[0].to.x).to.equal(4) + expect(pattern.parts.partD.paths.d.ops[0].to.y).to.equal(4) + expect(pattern.parts.partD.paths.d.ops[1].to.x).to.equal(44) + expect(pattern.parts.partD.paths.d.ops[1].to.y).to.equal(44) + }) + + it('Pattern.init() should load a single plugin', () => { + const plugin = { + name: 'example', + version: 1, + hooks: { + preRender: function (svg, attributes) { + svg.attributes.add('freesewing:plugin-example', version) + }, + }, + } + const part = { + name: 'test.part', + plugins: plugin, + draft: (part) => part, + } + const design = new Design({ parts: [part] }) + const pattern = new design() + pattern.init() + expect(pattern.hooks.preRender.length).to.equal(1) + }) + + it("Pattern.init() should load array of plugins", () => { + const plugin1 = { + name: "example1", + version: 1, + hooks: { + preRender: function(svg, attributes) { + svg.attributes.add("freesewing:plugin-example1", version); + } + } + }; + const plugin2 = { + name: "example2", + version: 2, + hooks: { + preRender: function(svg, attributes) { + svg.attributes.add("freesewing:plugin-example2", version); + } + } + }; + + const design = new Design( { plugins: [plugin1, plugin2] }); + const pattern = new design(); + pattern.init() + expect(pattern.hooks.preRender.length).to.equal(2); + }); + it("Pattern.init() should load conditional plugin", () => { + const plugin = { + name: "example", + version: 1, + hooks: { + preRender: function(svg, attributes) { + svg.attributes.add("freesewing:plugin-example", version); + } + } + }; + const condition = () => true + const design = new Design({ plugins: [ { plugin, condition } ] }); + const pattern = new design(); + pattern.init() + expect(pattern.hooks.preRender.length).to.equal(1); + }); + + it("Pattern.init() should not load conditional plugin", () => { + const plugin = { + name: "example", + version: 1, + hooks: { + preRender: function(svg, attributes) { + svg.attributes.add("freesewing:plugin-example", version); + } + } + }; + const condition = () => false + const design = new Design({ plugins: { plugin, condition } }); + const pattern = new design(); + expect(pattern.hooks.preRender.length).to.equal(0); + }); + + it("Pattern.init() should load multiple conditional plugins", () => { + const plugin = { + name: "example", + version: 1, + hooks: { + preRender: function(svg, attributes) { + svg.attributes.add("freesewing:plugin-example", version); + } + } + }; + const condition1 = () => true + const condition2 = () => false + const design = new Design({ plugins: [ + { plugin, condition: condition1 }, + { plugin, condition: condition2 }, + ]}); + const pattern = new design(); + pattern.init() + expect(pattern.hooks.preRender.length).to.equal(1); + }) + }) describe('Pattern.settings', () => { @@ -235,6 +636,22 @@ describe('Pattern', () => { it('Pattern settings should contain bool options', () => { expect(pattern.settings.options.bool).to.equal(false) }) - }) -}) + it('Pattern should throw an error for an unknown option', () => { + const part = { + name: 'test.part', + options: { unknown: { foo: 30 } }, + draft: () => { } + } + const Pattern = new Design({ + data: { name: 'test', version: '1.2.3' }, + parts: [ part ] + }) + const pattern = new Pattern() + expect(() => pattern.init()).to.throw() + }) + + }) + + +}) diff --git a/packages/core/tests/pattern.test.mjs b/packages/core/tests/pattern.test.mjs index 1697e94b9d7..f52be094fdf 100644 --- a/packages/core/tests/pattern.test.mjs +++ b/packages/core/tests/pattern.test.mjs @@ -4,106 +4,6 @@ import { round, Pattern, Design, pctBasedOn } from '../src/index.mjs' const expect = chai.expect describe('Pattern', () => { - it('Pattern constructor should initialize object', () => { - let pattern = new Pattern({ - foo: 'bar', - options: { - constant: 2, - percentage: { pct: 30, min: 0, max: 100 }, - }, - }) - expect(pattern.width).to.equal(0) - expect(pattern.height).to.equal(0) - expect(pattern.settings.complete).to.equal(true) - expect(pattern.parts).to.eql({}) - expect(pattern.settings.units).to.equal('metric') - expect(pattern.config.foo).to.equal('bar') - expect(pattern.settings.options.constant).to.equal(2) - expect(pattern.settings.options.percentage).to.equal(0.3) - }) - - it('Should load percentage options', () => { - let pattern = new Pattern({ - options: { - test: { pct: 30 }, - }, - }) - expect(pattern.settings.options.test).to.equal(0.3) - }) - - it('Should load millimeter options', () => { - let pattern = new Pattern({ - options: { - test: { mm: 30 }, - }, - }) - expect(pattern.settings.options.test).to.equal(30) - }) - - it('Should load degree options', () => { - let pattern = new Pattern({ - options: { - test: { deg: 15 }, - }, - }) - expect(pattern.settings.options.test).to.equal(15) - }) - - it('Should load an array option', () => { - let pattern = new Pattern({ - options: { - test: { dflt: 'foo' }, - }, - }) - expect(pattern.settings.options.test).to.equal('foo') - }) - - it('Should load a count option', () => { - let pattern = new Pattern({ - options: { - test: { count: 3 }, - }, - }) - expect(pattern.settings.options.test).to.equal(3) - }) - - it('Should load a boolean option', () => { - let pattern = new Pattern({ - options: { - test1: { bool: false }, - test2: { bool: true }, - }, - }) - expect(pattern.settings.options.test1).to.be.false - expect(pattern.settings.options.test2).to.be.true - }) - - it('Should throw an error for an unknown option', () => { - expect( - () => - new Pattern({ - options: { - test: { foo: 'bar' }, - }, - }) - ).to.throw() - }) - - it('Should merge settings with default settings', () => { - let pattern = new Pattern() - let settings = { - foo: 'bar', - deep: { - free: 'ze', - }, - } - pattern.apply(settings) - expect(pattern.settings.foo).to.equal('bar') - expect(pattern.settings.locale).to.equal('en') - expect(pattern.settings.margin).to.equal(2) - expect(pattern.settings.idPrefix).to.equal('fs-') - expect(pattern.settings.deep.free).to.equal('ze') - }) it('Should draft according to settings', () => { let count = 0