spoolFormValidation.test.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { describe, it, expect } from 'vitest';
  2. import { validateForm, defaultFormData } from '../../components/spool-form/types';
  3. describe('validateForm', () => {
  4. describe('standard mode', () => {
  5. it('requires slicer_filament, material, brand, and subtype', () => {
  6. const result = validateForm(defaultFormData);
  7. expect(result.isValid).toBe(false);
  8. expect(result.errors.slicer_filament).toBeDefined();
  9. expect(result.errors.material).toBeDefined();
  10. expect(result.errors.brand).toBeDefined();
  11. expect(result.errors.subtype).toBeDefined();
  12. });
  13. it('passes when all required fields are filled', () => {
  14. const data = {
  15. ...defaultFormData,
  16. slicer_filament: 'Bambu PLA Basic @BBL',
  17. material: 'PLA',
  18. brand: 'Bambu Lab',
  19. subtype: 'Basic',
  20. };
  21. const result = validateForm(data);
  22. expect(result.isValid).toBe(true);
  23. expect(Object.keys(result.errors)).toHaveLength(0);
  24. });
  25. });
  26. describe('quickAdd mode', () => {
  27. it('only requires material', () => {
  28. const result = validateForm(defaultFormData, true);
  29. expect(result.isValid).toBe(false);
  30. expect(result.errors.material).toBeDefined();
  31. expect(result.errors.slicer_filament).toBeUndefined();
  32. expect(result.errors.brand).toBeUndefined();
  33. });
  34. it('passes with only material set', () => {
  35. const data = { ...defaultFormData, material: 'PETG' };
  36. const result = validateForm(data, true);
  37. expect(result.isValid).toBe(true);
  38. });
  39. });
  40. describe('spoolmanMode', () => {
  41. it('only requires material (same as quickAdd)', () => {
  42. const result = validateForm(defaultFormData, false, true);
  43. expect(result.isValid).toBe(false);
  44. expect(result.errors.material).toBeDefined();
  45. expect(result.errors.slicer_filament).toBeUndefined();
  46. expect(result.errors.brand).toBeUndefined();
  47. expect(result.errors.subtype).toBeUndefined();
  48. });
  49. it('passes with only material set', () => {
  50. const data = { ...defaultFormData, material: 'PLA' };
  51. const result = validateForm(data, false, true);
  52. expect(result.isValid).toBe(true);
  53. expect(Object.keys(result.errors)).toHaveLength(0);
  54. });
  55. it('does not require slicer_filament even when present', () => {
  56. const data = { ...defaultFormData, material: 'ABS' };
  57. const result = validateForm(data, false, true);
  58. expect(result.isValid).toBe(true);
  59. });
  60. it('fails when material is empty string', () => {
  61. const data = { ...defaultFormData, material: '' };
  62. const result = validateForm(data, false, true);
  63. expect(result.isValid).toBe(false);
  64. expect(result.errors.material).toBeDefined();
  65. });
  66. it('quickAdd takes precedence over spoolmanMode', () => {
  67. const data = { ...defaultFormData, material: 'PLA' };
  68. const result = validateForm(data, true, true);
  69. expect(result.isValid).toBe(true);
  70. });
  71. it('passes when spoolman_filament_id is set and material is empty', () => {
  72. // When a catalog entry is pre-selected, material is not required
  73. const data = { ...defaultFormData, material: '', spoolman_filament_id: 7 };
  74. const result = validateForm(data, false, true);
  75. expect(result.isValid).toBe(true);
  76. expect(result.errors.material).toBeUndefined();
  77. });
  78. it('fails when both material and spoolman_filament_id are absent', () => {
  79. const data = { ...defaultFormData, material: '', spoolman_filament_id: null };
  80. const result = validateForm(data, false, true);
  81. expect(result.isValid).toBe(false);
  82. expect(result.errors.material).toBeDefined();
  83. });
  84. });
  85. // #1905: a spool created by quick-add / CSV import / RFID scan has no preset,
  86. // brand or subtype. Demanding them on every later edit blocked changes to
  87. // unrelated fields, and the forced preset pick then rewrote the spool's
  88. // manufacturer. Edit and copy now match what the backend requires: material.
  89. describe('edit and copy modes', () => {
  90. it('only requires material when editing', () => {
  91. const result = validateForm(defaultFormData, false, false, 'edit');
  92. expect(result.isValid).toBe(false);
  93. expect(result.errors.material).toBeDefined();
  94. expect(result.errors.slicer_filament).toBeUndefined();
  95. expect(result.errors.brand).toBeUndefined();
  96. expect(result.errors.subtype).toBeUndefined();
  97. });
  98. it('passes when editing a spool that only has material', () => {
  99. const data = { ...defaultFormData, material: 'ASA' };
  100. const result = validateForm(data, false, false, 'edit');
  101. expect(result.isValid).toBe(true);
  102. expect(Object.keys(result.errors)).toHaveLength(0);
  103. });
  104. it('passes when copying a spool that only has material', () => {
  105. const data = { ...defaultFormData, material: 'ASA' };
  106. const result = validateForm(data, false, false, 'copy');
  107. expect(result.isValid).toBe(true);
  108. });
  109. it('still requires the full details in create mode', () => {
  110. const data = { ...defaultFormData, material: 'ASA' };
  111. const result = validateForm(data, false, false, 'create');
  112. expect(result.isValid).toBe(false);
  113. expect(result.errors.slicer_filament).toBeDefined();
  114. expect(result.errors.brand).toBeDefined();
  115. expect(result.errors.subtype).toBeDefined();
  116. });
  117. it('defaults to create mode when no mode is given', () => {
  118. const data = { ...defaultFormData, material: 'ASA' };
  119. expect(validateForm(data).isValid).toBe(false);
  120. });
  121. });
  122. });