|
@@ -7,6 +7,7 @@ import { render } from '../utils';
|
|
|
import SlicerSettingsPanel, { type FilamentChoice } from '../../components/SlicerSettingsPanel';
|
|
import SlicerSettingsPanel, { type FilamentChoice } from '../../components/SlicerSettingsPanel';
|
|
|
import type { SettingValue } from '../../types/slicerSettings';
|
|
import type { SettingValue } from '../../types/slicerSettings';
|
|
|
import type { DesignOverride } from '../../types/plates';
|
|
import type { DesignOverride } from '../../types/plates';
|
|
|
|
|
+import type { SlicerPresetValuesReason } from '../../api/client';
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* The panel is a controlled component: it renders from the `values` prop and
|
|
* The panel is a controlled component: it renders from the `values` prop and
|
|
@@ -22,6 +23,7 @@ function Harness({
|
|
|
filamentChoices,
|
|
filamentChoices,
|
|
|
presetValues,
|
|
presetValues,
|
|
|
presetValuesResolved,
|
|
presetValuesResolved,
|
|
|
|
|
+ presetValuesReason,
|
|
|
}: {
|
|
}: {
|
|
|
initial: Record<string, SettingValue>;
|
|
initial: Record<string, SettingValue>;
|
|
|
onChange: (v: Record<string, SettingValue>, s: Record<string, string | string[]>) => void;
|
|
onChange: (v: Record<string, SettingValue>, s: Record<string, string | string[]>) => void;
|
|
@@ -30,6 +32,7 @@ function Harness({
|
|
|
filamentChoices?: FilamentChoice[];
|
|
filamentChoices?: FilamentChoice[];
|
|
|
presetValues?: Record<string, SettingValue>;
|
|
presetValues?: Record<string, SettingValue>;
|
|
|
presetValuesResolved?: boolean;
|
|
presetValuesResolved?: boolean;
|
|
|
|
|
+ presetValuesReason?: SlicerPresetValuesReason;
|
|
|
}) {
|
|
}) {
|
|
|
const [values, setValues] = useState(initial);
|
|
const [values, setValues] = useState(initial);
|
|
|
const [selected, setSelected] = useState(new Set(initialSelected ?? []));
|
|
const [selected, setSelected] = useState(new Set(initialSelected ?? []));
|
|
@@ -43,6 +46,7 @@ function Harness({
|
|
|
filamentChoices={filamentChoices}
|
|
filamentChoices={filamentChoices}
|
|
|
presetValues={presetValues}
|
|
presetValues={presetValues}
|
|
|
presetValuesResolved={presetValuesResolved}
|
|
presetValuesResolved={presetValuesResolved}
|
|
|
|
|
+ presetValuesReason={presetValuesReason}
|
|
|
sourceOverrides={sourceOverrides}
|
|
sourceOverrides={sourceOverrides}
|
|
|
sourceSelected={selected}
|
|
sourceSelected={selected}
|
|
|
onToggleSource={(key, on) =>
|
|
onToggleSource={(key, on) =>
|
|
@@ -66,6 +70,7 @@ async function renderPanel(
|
|
|
filamentChoices?: FilamentChoice[];
|
|
filamentChoices?: FilamentChoice[];
|
|
|
presetValues?: Record<string, SettingValue>;
|
|
presetValues?: Record<string, SettingValue>;
|
|
|
presetValuesResolved?: boolean;
|
|
presetValuesResolved?: boolean;
|
|
|
|
|
+ presetValuesReason?: SlicerPresetValuesReason;
|
|
|
} = {},
|
|
} = {},
|
|
|
) {
|
|
) {
|
|
|
const onChange = vi.fn();
|
|
const onChange = vi.fn();
|
|
@@ -447,6 +452,26 @@ describe('SlicerSettingsPanel — the picked preset\'s values', () => {
|
|
|
await waitFor(() => expect(screen.getByText(/Showing slicer defaults/)).toBeInTheDocument());
|
|
await waitFor(() => expect(screen.getByText(/Showing slicer defaults/)).toBeInTheDocument());
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ it('names the fix when the sidecar predates the endpoint', async () => {
|
|
|
|
|
+ // The dominant case: an install pulls SIDECAR_TAG:-latest regardless of
|
|
|
|
|
+ // its own release channel, so a current Bambuddy against an old sidecar is
|
|
|
|
|
+ // normal. A generic "could not be read" sends that user hunting.
|
|
|
|
|
+ await renderPanel({}, { presetValuesResolved: false, presetValuesReason: 'sidecar_outdated' });
|
|
|
|
|
+ await waitFor(() => expect(screen.getByText(/Update the sidecar image/)).toBeInTheDocument());
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('distinguishes a sidecar that is missing from one that is merely old', async () => {
|
|
|
|
|
+ await renderPanel({}, { presetValuesResolved: false, presetValuesReason: 'not_configured' });
|
|
|
|
|
+ await waitFor(() => expect(screen.getByText(/no slicer sidecar is configured/)).toBeInTheDocument());
|
|
|
|
|
+ expect(screen.queryByText(/Update the sidecar image/)).not.toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('distinguishes a sidecar that did not answer', async () => {
|
|
|
|
|
+ await renderPanel({}, { presetValuesResolved: false, presetValuesReason: 'sidecar_unavailable' });
|
|
|
|
|
+ await waitFor(() => expect(screen.getByText(/did not answer/)).toBeInTheDocument());
|
|
|
|
|
+ expect(screen.queryByText(/Update the sidecar image/)).not.toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
it('shows no such notice when they resolved', async () => {
|
|
it('shows no such notice when they resolved', async () => {
|
|
|
await renderPanel({}, { presetValues: { line_width: '0.42' }, presetValuesResolved: true });
|
|
await renderPanel({}, { presetValues: { line_width: '0.42' }, presetValuesResolved: true });
|
|
|
await waitFor(() => expect(screen.getByPlaceholderText('Search settings')).toBeInTheDocument());
|
|
await waitFor(() => expect(screen.getByPlaceholderText('Search settings')).toBeInTheDocument());
|