mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-09 15:35:39 +02:00
A spool created by Quick Add, a CSV import or an RFID scan has no slicer preset, brand or subtype. Reopening it in Edit Spool demanded all three before anything could be saved, so changing its storage location, cost or notes was impossible - and Copy Spool had the same gate with no Quick Add toggle to waive it. The preset you were then forced to pick auto- filled material, brand and subtype from the preset name, silently rewriting a hand-entered manufacturer (Elegoo -> Generic) so the spool no longer appeared where it had been filed. Editing and copying now require only what the backend requires: the material. Preset, brand and subtype stay fully visible and editable - nothing is hidden the way Quick Add hides it - and the required-field markers no longer advertise a rule that isn't enforced. Selecting a preset fills only fields that are still empty or that a previously selected preset had filled, so values the user (or the saved spool) provided survive; switching between presets still replaces what the earlier one contributed. The brand and material dropdowns also no longer filter themselves down to the brand/material pairs known to the color catalog and slicer presets. Elegoo is catalogued only for PLA, which made a real product like Elegoo ASA look impossible to enter. Both lists now always offer everything known, with paired entries ranked first under Suggested and the rest under All, and a spool's own custom brand or material is always present in its own dropdown. The SpoolBuddy write-tag form shares these fields and gets the same treatment. Lastly the Quick Add layout no longer leaks out of create mode: quick- adding a spool and then opening Edit left the edit form in the reduced layout with no toggle to leave it, because the toggle is create-only. Frontend only. Translated in all locales; wiki updated. Covered by validation and form-interaction tests.
139 lines
5.5 KiB
TypeScript
139 lines
5.5 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { validateForm, defaultFormData } from '../../components/spool-form/types';
|
|
|
|
describe('validateForm', () => {
|
|
describe('standard mode', () => {
|
|
it('requires slicer_filament, material, brand, and subtype', () => {
|
|
const result = validateForm(defaultFormData);
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errors.slicer_filament).toBeDefined();
|
|
expect(result.errors.material).toBeDefined();
|
|
expect(result.errors.brand).toBeDefined();
|
|
expect(result.errors.subtype).toBeDefined();
|
|
});
|
|
|
|
it('passes when all required fields are filled', () => {
|
|
const data = {
|
|
...defaultFormData,
|
|
slicer_filament: 'Bambu PLA Basic @BBL',
|
|
material: 'PLA',
|
|
brand: 'Bambu Lab',
|
|
subtype: 'Basic',
|
|
};
|
|
const result = validateForm(data);
|
|
expect(result.isValid).toBe(true);
|
|
expect(Object.keys(result.errors)).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe('quickAdd mode', () => {
|
|
it('only requires material', () => {
|
|
const result = validateForm(defaultFormData, true);
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errors.material).toBeDefined();
|
|
expect(result.errors.slicer_filament).toBeUndefined();
|
|
expect(result.errors.brand).toBeUndefined();
|
|
});
|
|
|
|
it('passes with only material set', () => {
|
|
const data = { ...defaultFormData, material: 'PETG' };
|
|
const result = validateForm(data, true);
|
|
expect(result.isValid).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('spoolmanMode', () => {
|
|
it('only requires material (same as quickAdd)', () => {
|
|
const result = validateForm(defaultFormData, false, true);
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errors.material).toBeDefined();
|
|
expect(result.errors.slicer_filament).toBeUndefined();
|
|
expect(result.errors.brand).toBeUndefined();
|
|
expect(result.errors.subtype).toBeUndefined();
|
|
});
|
|
|
|
it('passes with only material set', () => {
|
|
const data = { ...defaultFormData, material: 'PLA' };
|
|
const result = validateForm(data, false, true);
|
|
expect(result.isValid).toBe(true);
|
|
expect(Object.keys(result.errors)).toHaveLength(0);
|
|
});
|
|
|
|
it('does not require slicer_filament even when present', () => {
|
|
const data = { ...defaultFormData, material: 'ABS' };
|
|
const result = validateForm(data, false, true);
|
|
expect(result.isValid).toBe(true);
|
|
});
|
|
|
|
it('fails when material is empty string', () => {
|
|
const data = { ...defaultFormData, material: '' };
|
|
const result = validateForm(data, false, true);
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errors.material).toBeDefined();
|
|
});
|
|
|
|
it('quickAdd takes precedence over spoolmanMode', () => {
|
|
const data = { ...defaultFormData, material: 'PLA' };
|
|
const result = validateForm(data, true, true);
|
|
expect(result.isValid).toBe(true);
|
|
});
|
|
|
|
it('passes when spoolman_filament_id is set and material is empty', () => {
|
|
// When a catalog entry is pre-selected, material is not required
|
|
const data = { ...defaultFormData, material: '', spoolman_filament_id: 7 };
|
|
const result = validateForm(data, false, true);
|
|
expect(result.isValid).toBe(true);
|
|
expect(result.errors.material).toBeUndefined();
|
|
});
|
|
|
|
it('fails when both material and spoolman_filament_id are absent', () => {
|
|
const data = { ...defaultFormData, material: '', spoolman_filament_id: null };
|
|
const result = validateForm(data, false, true);
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errors.material).toBeDefined();
|
|
});
|
|
});
|
|
|
|
// #1905: a spool created by quick-add / CSV import / RFID scan has no preset,
|
|
// brand or subtype. Demanding them on every later edit blocked changes to
|
|
// unrelated fields, and the forced preset pick then rewrote the spool's
|
|
// manufacturer. Edit and copy now match what the backend requires: material.
|
|
describe('edit and copy modes', () => {
|
|
it('only requires material when editing', () => {
|
|
const result = validateForm(defaultFormData, false, false, 'edit');
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errors.material).toBeDefined();
|
|
expect(result.errors.slicer_filament).toBeUndefined();
|
|
expect(result.errors.brand).toBeUndefined();
|
|
expect(result.errors.subtype).toBeUndefined();
|
|
});
|
|
|
|
it('passes when editing a spool that only has material', () => {
|
|
const data = { ...defaultFormData, material: 'ASA' };
|
|
const result = validateForm(data, false, false, 'edit');
|
|
expect(result.isValid).toBe(true);
|
|
expect(Object.keys(result.errors)).toHaveLength(0);
|
|
});
|
|
|
|
it('passes when copying a spool that only has material', () => {
|
|
const data = { ...defaultFormData, material: 'ASA' };
|
|
const result = validateForm(data, false, false, 'copy');
|
|
expect(result.isValid).toBe(true);
|
|
});
|
|
|
|
it('still requires the full details in create mode', () => {
|
|
const data = { ...defaultFormData, material: 'ASA' };
|
|
const result = validateForm(data, false, false, 'create');
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errors.slicer_filament).toBeDefined();
|
|
expect(result.errors.brand).toBeDefined();
|
|
expect(result.errors.subtype).toBeDefined();
|
|
});
|
|
|
|
it('defaults to create mode when no mode is given', () => {
|
|
const data = { ...defaultFormData, material: 'ASA' };
|
|
expect(validateForm(data).isValid).toBe(false);
|
|
});
|
|
});
|
|
});
|