import React, { act } from 'react'; import { createRoot } from 'react-dom/client'; import { afterEach, describe, expect, it, vi } from 'vitest'; vi.mock('@renderer/components/ui/tooltip', () => ({ TooltipProvider: ({ children }: { children: React.ReactNode }) => React.createElement(React.Fragment, null, children), Tooltip: ({ children }: { children: React.ReactNode }) => React.createElement(React.Fragment, null, children), TooltipTrigger: ({ children }: { children: React.ReactNode }) => React.createElement(React.Fragment, null, children), TooltipContent: ({ children }: { children: React.ReactNode }) => React.createElement('div', null, children), })); vi.mock('@renderer/store', () => ({ useStore: (selector: (state: unknown) => unknown) => selector({ cliStatus: null, appConfig: { general: { multimodelEnabled: true } }, }), })); import { TeamModelSelector } from '@renderer/components/team/dialogs/TeamModelSelector'; import { GPT_5_1_CODEX_MINI_UI_DISABLED_REASON, GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON, } from '@renderer/utils/teamModelAvailability'; describe('TeamModelSelector disabled Codex models', () => { afterEach(() => { document.body.innerHTML = ''; }); it('renders GPT-5.1 Codex Mini as disabled with an explanation tooltip', async () => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); const host = document.createElement('div'); document.body.appendChild(host); const root = createRoot(host); await act(async () => { root.render( React.createElement(TeamModelSelector, { providerId: 'codex', onProviderChange: () => undefined, value: '', onValueChange: () => undefined, }) ); await Promise.resolve(); }); expect(host.textContent).toContain('GPT-5.1 Codex Mini'); expect(host.textContent).toContain('Disabled'); expect(host.textContent).toContain(GPT_5_1_CODEX_MINI_UI_DISABLED_REASON); await act(async () => { root.unmount(); await Promise.resolve(); }); }); it('renders GPT-5.3 Codex Spark as disabled with an explanation tooltip', async () => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); const host = document.createElement('div'); document.body.appendChild(host); const root = createRoot(host); await act(async () => { root.render( React.createElement(TeamModelSelector, { providerId: 'codex', onProviderChange: () => undefined, value: '', onValueChange: () => undefined, }) ); await Promise.resolve(); }); expect(host.textContent).toContain('GPT-5.3 Codex Spark'); expect(host.textContent).toContain('Disabled'); expect(host.textContent).toContain(GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON); await act(async () => { root.unmount(); await Promise.resolve(); }); }); it('normalizes a stale disabled selection back to default', async () => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); const host = document.createElement('div'); document.body.appendChild(host); const root = createRoot(host); const onValueChange = vi.fn(); await act(async () => { root.render( React.createElement(TeamModelSelector, { providerId: 'codex', onProviderChange: () => undefined, value: 'gpt-5.1-codex-mini', onValueChange, }) ); await Promise.resolve(); }); expect(onValueChange).toHaveBeenCalledWith(''); await act(async () => { root.unmount(); await Promise.resolve(); }); }); it('normalizes a stale GPT-5.3 Codex Spark selection back to default', async () => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); const host = document.createElement('div'); document.body.appendChild(host); const root = createRoot(host); const onValueChange = vi.fn(); await act(async () => { root.render( React.createElement(TeamModelSelector, { providerId: 'codex', onProviderChange: () => undefined, value: 'gpt-5.3-codex-spark', onValueChange, }) ); await Promise.resolve(); }); expect(onValueChange).toHaveBeenCalledWith(''); await act(async () => { root.unmount(); await Promise.resolve(); }); }); it('shows OpenCode as an in-development provider and keeps it non-selectable', async () => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); const host = document.createElement('div'); document.body.appendChild(host); const root = createRoot(host); const onProviderChange = vi.fn(); await act(async () => { root.render( React.createElement(TeamModelSelector, { providerId: 'anthropic', onProviderChange, value: '', onValueChange: () => undefined, disableGeminiOption: true, }) ); await Promise.resolve(); }); const trigger = host.querySelector('button'); expect(trigger).not.toBeNull(); await act(async () => { trigger?.dispatchEvent(new MouseEvent('click', { bubbles: true })); await Promise.resolve(); }); expect(host.textContent).toContain('OpenCode'); expect(host.textContent).not.toContain('Gemini in development'); expect(host.textContent?.match(/In development/g)?.length ?? 0).toBeGreaterThanOrEqual(1); const buttons = Array.from(host.querySelectorAll('button')); const openCodeButton = buttons.find((button) => button.textContent?.includes('OpenCode')); expect(openCodeButton).not.toBeNull(); expect(openCodeButton?.hasAttribute('disabled')).toBe(true); await act(async () => { openCodeButton?.dispatchEvent(new MouseEvent('click', { bubbles: true })); await Promise.resolve(); }); expect(onProviderChange).not.toHaveBeenCalled(); await act(async () => { root.unmount(); await Promise.resolve(); }); }); });