|
@@ -24,6 +24,8 @@ vi.mock('../../api/client', () => ({
|
|
|
updateSettings: vi.fn().mockResolvedValue({}),
|
|
updateSettings: vi.fn().mockResolvedValue({}),
|
|
|
getLocalPresets: vi.fn(),
|
|
getLocalPresets: vi.fn(),
|
|
|
getBuiltinFilaments: vi.fn(),
|
|
getBuiltinFilaments: vi.fn(),
|
|
|
|
|
+ orcaCloudListProfiles: vi.fn(),
|
|
|
|
|
+ orcaCloudGetProfile: vi.fn(),
|
|
|
searchColors: vi.fn(),
|
|
searchColors: vi.fn(),
|
|
|
getColorCatalog: vi.fn(),
|
|
getColorCatalog: vi.fn(),
|
|
|
resetAmsSlot: vi.fn(),
|
|
resetAmsSlot: vi.fn(),
|
|
@@ -85,6 +87,9 @@ describe('ConfigureAmsSlotModal', () => {
|
|
|
(api.configureAmsSlot as ReturnType<typeof vi.fn>).mockResolvedValue({ success: true });
|
|
(api.configureAmsSlot as ReturnType<typeof vi.fn>).mockResolvedValue({ success: true });
|
|
|
(api.saveSlotPreset as ReturnType<typeof vi.fn>).mockResolvedValue({ success: true });
|
|
(api.saveSlotPreset as ReturnType<typeof vi.fn>).mockResolvedValue({ success: true });
|
|
|
(api.getLocalPresets as ReturnType<typeof vi.fn>).mockResolvedValue({ filament: [] });
|
|
(api.getLocalPresets as ReturnType<typeof vi.fn>).mockResolvedValue({ filament: [] });
|
|
|
|
|
+ (api.orcaCloudListProfiles as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
|
|
|
+ filament: [], printer: [], process: [],
|
|
|
|
|
+ });
|
|
|
(api.getBuiltinFilaments as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
(api.getBuiltinFilaments as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
|
(api.searchColors as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
(api.searchColors as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
|
(api.getColorCatalog as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
(api.getColorCatalog as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
@@ -206,7 +211,7 @@ describe('ConfigureAmsSlotModal', () => {
|
|
|
expect(colorInput).toHaveValue('Red');
|
|
expect(colorInput).toHaveValue('Red');
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it('sends PFUS setting_id as tray_info_idx when cloud detail has filament_id: null (#1053)', async () => {
|
|
|
|
|
|
|
+ it('sends no tray_info_idx when cloud detail has filament_id: null (#3003)', async () => {
|
|
|
// Cloud returns a user preset that inherits from a generic Bambu base and
|
|
// Cloud returns a user preset that inherits from a generic Bambu base and
|
|
|
// has no distinct filament_id of its own — this is how Bambu Cloud responds
|
|
// has no distinct filament_id of its own — this is how Bambu Cloud responds
|
|
|
// for custom presets built on top of "Generic ABS @BBL H2D" etc.
|
|
// for custom presets built on top of "Generic ABS @BBL H2D" etc.
|
|
@@ -233,9 +238,118 @@ describe('ConfigureAmsSlotModal', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const payload = (api.configureAmsSlot as ReturnType<typeof vi.fn>).mock.calls[0][3];
|
|
const payload = (api.configureAmsSlot as ReturnType<typeof vi.fn>).mock.calls[0][3];
|
|
|
- // Before the fix, this collapsed to 'GFB99' (Generic ABS's filament_id),
|
|
|
|
|
- // which made OrcaSlicer/BambuStudio Sync Filaments resolve to "Generic ABS".
|
|
|
|
|
- expect(payload.tray_info_idx).toBe('PFUScd84f663d2c2ef');
|
|
|
|
|
|
|
+ // #1053 sent the PFUS here instead, to stop the slot collapsing to
|
|
|
|
|
+ // "Generic ABS" in the slicer. The A1 capture in #3003 showed that does not
|
|
|
|
|
+ // work: tray_info_idx is 8 characters on the printer, so an 18-character
|
|
|
|
|
+ // PFUS is stored truncated (PFUScd84 here) and acknowledged as a success.
|
|
|
|
|
+ // The slot then resolves to nothing — "Generic" anyway, plus a calibration
|
|
|
|
|
+ // table keyed by an id that does not exist. Sending nothing lets the
|
|
|
|
|
+ // backend pick a filament id the printer can actually hold; setting_id
|
|
|
|
|
+ // still carries the preset.
|
|
|
|
|
+ expect(payload.tray_info_idx).toBe('');
|
|
|
|
|
+ expect(payload.setting_id).toBe('PFUScd84f663d2c2ef');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('uses an Orca Cloud profile\'s own filament_id instead of a generic (#3003)', async () => {
|
|
|
|
|
+ // Orca profile ids are UUIDs the printer cannot hold, so this branch used
|
|
|
|
|
+ // to skip the lookup entirely and send a generic — every Orca custom
|
|
|
|
|
+ // filament reached the slicer as "Generic PLA". The profile's slicer JSON
|
|
|
|
|
+ // carries a filament_id of exactly the storable shape; use it.
|
|
|
|
|
+ const ORCA_ID = '3f2a9c1e-4b7d-4a02-9f61-8c5e2d1a7b30';
|
|
|
|
|
+ (api.orcaCloudListProfiles as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
|
|
|
+ filament: [{ setting_id: ORCA_ID, name: 'Overture Matte PLA @Orca', type: 'filament', is_custom: true }],
|
|
|
|
|
+ printer: [],
|
|
|
|
|
+ process: [],
|
|
|
|
|
+ });
|
|
|
|
|
+ (api.orcaCloudGetProfile as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
|
|
|
+ setting_id: ORCA_ID,
|
|
|
|
|
+ name: 'Overture Matte PLA @Orca',
|
|
|
|
|
+ type: 'filament',
|
|
|
|
|
+ setting: { filament_id: 'P56e1be0', filament_type: ['PLA'] },
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const slotInfo = { ...defaultProps.slotInfo, savedPresetId: ORCA_ID };
|
|
|
|
|
+ render(<ConfigureAmsSlotModal {...defaultProps} slotInfo={slotInfo} />);
|
|
|
|
|
+
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(screen.getByText('Overture Matte PLA @Orca')).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ fireEvent.click(screen.getByRole('button', { name: /Configure Slot/i }));
|
|
|
|
|
+
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(api.configureAmsSlot).toHaveBeenCalled();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const payload = (api.configureAmsSlot as ReturnType<typeof vi.fn>).mock.calls[0][3];
|
|
|
|
|
+ expect(payload.tray_info_idx).toBe('P56e1be0');
|
|
|
|
|
+ // The UUID is what the slicer cannot resolve; it goes in neither field.
|
|
|
|
|
+ expect(payload.setting_id).toBe('');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('falls back to a generic when an Orca profile has no filament_id (#3003)', async () => {
|
|
|
|
|
+ const ORCA_ID = '3f2a9c1e-4b7d-4a02-9f61-8c5e2d1a7b30';
|
|
|
|
|
+ (api.orcaCloudListProfiles as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
|
|
|
+ filament: [{ setting_id: ORCA_ID, name: 'Homebrew PLA @Orca', type: 'filament', is_custom: true }],
|
|
|
|
|
+ printer: [],
|
|
|
|
|
+ process: [],
|
|
|
|
|
+ });
|
|
|
|
|
+ (api.orcaCloudGetProfile as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
|
|
|
+ setting_id: ORCA_ID,
|
|
|
|
|
+ name: 'Homebrew PLA @Orca',
|
|
|
|
|
+ type: 'filament',
|
|
|
|
|
+ setting: {},
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const slotInfo = { ...defaultProps.slotInfo, savedPresetId: ORCA_ID };
|
|
|
|
|
+ render(<ConfigureAmsSlotModal {...defaultProps} slotInfo={slotInfo} />);
|
|
|
|
|
+
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(screen.getByText('Homebrew PLA @Orca')).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ fireEvent.click(screen.getByRole('button', { name: /Configure Slot/i }));
|
|
|
|
|
+
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(api.configureAmsSlot).toHaveBeenCalled();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const payload = (api.configureAmsSlot as ReturnType<typeof vi.fn>).mock.calls[0][3];
|
|
|
|
|
+ expect(payload.tray_info_idx).toBe('GFL99');
|
|
|
|
|
+ expect(payload.setting_id).toBe('');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('uses the filament_id nested under setting when the envelope has none (#3003)', async () => {
|
|
|
|
|
+ // Bambu Cloud returns a custom preset's own filament_id on the envelope for
|
|
|
|
|
+ // some presets and inside the preset JSON for others. Only the envelope was
|
|
|
|
|
+ // read, so presets of the second shape reached the slot as their inherited
|
|
|
|
|
+ // base — the "Bambu profile instead of my profile" half of #3003.
|
|
|
|
|
+ (api.getCloudSettingDetail as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
|
|
|
+ filament_id: null,
|
|
|
|
|
+ base_id: 'GFSB99_07',
|
|
|
|
|
+ name: '# Overture Matte PLA @BBL H2D',
|
|
|
|
|
+ setting: { filament_id: 'P4d64437' },
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const slotInfo = {
|
|
|
|
|
+ ...defaultProps.slotInfo,
|
|
|
|
|
+ savedPresetId: 'PFUScd84f663d2c2ef',
|
|
|
|
|
+ };
|
|
|
|
|
+ render(<ConfigureAmsSlotModal {...defaultProps} slotInfo={slotInfo} />);
|
|
|
|
|
+
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(screen.getByText('# Overture Matte PLA @BBL H2D')).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ fireEvent.click(screen.getByRole('button', { name: /Configure Slot/i }));
|
|
|
|
|
+
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(api.configureAmsSlot).toHaveBeenCalled();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const payload = (api.configureAmsSlot as ReturnType<typeof vi.fn>).mock.calls[0][3];
|
|
|
|
|
+ // 8 characters, so the printer stores it whole and the slicer matches it.
|
|
|
|
|
+ expect(payload.tray_info_idx).toBe('P4d64437');
|
|
|
expect(payload.setting_id).toBe('PFUScd84f663d2c2ef');
|
|
expect(payload.setting_id).toBe('PFUScd84f663d2c2ef');
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -293,9 +407,11 @@ describe('ConfigureAmsSlotModal', () => {
|
|
|
expect(api.getCloudSettingDetail).not.toHaveBeenCalled();
|
|
expect(api.getCloudSettingDetail).not.toHaveBeenCalled();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it('keeps default PFUS tray_info_idx when cloud detail fetch fails', async () => {
|
|
|
|
|
- // Network/5xx from /cloud/settings/{id} must not abort the configure flow
|
|
|
|
|
- // nor leave tray_info_idx empty — we fall back to the setting_id default.
|
|
|
|
|
|
|
+ it('sends no tray_info_idx when the cloud detail fetch fails (#3003)', async () => {
|
|
|
|
|
+ // Network/5xx from /cloud/settings/{id} must not abort the configure flow.
|
|
|
|
|
+ // It leaves no filament_id to send, and the PFUS default is not a usable
|
|
|
|
|
+ // stand-in (see the filament_id: null case above), so the field goes out
|
|
|
|
|
+ // empty and the backend resolves it.
|
|
|
(api.getCloudSettingDetail as ReturnType<typeof vi.fn>).mockRejectedValue(
|
|
(api.getCloudSettingDetail as ReturnType<typeof vi.fn>).mockRejectedValue(
|
|
|
new Error('cloud unreachable')
|
|
new Error('cloud unreachable')
|
|
|
);
|
|
);
|
|
@@ -317,7 +433,7 @@ describe('ConfigureAmsSlotModal', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const payload = (api.configureAmsSlot as ReturnType<typeof vi.fn>).mock.calls[0][3];
|
|
const payload = (api.configureAmsSlot as ReturnType<typeof vi.fn>).mock.calls[0][3];
|
|
|
- expect(payload.tray_info_idx).toBe('PFUScd84f663d2c2ef');
|
|
|
|
|
|
|
+ expect(payload.tray_info_idx).toBe('');
|
|
|
expect(payload.setting_id).toBe('PFUScd84f663d2c2ef');
|
|
expect(payload.setting_id).toBe('PFUScd84f663d2c2ef');
|
|
|
});
|
|
});
|
|
|
|
|
|