diff --git a/config/software/plugins.json b/config/software/plugins.json
index 202ab1dbb30..267d4ce7eb1 100644
--- a/config/software/plugins.json
+++ b/config/software/plugins.json
@@ -4,6 +4,7 @@
"plugin-bundle": "An umbrella package of 8 essential FreeSewing build-time plugins",
"plugin-bust": "A FreeSewing plugin that helps with bust-adjusting menswear patterns",
"plugin-buttons": "A FreeSewing plugin that provides button, buttonhole, and snap snippets",
+ "plugin-cutlist": "A FreeSewing plugin to store data regarding a pattern's cutlist",
"plugin-cutonfold": "A FreeSewing plugin to add cut-on-fold indicators on your patterns",
"plugin-dimension": "A FreeSewing plugin to add dimensions to your (paperless) pattern",
"plugin-flip": "A FreeSewing plugin to flip parts horizontally",
diff --git a/packages/core/src/part.mjs b/packages/core/src/part.mjs
index 212235ffaba..c3693cc1be3 100644
--- a/packages/core/src/part.mjs
+++ b/packages/core/src/part.mjs
@@ -354,62 +354,6 @@ Part.prototype.generateTransform = function(transforms) {
}
}
-/** Chainable way to add the cut info */
-Part.prototype.addCut = function (cut=2, material='fabric', identical=false) {
- if (cut === false) {
- if (this.cut.materials[material]) delete this.cut.materials[material]
- else this.context.raise.warning(`Tried to remove a material that is not set`)
- return this
- }
- if (typeof this.cut.materials[material] === 'undefined') this.cut.materials[material] = {}
- if (!(Number.isInteger(cut) && cut > -1)) {
- this.context.raise.error(`Tried to set cut to a value that is not a positive integer`)
- return this
- }
- if (typeof material !== 'string') {
- this.context.raise.warning(`Tried to set material to a value that is not a string`)
- return this
- }
- this.cut.materials[material].cut = cut
- this.cut.materials[material].identical = identical
-
- return this
-}
-
-/** Chainable way to remove (some) cut info */
-Part.prototype.removeCut = function (material=false) {
- return this.addCut(false, material)
-}
-
-/** Chainable way to add the grain info */
-Part.prototype.setGrain = function (grain=false) {
- if (grain === false) {
- this.cut.grain = false
- return this
- }
- if (typeof grain !== 'number') {
- this.context.raise.error('Called part.setGrain() with a value that is not a number')
- return this
- }
- this.cut.grain = grain
-
- return this
-}
-
-/** Chainable way to add the cutOnFold info */
-Part.prototype.setCutOnFold = function (p1, p2) {
- if (p1 === false && typeof p2 === 'undefined') {
- delete this.cut.cutOnFold
- return this
- }
- if (p1 instanceof Point && p2 instanceof Point) {
- this.cut.cutOnFold = [p1, p2]
- }
- else this.context.raise.error('Called part.setCutOnFold() but at least one parameter is not a Point instance')
-
- return this
-}
-
Part.prototype.isEmpty = function() {
if (Object.keys(this.snippets).length > 0) return false
diff --git a/packages/core/tests/part.test.mjs b/packages/core/tests/part.test.mjs
index 7fe51cfec79..f39d9bdc6f8 100644
--- a/packages/core/tests/part.test.mjs
+++ b/packages/core/tests/part.test.mjs
@@ -276,132 +276,6 @@ describe('Part', () => {
expect(part.attributes.list.transform[0]).to.equal('translate(10 20)')
});
-
- it("Should add the part cut", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut(4, 'fabric', true)
- expect(part.cut.materials.fabric.cut).to.equal(4)
- expect(part.cut.materials.fabric.identical).to.equal(true)
- part.addCut()
- expect(part.cut.materials.fabric.cut).to.equal(2)
- expect(part.cut.materials.fabric.identical).to.equal(false)
- });
-
- it("Should generate an error if cut is not a number", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut('a', 'fabric', true)
- expect(pattern.events.error.length).to.equal(1)
- expect(pattern.events.error[0]).to.equal('Tried to set cut to a value that is not a positive integer')
- });
-
- it("Should generate an warning if material is not a string", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut(3, 4)
- expect(pattern.events.warning.length).to.equal(1)
- expect(pattern.events.warning[0]).to.equal('Tried to set material to a value that is not a string')
- });
-
- it("Should generate an error when removing a material that is not set (through addCut)", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut(4, 'fabric', true)
- part.addCut(false, 'lining')
- expect(pattern.events.warning.length).to.equal(1)
- expect(pattern.events.warning[0]).to.equal('Tried to remove a material that is not set')
- });
-
- it("Should remove the part cut through addCut", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut(4, 'fabric', true)
- part.addCut(false, 'fabric')
- expect(typeof part.cut.materials.fabric).to.equal('undefined')
- });
-
- it("Should remove the part cut through removeCut", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut(4, 'fabric', true)
- part.removeCut('fabric')
- expect(typeof part.cut.materials.fabric).to.equal('undefined')
- });
-
- it("Should generate an error when removing a material that is not set (through removeCut)", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut(4, 'fabric', true)
- part.removeCut('lining')
- expect(pattern.events.warning.length).to.equal(1)
- expect(pattern.events.warning[0]).to.equal('Tried to remove a material that is not set')
- });
-
- it("Should generate an error when removing a material that is not a string (through removeCut)", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut(4, 'fabric', true)
- part.removeCut(23)
- expect(pattern.events.warning.length).to.equal(1)
- expect(pattern.events.warning[0]).to.equal('Tried to remove a material that is not set')
- });
-
- it("Should generate a warning when calling removeCut without parameters", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.addCut(4, 'fabric', true)
- part.removeCut()
- expect(pattern.events.warning.length).to.equal(1)
- expect(pattern.events.warning[0]).to.equal('Tried to remove a material that is not set')
- });
-
- it("Should set the part grainline", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- expect(part.cut.grain).to.equal(90)
- part.setGrain(123)
- expect(part.cut.grain).to.equal(123)
- });
-
- it("Should raise a warning when calling part.setGrain() without any parameters", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.setGrain()
- expect(part.cut.grain).to.equal(false)
- });
-
- it("Should raise an error when calling part.setGrain() with a value that is not a number", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- part.setGrain('a')
- expect(part.cut.grain).to.equal(90)
- expect(pattern.events.error.length).to.equal(1)
- expect(pattern.events.error[0]).to.equal('Called part.setGrain() with a value that is not a number')
- });
-
- it("Should set and then remove the cutOnFold", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- const { Point } = part.shorthand()
- part.setCutOnFold(new Point(2,3), new Point(100,200))
- expect(part.cut.cutOnFold[0].x).to.equal(2)
- expect(part.cut.cutOnFold[0].y).to.equal(3)
- expect(part.cut.cutOnFold[1].x).to.equal(100)
- expect(part.cut.cutOnFold[1].y).to.equal(200)
- part.setCutOnFold(false)
- expect(typeof part.cut.cutOnFold).to.equal('undefined')
- });
-
- it("Should raise an error when setting the cutOnFold with a non-Point value", () => {
- let pattern = new Pattern();
- let part = new pattern.Part();
- const { Point } = part.shorthand()
- part.setCutOnFold(new Point(2,3), 12)
- expect(pattern.events.error.length).to.equal(1)
- expect(pattern.events.error[0]).to.equal('Called part.setCutOnFold() but at least one parameter is not a Point instance')
- });
-
describe('isEmpty', () => {
it("Should return true if the part has no paths or snippets", () => {
let pattern = new Pattern();
diff --git a/plugins/plugin-cutlist/CHANGELOG.md b/plugins/plugin-cutlist/CHANGELOG.md
new file mode 100644
index 00000000000..ab19f198610
--- /dev/null
+++ b/plugins/plugin-cutlist/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Change log for: @freesewing/plugin-cutlist
+
+
+
+This is the **initial release**, and the start of this change log.
+
+> Prior to version 2, FreeSewing was not a JavaScript project.
+> As such, that history is out of scope for this change log.
+
diff --git a/plugins/plugin-cutlist/README.md b/plugins/plugin-cutlist/README.md
new file mode 100644
index 00000000000..4ca4339fd03
--- /dev/null
+++ b/plugins/plugin-cutlist/README.md
@@ -0,0 +1,287 @@
+
+
+
+
+
+
+
+
+
+
+
+
+# @freesewing/plugin-cutlist
+
+A FreeSewing plugin to store a pattern's cutlist
+
+
+
+
+> #### Note: Version 3 is a work in progress
+>
+> We are working on a new major version (v3) but it is not ready for prime-time.
+> For production use, please refer to our v2 packages (the `latest` on NPM)
+> or [the `v2` branch in our monorepo](https://github.com/freesewing/freesewing/tree/v2).
+
+## What am I looking at? π€
+
+This repository is our *monorepo* holding all our NPM designs, plugins, other NPM packages, and (web)sites.
+
+This folder holds: @freesewing/plugin-cutlist
+
+If you're not entirely sure what to do or how to start, type this command:
+
+```
+npm run tips
+```
+
+> If you don't want to set up a dev environment, you can run it in your browser:
+>
+> [](https://gitpod.io/#https://github.com/freesewing/freesewing)
+>
+> We recommend that you fork our repository and then
+> put `gitpod.io/# to start up a browser-based dev environment of your own.
+
+## About FreeSewing π
+
+Where the world of makers and developers collide, that's where you'll find FreeSewing.
+
+If you're a maker, checkout [freesewing.org](https://freesewing.org/) where you can generate
+our sewing patterns adapted to your measurements.
+
+If you're a developer, our documentation is on [freesewing.dev](https://freesewing.dev/).
+Our [core library](https://freesewing.dev/reference/api/) is a *batteries-included* toolbox
+for parametric design of sewing patterns. But we also provide a range
+of [plugins](https://freesewing.dev/reference/plugins/) that further extend the
+functionality of the platform.
+
+If you have NodeJS installed, you can try it right now by running:
+
+```bash
+npx create-freesewing-pattern
+```
+
+Or, consult our getting started guides
+for [Linux](https://freesewing.dev/tutorials/getting-started-linux/),
+[MacOS](https://freesewing.dev/tutorials/getting-started-mac/),
+or [Windows](https://freesewing.dev/tutorials/getting-started-windows/).
+
+We also have a [pattern design tutorial](https://freesewing.dev/tutorials/pattern-design/) that
+walks you through your first parametric design,
+and [a friendly community](https://freesewing.org/community/where/) with
+people who can help you when you get stuck.
+
+## Support FreeSewing: Become a patron π₯°
+
+FreeSewing is an open source project run by a community,
+and financially supported by our patrons.
+
+If you feel what we do is worthwhile, and you can spend a few coind without
+hardship, then you should [join us and become a patron](https://freesewing.org/community/join).
+
+## Links π©βπ»
+
+ - π» Makers website: [freesewing.org](https://freesewing.org)
+ - π» Developers website: [freesewing.dev](https://freesewing.dev)
+ - π¬ Chat: On Discord via [discord.freesewing.org](https://discord.freesewing.org/)
+ - β
Todo list/Kanban board: On Github via [todo.freesewing.org](https://todo.freesewing.org/)
+ - π¦ Twitter: [@freesewing_org](https://twitter.com/freesewing_org)
+ - π· Instagram: [@freesewing_org](https://instagram.com/freesewing_org)
+
+## License: MIT π€
+
+Β© [Joost De Cock](https://github.com/joostdecock).
+See [the license file](https://github.com/freesewing/freesewing/blob/develop/LICENSE) for details.
+
+## Where to get help π€―
+
+Our [chatrooms on Discord](https://chat.freesewing.org/) are the best place to ask questions,
+share your feedback, or just hang out.
+
+If you want to report a problem, please [create an issue](https://github.com/freesewing/freesewing/issues/new).
+
+
+
+## Contributors β¨
+
+Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+
+
+
+
+
+
+
+
+
+
+
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
+
diff --git a/plugins/plugin-cutlist/build.mjs b/plugins/plugin-cutlist/build.mjs
new file mode 100644
index 00000000000..c5581c1e4a2
--- /dev/null
+++ b/plugins/plugin-cutlist/build.mjs
@@ -0,0 +1,50 @@
+/* This script will build the package with esbuild */
+import esbuild from 'esbuild'
+import pkg from './package.json' assert { type: 'json' }
+
+// Create banner based on package info
+const banner = `/**
+ * ${pkg.name} | v${pkg.version}
+ * ${pkg.description}
+ * (c) ${new Date().getFullYear()} ${pkg.author}
+ * @license ${pkg.license}
+ */`
+
+// Shared esbuild options
+const options = {
+ banner: { js: banner },
+ bundle: true,
+ entryPoints: ['src/index.mjs'],
+ format: 'esm',
+ outfile: 'dist/index.mjs',
+ external: ["@freesewing"],
+ metafile: process.env.VERBOSE ? true : false,
+ minify: process.env.NO_MINIFY ? false : true,
+ sourcemap: true,
+}
+
+// Let esbuild generate the build
+let result
+(async () => {
+ result = await esbuild.build(options).catch(() => process.exit(1))
+
+ if (process.env.VERBOSE) {
+ const info = await esbuild.analyzeMetafile(result.metafile)
+ console.log(info)
+ }
+
+ // Also build a version that has all dependencies bundled
+ // This makes it easy to run tests
+ await esbuild
+ .build({
+ ...options,
+ minify: false,
+ keepNames: true,
+ sourcemap: false,
+ outfile: 'tests/dist/index.mjs',
+ format: 'esm',
+ external: [],
+ })
+ .catch(() => process.exit(1))
+
+})()
diff --git a/plugins/plugin-cutlist/data.mjs b/plugins/plugin-cutlist/data.mjs
new file mode 100644
index 00000000000..9d228c3bd8d
--- /dev/null
+++ b/plugins/plugin-cutlist/data.mjs
@@ -0,0 +1,4 @@
+// This file is auto-generated | All changes you make will be overwritten.
+export const name = "@freesewing/plugin-cutlist"
+export const version = "3.0.0-alpha.0"
+export const data = { name, version }
diff --git a/plugins/plugin-cutlist/package.json b/plugins/plugin-cutlist/package.json
new file mode 100644
index 00000000000..a4d1a9300df
--- /dev/null
+++ b/plugins/plugin-cutlist/package.json
@@ -0,0 +1,65 @@
+{
+ "name": "@freesewing/plugin-cutlist",
+ "version": "3.0.0-alpha.0",
+ "description": "A FreeSewing plugin to store a pattern's cutlist",
+ "author": "Joost De Cock (https://github.com/joostdecock)",
+ "homepage": "https://freesewing.org/",
+ "repository": "github:freesewing/freesewing",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/freesewing/freesewing/issues"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://freesewing.org/patrons/join"
+ },
+ "keywords": [
+ "freesewing",
+ "plugin",
+ "sewing pattern",
+ "sewing",
+ "design",
+ "parametric design",
+ "made to measure",
+ "diy",
+ "fashion"
+ ],
+ "type": "module",
+ "module": "dist/index.mjs",
+ "exports": {
+ ".": "./dist/index.mjs"
+ },
+ "scripts": {
+ "build": "node build.mjs",
+ "clean": "rimraf dist",
+ "mbuild": "NO_MINIFY=1 node build.mjs",
+ "symlink": "mkdir -p ./node_modules/@freesewing && cd ./node_modules/@freesewing && ln -s -f ../../../* . && cd -",
+ "test": "npx mocha tests/*.test.mjs",
+ "vbuild": "VERBOSE=1 node build.mjs",
+ "lab": "cd ../../sites/lab && yarn start",
+ "tips": "node ../../scripts/help.mjs",
+ "prettier": "npx prettier --write 'src/*.mjs' 'tests/*.mjs'",
+ "testci": "npx mocha tests/*.test.mjs --reporter ../../tests/reporters/terse.js",
+ "cibuild_step1": "node build.mjs"
+ },
+ "peerDependencies": {
+ "@freesewing/core": "^3.0.0-alpha.0"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "mocha": "^10.0.0",
+ "chai": "^4.2.0"
+ },
+ "files": [
+ "dist/*",
+ "README.md"
+ ],
+ "publishConfig": {
+ "access": "public",
+ "tag": "next"
+ },
+ "engines": {
+ "node": ">=16.0.0",
+ "npm": ">=8"
+ }
+}
diff --git a/plugins/plugin-cutlist/src/index.mjs b/plugins/plugin-cutlist/src/index.mjs
new file mode 100644
index 00000000000..428fca79fb9
--- /dev/null
+++ b/plugins/plugin-cutlist/src/index.mjs
@@ -0,0 +1,71 @@
+import { name, version } from '../package.json'
+import { Point } from '@freesewing/core'
+
+export const plugin = {
+ name,
+ version,
+ store: [
+ ['addCut', addCut],
+ ['removeCut', removeCut],
+ ['setGrain', setGrain],
+ ['setCutOnFold', setCutOnFold],
+ ]
+}
+
+// More specifically named exports
+export const cutlistPlugin = plugin
+export const pluginCutlist = plugin
+
+/** Method to add the cut info */
+function addCut(store, partName, cut=2, material='fabric', identical=false) {
+ if (cut === false) {
+ if (material === false) store.unset(['cutlist', partName, 'materials'])
+ else store.unset(['cutlist', partName, 'materials', material])
+ return store
+ }
+ if (!(Number.isInteger(cut) && cut > -1)) {
+ store.log.error(`Tried to set cut to a value that is not a positive integer`)
+ return store
+ }
+ if (typeof material !== 'string') {
+ store.log.warning(`Tried to set material to a value that is not a string`)
+ return store
+ }
+ const path = ['cutlist', partName, 'materials', material]
+ store.set([...path, 'cut'], cut)
+ store.set([...path, 'identical'], identical)
+
+ return store
+}
+
+/** Method to remove the cut info */
+function removeCut(store, partName, material=false) {
+ return addCut(store, partName, false, material)
+}
+
+/** Method to add the grain info */
+function setGrain(store, partName, grain=false) {
+ const path = ['cutlist', partName, 'grain']
+ if (grain === false) return store.unset(path)
+ if (typeof grain !== 'number') {
+ store.log.error('Called part.setGrain() with a value that is not a number')
+ return store
+ }
+ return store.set(path, grain)
+
+ return store
+}
+
+/** Method to add the cutOnFold info */
+function setCutOnFold(store, partName, p1, p2) {
+ const path = ['cutlist', partName, 'cutOnFold']
+ if (p1 === false && typeof p2 === 'undefined') {
+ return store.unset(path)
+ }
+ if (p1 instanceof Point && p2 instanceof Point) {
+ store.set(path, [p1, p2])
+ }
+ else store.log.error('Called part.setCutOnFold() but at least one parameter is not a Point instance')
+
+ return store
+}
diff --git a/plugins/plugin-cutlist/tests/plugin.test.mjs b/plugins/plugin-cutlist/tests/plugin.test.mjs
new file mode 100644
index 00000000000..9ff7a086c86
--- /dev/null
+++ b/plugins/plugin-cutlist/tests/plugin.test.mjs
@@ -0,0 +1,170 @@
+import chai from 'chai'
+import { Design, Point } from '@freesewing/core'
+import { plugin } from '../dist/index.mjs'
+
+const expect = chai.expect
+
+describe('Cutlist Plugin Tests', () => {
+
+ it("Should make methods available through part.shorthand()", () => {
+ let methods
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { addCut, removeCut, setGrain, setCutOnFold } = part.shorthand()
+ methods = { addCut, removeCut, setGrain, setCutOnFold }
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(typeof methods.addCut).to.equal("function")
+ expect(typeof methods.removeCut).to.equal("function")
+ expect(typeof methods.setGrain).to.equal("function")
+ expect(typeof methods.setCutOnFold).to.equal("function")
+ })
+
+ it("Should handle addCut() with defaults", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { addCut } = part.shorthand()
+ addCut()
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(pattern.store.cutlist.example_part.materials.fabric.cut).to.equal(2)
+ expect(pattern.store.cutlist.example_part.materials.fabric.identical).to.equal(false)
+ })
+
+ it("Should handle addCut() with non-defaults", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { addCut } = part.shorthand()
+ addCut(3, 'lining', true)
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(pattern.store.cutlist.example_part.materials.lining.cut).to.equal(3)
+ expect(pattern.store.cutlist.example_part.materials.lining.identical).to.equal(true)
+ })
+
+ it("Should remove cut info via addCut(false)", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { addCut } = part.shorthand()
+ addCut(2, 'fabric')
+ addCut(4, 'lining', true)
+ addCut(false, 'lining')
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(typeof pattern.store.cutlist.example_part.materials.lining).to.equal('undefined')
+ })
+
+ it("Should remove cut info for a material via removeCut()", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { addCut, removeCut } = part.shorthand()
+ addCut(2, 'fabric')
+ addCut(4, 'lining', true)
+ removeCut('lining')
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(typeof pattern.store.cutlist.example_part.materials.lining).to.equal('undefined')
+ expect(pattern.store.cutlist.example_part.materials.fabric.cut).to.equal(2)
+ })
+
+ it("Should remove cut info for all materials via removeCut(true)", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { addCut, removeCut } = part.shorthand()
+ addCut(2, 'fabric')
+ addCut(4, 'lining', true)
+ removeCut()
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(typeof pattern.store.cutlist.example_part.materials).to.equal('undefined')
+ })
+
+ it("Should set the grain via setGrain()", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { setGrain } = part.shorthand()
+ setGrain(45)
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(pattern.store.cutlist.example_part.grain).to.equal(45)
+ })
+
+ it("Should remove the grain via setGrain(false)", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { setGrain } = part.shorthand()
+ setGrain(45)
+ setGrain(false)
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(typeof pattern.store.cutlist.example_part.grain).to.equal('undefined')
+ })
+
+ it("Should set the cutOnFold via setCutOnFold(p1, p2)", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { Point, setCutOnFold } = part.shorthand()
+ try {
+ setCutOnFold(new Point(2,2), new Point(200,200))
+ } catch(err){ console.log(err)}
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(pattern.store.cutlist.example_part.cutOnFold[0].x).to.equal(2)
+ expect(pattern.store.cutlist.example_part.cutOnFold[0].y).to.equal(2)
+ expect(pattern.store.cutlist.example_part.cutOnFold[1].x).to.equal(200)
+ expect(pattern.store.cutlist.example_part.cutOnFold[1].y).to.equal(200)
+ })
+
+ it("Should removet the cutOnFold via setCutOnFold(false)", () => {
+ const part = {
+ name: 'example_part',
+ draft: part => {
+ const { Point, setCutOnFold } = part.shorthand()
+ try {
+ setCutOnFold(new Point(2,2), new Point(200,200))
+ setCutOnFold(false)
+ } catch(err){ console.log(err)}
+ }
+ }
+ const Test = new Design({plugins: [plugin], parts: [ part ]})
+ const pattern = new Test()
+ pattern.draft()
+ expect(typeof pattern.store.cutlist.example_part.cutOnFold).to.equal('undefined')
+ })
+})
diff --git a/plugins/plugin-cutlist/tests/shared.test.mjs b/plugins/plugin-cutlist/tests/shared.test.mjs
new file mode 100644
index 00000000000..fdf846844b0
--- /dev/null
+++ b/plugins/plugin-cutlist/tests/shared.test.mjs
@@ -0,0 +1,7 @@
+// This file is auto-generated | Any changes you make will be overwritten.
+import { plugin } from './dist/index.mjs'
+import { sharedPluginTests } from '../../../tests/plugins/shared.mjs'
+
+// Run shared tests
+sharedPluginTests(plugin)
+