mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-24 12:13:28 +00:00
Neutralize infrastructure add catalog posture
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"default_infrastructure_source_order": [
|
||||
"proxmox-pve",
|
||||
"agent",
|
||||
"docker",
|
||||
"truenas",
|
||||
"proxmox-pve",
|
||||
"proxmox-pbs",
|
||||
"proxmox-pmg",
|
||||
"kubernetes",
|
||||
"truenas"
|
||||
"docker",
|
||||
"kubernetes"
|
||||
],
|
||||
"platforms": [
|
||||
{
|
||||
|
||||
@@ -1001,18 +1001,21 @@ surfaces grow a second VMware availability fetch or a VMware-only handoff
|
||||
path.
|
||||
That same infrastructure workspace boundary now also owns the first-run
|
||||
handoff copy for new operators. `InfrastructureWorkspace.tsx` must keep
|
||||
`Install on a host` visible as the first monitored-system path while still
|
||||
presenting `Platform connections` as the explicit API-backed alternative
|
||||
instead of leaving first-session install guidance implicit in generic
|
||||
settings-shell prose or retreating to one provider's name as the primary
|
||||
alternative.
|
||||
`Install on a host` and `Platform connections` explicit in the shared
|
||||
workspace instead of leaving first-session guidance implicit in generic
|
||||
settings-shell prose or retreating to one provider's name or one onboarding
|
||||
mode as the primary story.
|
||||
That same first-run infrastructure handoff now also owns the inline
|
||||
add-infrastructure landing in `ConnectionEditor.tsx`: when the target can run
|
||||
the Unified Agent, the landing must lead with the recommended host-install
|
||||
card above the platform-API probe and picker instead of teaching probe-first
|
||||
as the default story, and returning `Back to catalog` must reset probe input
|
||||
and result state rather than reopening the landing with stale no-match or
|
||||
detected-product state already rendered.
|
||||
the Unified Agent, the landing must lead with the platform-API probe and
|
||||
picker as the primary catalog story while keeping the host-install path in a
|
||||
separate secondary section instead of teaching one provider-specific shortcut
|
||||
as the frame for the whole screen. Product-specific host advantages such as
|
||||
Proxmox auto-registration may be explained inside that host-install section,
|
||||
but they must not become the headline copy or top visual weight for the
|
||||
shared add-infrastructure landing. Returning `Back to catalog` must reset
|
||||
probe input and result state rather than reopening the landing with stale
|
||||
no-match or detected-product state already rendered.
|
||||
Render-order proof for that landing belongs to DOM-backed settings tests, not
|
||||
raw source-string position checks, so lifecycle ownership continues to guard
|
||||
the operator-visible order after reasonable component extraction or copy
|
||||
|
||||
@@ -26,7 +26,7 @@ export const AddressProbeStep: Component<AddressProbeStepProps> = (props) => {
|
||||
id="connection-address"
|
||||
type="text"
|
||||
class={formControl}
|
||||
placeholder="pve01.lan, 10.0.0.4:8006, https://pbs.lab:8007"
|
||||
placeholder="vcenter.lab, truenas.lan, https://pve.lab:8006"
|
||||
value={props.state.address()}
|
||||
onInput={(event) => props.state.setAddress(event.currentTarget.value)}
|
||||
autocomplete="off"
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { ConnectionType, ProbeCandidate } from '@/api/connections';
|
||||
import { AddressProbeStep } from './AddressProbeStep';
|
||||
import {
|
||||
CONNECTION_TYPE_LABELS,
|
||||
DEFAULT_CONNECTION_EDITOR_PLATFORM_TYPES,
|
||||
createConnectionEditorState,
|
||||
type ConnectionEditorState,
|
||||
} from './useConnectionEditor';
|
||||
@@ -30,12 +31,6 @@ export interface ConnectionEditorProps {
|
||||
onSaved?: () => void;
|
||||
}
|
||||
|
||||
// Platform integrations — connect to a product's management API. Peers of
|
||||
// each other. The agent is NOT in this list: it is a different kind of
|
||||
// integration (see the dedicated section below the grid) and surfacing it
|
||||
// as a tile alongside these hides what it actually adds.
|
||||
const DEFAULT_PLATFORM_TYPES: ConnectionType[] = ['pve', 'pbs', 'pmg', 'vmware', 'truenas'];
|
||||
|
||||
interface TileMeta {
|
||||
icon: Component<{ class?: string }>;
|
||||
description: string;
|
||||
@@ -61,7 +56,9 @@ export const ConnectionEditor: Component<ConnectionEditorProps> = (props) => {
|
||||
const [selectedCandidate, setSelectedCandidate] = createSignal<ProbeCandidate | null>(null);
|
||||
|
||||
const platformOptions = createMemo(() =>
|
||||
(props.manualTypeOptions ?? DEFAULT_PLATFORM_TYPES).filter((type) => type !== 'agent'),
|
||||
(props.manualTypeOptions ?? DEFAULT_CONNECTION_EDITOR_PLATFORM_TYPES).filter(
|
||||
(type) => type !== 'agent',
|
||||
),
|
||||
);
|
||||
|
||||
const activeType = () => selectedType();
|
||||
@@ -94,62 +91,14 @@ export const ConnectionEditor: Component<ConnectionEditorProps> = (props) => {
|
||||
when={showCredentialSlot()}
|
||||
fallback={
|
||||
<div class="space-y-8 p-4">
|
||||
<section class="space-y-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => chooseManualType('agent')}
|
||||
class="group flex w-full items-start gap-4 rounded-lg border-2 border-blue-300 bg-blue-50/40 p-4 text-left transition-colors hover:border-blue-500 hover:bg-blue-50 dark:border-blue-800 dark:bg-blue-950/20 dark:hover:border-blue-600 dark:hover:bg-blue-950/40"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="flex h-10 w-10 flex-none items-center justify-center rounded-md border border-blue-200 bg-blue-100 text-blue-700 dark:border-blue-900 dark:bg-blue-900/40 dark:text-blue-300"
|
||||
>
|
||||
<Cpu class="h-5 w-5" />
|
||||
</div>
|
||||
<div class="flex-1 space-y-1.5">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<div class="text-sm font-semibold text-base-content">Install Pulse Agent</div>
|
||||
<div class="rounded-full border border-blue-300 bg-blue-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-blue-800 dark:border-blue-700 dark:bg-blue-900/60 dark:text-blue-200">
|
||||
Recommended
|
||||
</div>
|
||||
<div class="rounded-full border border-border bg-surface px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted">
|
||||
Runs on a host
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs text-muted">
|
||||
<span class="font-medium text-base-content">
|
||||
On a Proxmox host, this is the fastest path.
|
||||
</span>{' '}
|
||||
The installer auto-detects Proxmox on the machine, creates the needed API
|
||||
token(s), and auto-registers any detected PVE / PBS services — no address or
|
||||
credentials to paste.
|
||||
</div>
|
||||
<div class="text-xs text-muted">
|
||||
It also reports CPU temperature, disk SMART, systemd services, and network
|
||||
metrics from the host, and auto-detects Docker and Kubernetes on that machine.
|
||||
Required for bare-metal Linux / Unraid / FreeBSD targets that have no platform
|
||||
API to connect.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="flex-none self-center text-blue-600 transition-colors group-hover:text-blue-700 dark:text-blue-400 dark:group-hover:text-blue-300"
|
||||
>
|
||||
<ArrowRight class="h-4 w-4" />
|
||||
</div>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<section class="space-y-4">
|
||||
<div class="flex items-center gap-3 text-xs font-semibold uppercase tracking-wide text-muted">
|
||||
<span class="h-px flex-1 bg-border" aria-hidden="true" />
|
||||
Or connect a platform API directly
|
||||
<span class="h-px flex-1 bg-border" aria-hidden="true" />
|
||||
<div class="space-y-1">
|
||||
<div class="text-sm font-semibold text-base-content">Connect a platform</div>
|
||||
<p class="text-xs text-muted">
|
||||
Use a management API when the product exposes one. Paste an address to auto-detect
|
||||
it, or pick a supported platform from the catalog.
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-xs text-muted">
|
||||
For VMware, TrueNAS, PMG, or a remote Proxmox you can't install the agent on. Paste
|
||||
the address and Pulse will auto-detect the product, or pick it below.
|
||||
</p>
|
||||
|
||||
<AddressProbeStep
|
||||
state={state}
|
||||
@@ -187,6 +136,53 @@ export const ConnectionEditor: Component<ConnectionEditorProps> = (props) => {
|
||||
</For>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="space-y-3 border-t border-border pt-6">
|
||||
<div class="space-y-1">
|
||||
<div class="text-sm font-semibold text-base-content">Install on a host instead</div>
|
||||
<p class="text-xs text-muted">
|
||||
Use the Pulse Agent when you want machine-level telemetry or the system has no
|
||||
management API to connect.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => chooseManualType('agent')}
|
||||
class="group flex w-full items-start gap-4 rounded-lg border border-border bg-surface p-4 text-left transition-colors hover:border-blue-500 hover:bg-blue-50/40 dark:hover:bg-blue-950/20"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="flex h-10 w-10 flex-none items-center justify-center rounded-md border border-emerald-200 bg-emerald-100 text-emerald-700 dark:border-emerald-900 dark:bg-emerald-900/40 dark:text-emerald-300"
|
||||
>
|
||||
<Cpu class="h-5 w-5" />
|
||||
</div>
|
||||
<div class="flex-1 space-y-1.5">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<div class="text-sm font-semibold text-base-content">Install Pulse Agent</div>
|
||||
<div class="rounded-full border border-border bg-surface px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted">
|
||||
Runs on a host
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs text-muted">
|
||||
Install on bare-metal Linux, Unraid, FreeBSD, or any machine where you want CPU
|
||||
temperature, disk SMART, systemd services, and network metrics from the host
|
||||
itself.
|
||||
</div>
|
||||
<div class="text-xs text-muted">
|
||||
On supported Proxmox hosts, the installer can also detect local PVE / PBS
|
||||
services, create the needed API token(s), and register them automatically.
|
||||
Docker and Kubernetes on that machine are detected too.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="flex-none self-center text-muted transition-colors group-hover:text-blue-700 dark:group-hover:text-blue-300"
|
||||
>
|
||||
<ArrowRight class="h-4 w-4" />
|
||||
</div>
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
||||
+21
-18
@@ -38,7 +38,7 @@ describe('ConnectionEditor', () => {
|
||||
|
||||
render(() => <ConnectionEditor renderCredentialSlot={renderSlot} onClose={() => {}} />);
|
||||
|
||||
const input = screen.getByPlaceholderText(/pve01\.lan/) as HTMLInputElement;
|
||||
const input = screen.getByPlaceholderText(/vcenter\.lab/) as HTMLInputElement;
|
||||
fireEvent.input(input, { target: { value: 'pve.lab' } });
|
||||
|
||||
const probeButton = screen.getByRole('button', { name: /probe address/i });
|
||||
@@ -66,7 +66,7 @@ describe('ConnectionEditor', () => {
|
||||
|
||||
render(() => <ConnectionEditor renderCredentialSlot={renderSlot} onClose={() => {}} />);
|
||||
|
||||
const input = screen.getByPlaceholderText(/pve01\.lan/) as HTMLInputElement;
|
||||
const input = screen.getByPlaceholderText(/vcenter\.lab/) as HTMLInputElement;
|
||||
fireEvent.input(input, { target: { value: '192.168.1.50' } });
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /probe address/i }));
|
||||
@@ -84,26 +84,29 @@ describe('ConnectionEditor', () => {
|
||||
expect(lastCall.candidate).toBeNull();
|
||||
});
|
||||
|
||||
it('renders an agent-led catalog with the API fallback beneath it', () => {
|
||||
it('renders a platform-first catalog with the host-install path beneath it', () => {
|
||||
render(() => <ConnectionEditor renderCredentialSlot={() => <div />} onClose={() => {}} />);
|
||||
|
||||
const platformHeading = screen.getByText('Connect a platform');
|
||||
const agentButton = screen.getByRole('button', { name: /Install Pulse Agent/i });
|
||||
const apiHeading = screen.getByText('Or connect a platform API directly');
|
||||
const probeButton = screen.getByRole('button', { name: /probe address/i });
|
||||
const vmwareButton = screen.getByRole('button', { name: /VMware vCenter \/ ESXi/i });
|
||||
const trueNASButton = screen.getByRole('button', { name: /TrueNAS SCALE/i });
|
||||
const proxmoxButton = screen.getByRole('button', { name: /^Proxmox VE/i });
|
||||
|
||||
// Catalog landing — lead with the agent card, then collapse the probe and
|
||||
// direct platform options into one API fallback section below it.
|
||||
// Catalog landing — lead with management-platform onboarding, then keep
|
||||
// host install as a secondary path below it.
|
||||
expect(platformHeading).toBeInTheDocument();
|
||||
expect(agentButton).toBeInTheDocument();
|
||||
expect(apiHeading).toBeInTheDocument();
|
||||
expect(probeButton).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /^Proxmox VE/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /TrueNAS SCALE/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /VMware vCenter \/ ESXi/i })).toBeInTheDocument();
|
||||
expectNodeBefore(agentButton, apiHeading);
|
||||
expectNodeBefore(apiHeading, probeButton);
|
||||
expect(
|
||||
screen.queryByRole('button', { name: /install the unified agent on a host/i }),
|
||||
).toBeNull();
|
||||
expect(vmwareButton).toBeInTheDocument();
|
||||
expect(trueNASButton).toBeInTheDocument();
|
||||
expect(proxmoxButton).toBeInTheDocument();
|
||||
expectNodeBefore(platformHeading, probeButton);
|
||||
expectNodeBefore(vmwareButton, trueNASButton);
|
||||
expectNodeBefore(trueNASButton, proxmoxButton);
|
||||
expectNodeBefore(proxmoxButton, agentButton);
|
||||
expect(screen.queryByText('Recommended')).toBeNull();
|
||||
});
|
||||
|
||||
it('offers the agent path contextually when a probe returns no match', async () => {
|
||||
@@ -113,7 +116,7 @@ describe('ConnectionEditor', () => {
|
||||
|
||||
render(() => <ConnectionEditor renderCredentialSlot={renderSlot} onClose={() => {}} />);
|
||||
|
||||
const input = screen.getByPlaceholderText(/pve01\.lan/) as HTMLInputElement;
|
||||
const input = screen.getByPlaceholderText(/vcenter\.lab/) as HTMLInputElement;
|
||||
fireEvent.input(input, { target: { value: 'baremetal.lan' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: /probe address/i }));
|
||||
await waitFor(() => expect(mockedProbe).toHaveBeenCalled());
|
||||
@@ -158,7 +161,7 @@ describe('ConnectionEditor', () => {
|
||||
|
||||
render(() => <ConnectionEditor renderCredentialSlot={renderSlot} onClose={() => {}} />);
|
||||
|
||||
const input = screen.getByPlaceholderText(/pve01\.lan/) as HTMLInputElement;
|
||||
const input = screen.getByPlaceholderText(/vcenter\.lab/) as HTMLInputElement;
|
||||
fireEvent.input(input, { target: { value: '192.168.1.50' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: /probe address/i }));
|
||||
|
||||
@@ -170,7 +173,7 @@ describe('ConnectionEditor', () => {
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /back to catalog/i }));
|
||||
|
||||
const resetInput = screen.getByPlaceholderText(/pve01\.lan/) as HTMLInputElement;
|
||||
const resetInput = screen.getByPlaceholderText(/vcenter\.lab/) as HTMLInputElement;
|
||||
expect(resetInput.value).toBe('');
|
||||
expect(screen.queryByText(/no supported product detected/i)).toBeNull();
|
||||
expect(screen.queryByTestId('slot')).toBeNull();
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type ProbeCandidate,
|
||||
type ProbeResponse,
|
||||
} from '@/api/connections';
|
||||
import { DEFAULT_INFRASTRUCTURE_SOURCE_ORDER } from '@/utils/platformSupportManifest';
|
||||
|
||||
const PROBE_ERROR_FALLBACK = 'Probe failed. Try again or enter credentials manually.';
|
||||
|
||||
@@ -31,6 +32,38 @@ export interface ConnectionEditorState {
|
||||
runProbe: () => Promise<void>;
|
||||
}
|
||||
|
||||
type PlatformConnectionType = Extract<ConnectionType, 'pve' | 'pbs' | 'pmg' | 'truenas' | 'vmware'>;
|
||||
|
||||
const SOURCE_PLATFORM_TO_CONNECTION_TYPE: Partial<Record<string, PlatformConnectionType>> = {
|
||||
'vmware-vsphere': 'vmware',
|
||||
truenas: 'truenas',
|
||||
'proxmox-pve': 'pve',
|
||||
'proxmox-pbs': 'pbs',
|
||||
'proxmox-pmg': 'pmg',
|
||||
};
|
||||
|
||||
// The supported-source manifest order is reused where it applies, but the
|
||||
// add-infrastructure catalog still needs to surface the admitted vSphere path.
|
||||
const CONNECTION_EDITOR_PRIORITY_TYPES: PlatformConnectionType[] = ['vmware'];
|
||||
|
||||
const PLATFORM_CONNECTION_TYPE_FALLBACK_ORDER: PlatformConnectionType[] = [
|
||||
'truenas',
|
||||
'pve',
|
||||
'pbs',
|
||||
'pmg',
|
||||
];
|
||||
|
||||
export const DEFAULT_CONNECTION_EDITOR_PLATFORM_TYPES: PlatformConnectionType[] = Array.from(
|
||||
new Set([
|
||||
...CONNECTION_EDITOR_PRIORITY_TYPES,
|
||||
...DEFAULT_INFRASTRUCTURE_SOURCE_ORDER.flatMap((platformKey) => {
|
||||
const type = SOURCE_PLATFORM_TO_CONNECTION_TYPE[platformKey];
|
||||
return type ? [type] : [];
|
||||
}),
|
||||
...PLATFORM_CONNECTION_TYPE_FALLBACK_ORDER,
|
||||
]),
|
||||
);
|
||||
|
||||
// Validation on the client side is intentionally lenient: the backend is the
|
||||
// real authority on what constitutes a probeable address. We only reject the
|
||||
// obviously empty case so the API does not see a payload it will always
|
||||
|
||||
@@ -119,6 +119,10 @@ const connectionFixture = (overrides: Partial<Connection> = {}): Connection => (
|
||||
...overrides,
|
||||
});
|
||||
|
||||
function expectNodeBefore(a: Node, b: Node) {
|
||||
expect(a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||
}
|
||||
|
||||
const baseProps = () =>
|
||||
({
|
||||
selectedAgent: () => 'pve',
|
||||
@@ -227,14 +231,23 @@ describe('InfrastructureWorkspace', () => {
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /Add infrastructure/i }));
|
||||
|
||||
// The catalog landing leads with the agent path and keeps the direct API
|
||||
// probe/catalog visible below it in the same shared editor.
|
||||
// The catalog landing leads with peer platform onboarding and keeps the
|
||||
// host install path available beneath it in the same shared editor.
|
||||
const platformHeading = screen.getByText('Connect a platform');
|
||||
const vmwareButton = screen.getByRole('button', { name: /VMware vCenter \/ ESXi/i });
|
||||
const trueNASButton = screen.getByRole('button', { name: /TrueNAS SCALE/i });
|
||||
const proxmoxButton = screen.getByRole('button', { name: /^Proxmox VE/i });
|
||||
const agentButton = screen.getByRole('button', { name: /Install Pulse Agent/i });
|
||||
|
||||
expect(platformHeading).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Install Pulse Agent/i })).toBeInTheDocument();
|
||||
expect(screen.getByText('Or connect a platform API directly')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Probe address/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /^Proxmox VE/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /TrueNAS SCALE/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /VMware vCenter \/ ESXi/i })).toBeInTheDocument();
|
||||
expect(proxmoxButton).toBeInTheDocument();
|
||||
expect(trueNASButton).toBeInTheDocument();
|
||||
expect(vmwareButton).toBeInTheDocument();
|
||||
expectNodeBefore(vmwareButton, trueNASButton);
|
||||
expectNodeBefore(trueNASButton, proxmoxButton);
|
||||
expectNodeBefore(proxmoxButton, agentButton);
|
||||
expect(navigateSpy).not.toHaveBeenCalled();
|
||||
expect(setSearchParamsSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -81,28 +81,18 @@ describe('settings architecture guardrails', () => {
|
||||
expect(infrastructureWorkspaceSource).not.toContain('layout="drawer-right"');
|
||||
});
|
||||
|
||||
it('keeps the agent-led add landing and inline node credentials on the shared editor model', () => {
|
||||
it('keeps the platform-first add landing and inline node credentials on the shared editor model', () => {
|
||||
expect(connectionEditorSource).toContain(
|
||||
"import { AddressProbeStep } from './AddressProbeStep';",
|
||||
);
|
||||
// Platform integrations render as peer tiles; the agent lives in its own
|
||||
// section below so it can explain what host-level telemetry adds instead
|
||||
// of being mistaken for one more peer of Proxmox / VMware / TrueNAS.
|
||||
expect(connectionEditorSource).toContain('const DEFAULT_PLATFORM_TYPES: ConnectionType[] =');
|
||||
expect(connectionEditorSource).not.toContain("'agent'] =");
|
||||
expect(connectionEditorSource).toContain('DEFAULT_CONNECTION_EDITOR_PLATFORM_TYPES');
|
||||
expect(connectionEditorSource).toContain('<AddressProbeStep');
|
||||
expect(connectionEditorSource).toContain('Or connect a platform API directly');
|
||||
// Keep the landing source aligned with the canonical agent-first story, but
|
||||
// leave actual DOM ordering to the render test rather than raw source
|
||||
// string positions.
|
||||
expect(connectionEditorSource).not.toContain('Or install the agent on the host');
|
||||
expect(connectionEditorSource).toContain('On a Proxmox host, this is the');
|
||||
expect(connectionEditorSource).toContain('auto-registers any detected PVE / PBS services');
|
||||
expect(connectionEditorSource).toContain('Connect a platform');
|
||||
expect(connectionEditorSource).toContain('Install on a host instead');
|
||||
expect(connectionEditorSource).toContain('On supported Proxmox hosts');
|
||||
expect(connectionEditorSource).toContain('register them automatically');
|
||||
expect(connectionEditorSource).not.toContain('auto-registers the node');
|
||||
expect(connectionEditorSource).toContain('Recommended');
|
||||
// The agent install path is a first-class ledger-header action, not a
|
||||
// subtext offramp inside the editor — make sure it doesn't drift back.
|
||||
expect(connectionEditorSource).not.toContain('Install the Unified Agent on a host');
|
||||
expect(connectionEditorSource).not.toContain('Recommended');
|
||||
expect(connectionEditorSource).not.toContain('NodeModal');
|
||||
|
||||
expect(addressProbeStepSource).toContain('Probe address');
|
||||
@@ -114,6 +104,10 @@ describe('settings architecture guardrails', () => {
|
||||
|
||||
expect(connectionEditorStateSource).toContain('ConnectionsAPI.probe(value)');
|
||||
expect(connectionEditorStateSource).toContain('export const CONNECTION_TYPE_LABELS');
|
||||
expect(connectionEditorStateSource).toContain('DEFAULT_INFRASTRUCTURE_SOURCE_ORDER');
|
||||
expect(connectionEditorStateSource).toContain(
|
||||
'export const DEFAULT_CONNECTION_EDITOR_PLATFORM_TYPES',
|
||||
);
|
||||
|
||||
expect(nodeCredentialSlotSource).toContain('useNodeModalState(modalProps)');
|
||||
expect(nodeCredentialSlotSource).toContain('<NodeModalBasicInfoSection');
|
||||
|
||||
@@ -9,9 +9,9 @@ describe('sourcePlatformOptions', () => {
|
||||
it('orders canonical source platform keys with preferred source ordering', () => {
|
||||
expect(orderSourcePlatformKeys(['truenas', 'pbs', 'agent', 'docker'])).toEqual([
|
||||
'agent',
|
||||
'docker',
|
||||
'proxmox-pbs',
|
||||
'truenas',
|
||||
'proxmox-pbs',
|
||||
'docker',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -25,13 +25,13 @@ describe('sourcePlatformOptions', () => {
|
||||
|
||||
it('exports the default infrastructure source options in canonical order', () => {
|
||||
expect(DEFAULT_INFRASTRUCTURE_SOURCE_OPTIONS.map((option) => option.key)).toEqual([
|
||||
'proxmox-pve',
|
||||
'agent',
|
||||
'docker',
|
||||
'truenas',
|
||||
'proxmox-pve',
|
||||
'proxmox-pbs',
|
||||
'proxmox-pmg',
|
||||
'docker',
|
||||
'kubernetes',
|
||||
'truenas',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
// This file is generated by scripts/release_control/generate_platform_support_frontend_module.py.
|
||||
// Do not edit by hand.
|
||||
// Source: docs/release-control/v6/internal/PLATFORM_SUPPORT_MANIFEST.json
|
||||
// Source SHA256: 79568ec8c69f13d8d49afe2b7a82b28c921b1885a90ebfb08cc98284d48b80e9
|
||||
// Source SHA256: afe3841efbb355a9d6245e3aea6af47d31d87a870a44cba5c79ec749448cd78f
|
||||
|
||||
export const PLATFORM_SUPPORT_MANIFEST_SOURCE = {
|
||||
path: 'docs/release-control/v6/internal/PLATFORM_SUPPORT_MANIFEST.json',
|
||||
sha256: '79568ec8c69f13d8d49afe2b7a82b28c921b1885a90ebfb08cc98284d48b80e9',
|
||||
sha256: 'afe3841efbb355a9d6245e3aea6af47d31d87a870a44cba5c79ec749448cd78f',
|
||||
} as const;
|
||||
export const PLATFORM_SUPPORT_MANIFEST = {
|
||||
schemaVersion: 1,
|
||||
defaultInfrastructureSourceOrder: [
|
||||
'proxmox-pve',
|
||||
'agent',
|
||||
'docker',
|
||||
'truenas',
|
||||
'proxmox-pve',
|
||||
'proxmox-pbs',
|
||||
'proxmox-pmg',
|
||||
'docker',
|
||||
'kubernetes',
|
||||
'truenas',
|
||||
],
|
||||
platforms: [
|
||||
{
|
||||
@@ -208,13 +208,13 @@ export const KNOWN_SOURCE_PLATFORM_KEYS = [
|
||||
'generic',
|
||||
] as const;
|
||||
export const DEFAULT_INFRASTRUCTURE_SOURCE_ORDER = [
|
||||
'proxmox-pve',
|
||||
'agent',
|
||||
'docker',
|
||||
'truenas',
|
||||
'proxmox-pve',
|
||||
'proxmox-pbs',
|
||||
'proxmox-pmg',
|
||||
'docker',
|
||||
'kubernetes',
|
||||
'truenas',
|
||||
] as const;
|
||||
export const SOURCE_PLATFORM_ALIAS_MAP = {
|
||||
k8s: 'kubernetes',
|
||||
|
||||
Reference in New Issue
Block a user