The slot list comes from the project file, which is sometimes wrong and sometimes not what you intend to print. "Edit slots" turns it into fields: material and colour per slot, with slots you can add or drop. Saved slots are marked as yours and shown with a "your list" badge, and "Use the project's list" throws the override away and re-reads the 3MF. Edits have to outlive the background reader, which is the part that would fail silently: the reader only picks up models with no status, and reread() now skips 'edited' rather than nulling everything. Verified on a real model — the edit survived a reread plus a full worker pass, and the revert restored the project's own four slots exactly. Input cleaning moved out of the route into lib/filaments.js so it can be tested: a slot with no material is dropped, and a colour that isn't a proper hex becomes "any colour of that material" rather than being rejected. Not verified: none of this has been seen rendered — the local preview browser has no session. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
107 lines
5 KiB
JavaScript
107 lines
5 KiB
JavaScript
const test = require('node:test');
|
||
const assert = require('node:assert/strict');
|
||
const filaments = require('../lib/filaments');
|
||
|
||
const spool = (material, hex, name) => ({ id: hex, material, colour_hex: hex, colour: name || hex, remaining_g: 500 });
|
||
|
||
// The model that started this: four PLA slots, one spool of Jade White.
|
||
const pooh = [
|
||
{ type: 'PLA', colour: '#000000' },
|
||
{ type: 'PLA', colour: '#586579' },
|
||
{ type: 'PLA', colour: '#8E9089' },
|
||
{ type: 'PLA', colour: '#FFFFFF' },
|
||
];
|
||
|
||
test('a four-colour print needs those colours, not four spools of anything', () => {
|
||
const shelf = [spool('PLA', '#ffffff', 'Jade White')];
|
||
const slots = filaments.assignSpools(pooh, shelf);
|
||
assert.deepEqual(slots.map((s) => s.filled), [false, false, false, true]);
|
||
assert.equal(slots[3].by, 'Jade White');
|
||
assert.equal(filaments.isPrintable(pooh, shelf), false);
|
||
assert.equal(filaments.shortfall(pooh, shelf).length, 3);
|
||
|
||
// Four white spools still can't print it: the other three colours are missing.
|
||
const fourWhite = ['a', 'b', 'c', 'd'].map((k) => ({ ...spool('PLA', '#ffffff', 'Jade White'), id: k }));
|
||
assert.equal(filaments.isPrintable(pooh, fourWhite), false);
|
||
});
|
||
|
||
test('with every colour on the shelf it prints', () => {
|
||
const shelf = [spool('PLA', '#000000', 'Black'), spool('PLA', '#586579', 'Blue Grey'),
|
||
spool('PLA', '#8E9089', 'Grey'), spool('PLA', '#ffffff', 'Jade White')];
|
||
assert.equal(filaments.isPrintable(pooh, shelf), true);
|
||
assert.deepEqual(filaments.assignSpools(pooh, shelf).map((s) => s.by),
|
||
['Black', 'Blue Grey', 'Grey', 'Jade White']);
|
||
});
|
||
|
||
test('one spool cannot fill two slots of the same colour', () => {
|
||
const twoBlack = [{ type: 'PLA', colour: '#000000' }, { type: 'PLA', colour: '#000000' }];
|
||
assert.equal(filaments.isPrintable(twoBlack, [spool('PLA', '#000000', 'Black')]), false);
|
||
assert.equal(filaments.isPrintable(twoBlack,
|
||
[spool('PLA', '#000000', 'Black'), { ...spool('PLA', '#000000', 'Black 2'), id: 'b2' }]), true);
|
||
});
|
||
|
||
test('8-digit hex with alpha parses as its colour', () => {
|
||
assert.deepEqual(filaments.rgb('#FFFFFFFF'), { r: 255, g: 255, b: 255 });
|
||
assert.deepEqual(filaments.rgb('#586579'), { r: 88, g: 101, b: 121 });
|
||
assert.equal(filaments.rgb('not a colour'), null);
|
||
// A slot written with alpha still matches a plain spool of that colour.
|
||
assert.equal(filaments.isPrintable([{ type: 'PLA', colour: '#FFFFFFFF' }],
|
||
[spool('PLA', '#ffffff', 'Jade White')]), true);
|
||
});
|
||
|
||
test('near shades count, but near-black is not black', () => {
|
||
const nearWhite = [{ type: 'PLA', colour: '#FDFDFD' }];
|
||
assert.equal(filaments.isPrintable(nearWhite, [spool('PLA', '#ffffff', 'Jade White')]), true);
|
||
// #161616 sits about 66 away from black, beyond the tolerance of 50.
|
||
assert.ok(filaments.colourDistance('#000000', '#161616') > filaments.COLOUR_TOLERANCE);
|
||
assert.equal(filaments.isPrintable([{ type: 'PLA', colour: '#161616' }],
|
||
[spool('PLA', '#000000', 'Black')]), false);
|
||
});
|
||
|
||
test('the nearest spool is named when a slot can’t be filled', () => {
|
||
const [slot] = filaments.assignSpools([{ type: 'PLA', colour: '#000000' }],
|
||
[spool('PLA', '#ffffff', 'Jade White')]);
|
||
assert.equal(slot.filled, false);
|
||
assert.equal(slot.nearest.name, 'Jade White');
|
||
assert.ok(slot.nearest.distance > 0);
|
||
});
|
||
|
||
test('material still matters, and a slot with no colour takes any spool of it', () => {
|
||
assert.equal(filaments.isPrintable([{ type: 'PETG', colour: '#ffffff' }],
|
||
[spool('PLA', '#ffffff', 'Jade White')]), false);
|
||
assert.equal(filaments.isPrintable([{ type: 'PLA', colour: null }],
|
||
[spool('PLA', '#123456', 'Anything')]), true);
|
||
});
|
||
|
||
test('an empty spool doesn’t count, but an unweighed one does', () => {
|
||
const one = [{ type: 'PLA', colour: '#ffffff' }];
|
||
assert.equal(filaments.isPrintable(one, [{ ...spool('PLA', '#ffffff', 'Jade White'), remaining_g: 0 }]), false);
|
||
assert.equal(filaments.isPrintable(one, [{ ...spool('PLA', '#ffffff', 'Jade White'), remaining_g: null }]), true);
|
||
});
|
||
|
||
test('a model we know nothing about is not claimed to be printable', () => {
|
||
assert.equal(filaments.isPrintable(null, [spool('PLA', '#ffffff')]), false);
|
||
assert.equal(filaments.isPrintable([], [spool('PLA', '#ffffff')]), false);
|
||
assert.deepEqual(filaments.assignSpools(null, []), []);
|
||
});
|
||
|
||
test('slots sent from the page are cleaned before they reach the database', () => {
|
||
const cleaned = filaments.cleanSlots([
|
||
{ type: ' pla ', colour: '#ffffff' }, // trimmed, hex upper-cased
|
||
{ type: 'PETG', colour: 'not a colour' }, // bad colour becomes "any colour"
|
||
{ type: '', colour: '#000000' }, // no material: dropped entirely
|
||
{ type: 'PLA', colour: '#8e9089' },
|
||
]);
|
||
assert.deepEqual(cleaned, [
|
||
{ type: 'pla', colour: '#FFFFFF' },
|
||
{ type: 'PETG', colour: null },
|
||
{ type: 'PLA', colour: '#8E9089' },
|
||
]);
|
||
});
|
||
|
||
test('anything that isn’t a list of slots is refused outright', () => {
|
||
assert.equal(filaments.cleanSlots(null), null);
|
||
assert.equal(filaments.cleanSlots('PLA'), null);
|
||
assert.equal(filaments.cleanSlots({ type: 'PLA' }), null);
|
||
assert.deepEqual(filaments.cleanSlots([]), []);
|
||
});
|