// @vitest-environment jsdom
import { cleanup, render, screen, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import type { InstanceContext, InstanceModule } from '../app-shell.js';
import {
InstanceDetail,
type CapabilityDataSource,
type InstanceCapabilityMap,
} from './instance-detail.js';
afterEach(cleanup);
const owner: InstanceContext = {
id: 'owner',
name: 'Owner modem',
origin: 'https://owner.example/path',
status: 'online',
authentication: 'authenticated',
freshness: 'fresh',
};
const capabilities: InstanceCapabilityMap = {
overview: { state: 'supported' },
messages: { state: 'degraded', explanation: 'Message history is read-only.' },
calls: { state: 'unsupported', explanation: 'This modem has no voice support.' },
esim: { state: 'unknown', explanation: 'Capability discovery did not report eSIM.' },
};
describe('InstanceDetail', () => {
it('makes only supported modules actionable and explains every other capability state', () => {
render(
,
);
expect(screen.getByRole('link', { name: 'Overview' }).getAttribute('href')).toBe(
'/instances/owner/overview',
);
expect(screen.queryByRole('link', { name: 'Messages' })).toBeNull();
expect(screen.getByText('Message history is read-only.')).toBeTruthy();
expect(screen.getByText('This modem has no voice support.')).toBeTruthy();
expect(screen.getByText('Capability discovery did not report eSIM.')).toBeTruthy();
expect(screen.getAllByText('Capability state is unknown.').length).toBeGreaterThan(0);
});
it('never renders or loads context when the direct-route owner does not match', () => {
const load = vi.fn();
render(
,
);
expect(screen.queryByText('Owner modem')).toBeNull();
expect(screen.queryByRole('navigation', { name: 'Instance modules' })).toBeNull();
expect(screen.getByText(/Instance context is unavailable/i)).toBeTruthy();
expect(load).not.toHaveBeenCalled();
});
it('aborts the prior owner load and fences stale results to the latest owner', async () => {
const pending = new Map<
string,
{ resolve: (value: InstanceCapabilityMap) => void; signal: AbortSignal }
>();
const dataSource: CapabilityDataSource = {
load(instanceId, signal) {
return new Promise((resolve) => pending.set(instanceId, { resolve, signal }));
},
};
const second: InstanceContext = { ...owner, id: 'second', name: 'Second modem' };
const { rerender } = render(
,
);
await waitFor(() => expect(pending.has('owner')).toBe(true));
rerender(
,
);
await waitFor(() => expect(pending.has('second')).toBe(true));
expect(pending.get('owner')?.signal.aborted).toBe(true);
pending.get('second')?.resolve({ overview: { state: 'supported' } });
expect(await screen.findByRole('link', { name: 'Overview' })).toBeTruthy();
pending
.get('owner')
?.resolve({ overview: { state: 'unsupported', explanation: 'Stale owner result' } });
await waitFor(() => expect(screen.queryByText('Stale owner result')).toBeNull());
expect(screen.getByText('Second modem')).toBeTruthy();
});
it.each<[InstanceModule, string]>([
['cellular', 'Cellular'],
['device-network', 'Device Network'],
['automation', 'Automation'],
['ota', 'OTA'],
])('uses the existing module contract for %s', (module, label) => {
render(
,
);
expect(screen.getByRole('heading', { name: label })).toBeTruthy();
});
});