PrintersPageFillLevel.test.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. /**
  2. * Tests for inventory fill level calculation logic.
  3. *
  4. * The fill level is calculated inline in PrintersPage.tsx as:
  5. * if (sp && sp.label_weight > 0 && sp.weight_used != null)
  6. * → Math.round(Math.max(0, sp.label_weight - sp.weight_used) / sp.label_weight * 100)
  7. * else → null
  8. *
  9. * These tests validate the calculation logic directly, particularly the
  10. * fix for weight_used == null (brand new spools) and weight_used == 0.
  11. */
  12. import { describe, it, expect } from 'vitest';
  13. /**
  14. * Mirrors the inline inventoryFill calculation from PrintersPage.tsx.
  15. * Extracted here for testability.
  16. */
  17. function computeInventoryFill(spool: { label_weight: number; weight_used: number | null } | null): number | null {
  18. const sp = spool;
  19. if (sp && sp.label_weight > 0 && sp.weight_used != null) {
  20. return Math.round(Math.max(0, sp.label_weight - sp.weight_used) / sp.label_weight * 100);
  21. }
  22. return null;
  23. }
  24. describe('inventoryFill calculation', () => {
  25. it('returns 100% for brand new spool with weight_used = 0', () => {
  26. expect(computeInventoryFill({ label_weight: 1000, weight_used: 0 })).toBe(100);
  27. });
  28. it('returns null for brand new spool with weight_used = null', () => {
  29. // weight_used null means "never tracked" — we can't compute fill
  30. expect(computeInventoryFill({ label_weight: 1000, weight_used: null })).toBeNull();
  31. });
  32. it('returns correct percentage for partially used spool', () => {
  33. expect(computeInventoryFill({ label_weight: 1000, weight_used: 250 })).toBe(75);
  34. });
  35. it('returns 0% for fully used spool', () => {
  36. expect(computeInventoryFill({ label_weight: 1000, weight_used: 1000 })).toBe(0);
  37. });
  38. it('returns 0% when weight_used exceeds label_weight', () => {
  39. // Math.max(0, ...) prevents negative fill
  40. expect(computeInventoryFill({ label_weight: 1000, weight_used: 1200 })).toBe(0);
  41. });
  42. it('returns null when no spool data', () => {
  43. expect(computeInventoryFill(null)).toBeNull();
  44. });
  45. it('returns null when label_weight is 0', () => {
  46. expect(computeInventoryFill({ label_weight: 0, weight_used: 0 })).toBeNull();
  47. });
  48. it('rounds to nearest integer', () => {
  49. // 1000 - 333 = 667, 667/1000 = 66.7 → 67
  50. expect(computeInventoryFill({ label_weight: 1000, weight_used: 333 })).toBe(67);
  51. });
  52. });
  53. describe('inventoryFill: old bug — weight_used > 0 vs weight_used != null', () => {
  54. /**
  55. * The old condition was: sp.weight_used > 0
  56. * This caused brand-new spools (weight_used=0) to show no fill bar.
  57. * The fix changed it to: sp.weight_used != null
  58. */
  59. it('weight_used = 0 now shows fill (was broken with > 0 check)', () => {
  60. // Old: 0 > 0 = false → null (no fill bar)
  61. // New: 0 != null = true → 100% fill
  62. const result = computeInventoryFill({ label_weight: 1000, weight_used: 0 });
  63. expect(result).toBe(100);
  64. expect(result).not.toBeNull();
  65. });
  66. it('weight_used = 0.1 shows fill (small usage)', () => {
  67. const result = computeInventoryFill({ label_weight: 1000, weight_used: 0.1 });
  68. expect(result).toBe(100); // rounds to 100 since 0.1g from 1000g is negligible
  69. });
  70. });