diff --git a/bun.lock b/bun.lock
index 08d047a6..ec864745 100644
--- a/bun.lock
+++ b/bun.lock
@@ -5,7 +5,7 @@
"": {
"name": "tanstack-start-app",
"dependencies": {
- "@clickhouse/click-ui": "0.2.0-rc.4",
+ "@clickhouse/click-ui": "0.9.1",
"@librechat/data-schemas": "^0.0.56",
"@radix-ui/react-dialog": "1.1.15",
"@tailwindcss/vite": "^4.3.1",
@@ -160,7 +160,7 @@
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "7.27.1", "@babel/helper-validator-identifier": "7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
- "@clickhouse/click-ui": ["@clickhouse/click-ui@0.2.0-rc.4", "", { "dependencies": { "@h6s/calendar": "2.0.1", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-avatar": "1.1.1", "@radix-ui/react-checkbox": "1.1.2", "@radix-ui/react-context-menu": "2.2.2", "@radix-ui/react-dialog": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.1", "@radix-ui/react-dropdown-menu": "2.1.2", "@radix-ui/react-hover-card": "1.1.2", "@radix-ui/react-popover": "1.1.2", "@radix-ui/react-popper": "1.2.1", "@radix-ui/react-radio-group": "1.2.1", "@radix-ui/react-separator": "1.1.1", "@radix-ui/react-switch": "1.1.1", "@radix-ui/react-tabs": "1.1.1", "@radix-ui/react-toast": "1.2.2", "@radix-ui/react-tooltip": "1.1.2", "dayjs": "^1.11.19", "lodash-es": "^4.17.23", "react-sortablejs": "^6.1.4", "react-syntax-highlighter": "^16.1.0", "react-virtualized-auto-sizer": "^1.0.20", "react-window": "^1.8.9", "sortablejs": "^1.15.0", "styled-components": "^6.1.11" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-WcPjvS7W/InvHP/JVeMIHA/30jl3Ks6CN+zch7kfqogjcbTc4byOzSUSkOJlYblzLxfdins0z0oeBF+jVsBMDA=="],
+ "@clickhouse/click-ui": ["@clickhouse/click-ui@0.9.1", "", { "dependencies": { "@h6s/calendar": "2.2.0", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-avatar": "1.1.1", "@radix-ui/react-checkbox": "1.1.2", "@radix-ui/react-context-menu": "2.2.2", "@radix-ui/react-dialog": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.1", "@radix-ui/react-dropdown-menu": "2.1.2", "@radix-ui/react-hover-card": "1.1.2", "@radix-ui/react-popover": "1.1.2", "@radix-ui/react-popper": "1.2.1", "@radix-ui/react-radio-group": "1.2.1", "@radix-ui/react-separator": "1.1.1", "@radix-ui/react-switch": "1.1.1", "@radix-ui/react-tabs": "1.1.1", "@radix-ui/react-toast": "1.2.2", "@radix-ui/react-tooltip": "1.1.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dayjs": "^1.11.19", "lodash-es": "^4.17.23", "react-sortablejs": "^6.1.4", "react-syntax-highlighter": "^16.1.0", "react-virtualized-auto-sizer": "^1.0.20", "react-window": "^1.8.9", "sortablejs": "^1.15.0", "styled-components": "^6.1.11" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-gAyIlzIDYqnXcmHM2rOWNpGAoC0UAeacT5yJpDgu09I+TjmuOdzh9tYaJlS938SmUyLsg2U6t8oMA7do108Hgg=="],
"@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="],
@@ -188,8 +188,6 @@
"@emotion/memoize": ["@emotion/memoize@0.9.0", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="],
- "@emotion/unitless": ["@emotion/unitless@0.10.0", "", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="],
-
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
@@ -270,7 +268,7 @@
"@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
- "@h6s/calendar": ["@h6s/calendar@2.0.1", "", { "peerDependencies": { "date-fns": ">= 2", "react": ">= 18" } }, "sha512-9q5ksdnUsLDeuSm5arXzkxHyS22u7yi5TtVr4DZgW514AjdL+76kx7d+ScX9sKusasRQVxrhM2LTVmd8A/u42Q=="],
+ "@h6s/calendar": ["@h6s/calendar@2.2.0", "", { "peerDependencies": { "react": ">= 18" } }, "sha512-o1fb4RlMWDRIOiMEX65vN5qDTiD/OyC++EkJzmoz9l1vUOkN89gTE+wNE83XZBi3hbYqMV3CNhgKf51n/0hcBw=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@@ -644,8 +642,6 @@
"@types/sortablejs": ["@types/sortablejs@1.15.9", "", {}, "sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ=="],
- "@types/stylis": ["@types/stylis@4.2.7", "", {}, "sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA=="],
-
"@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="],
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
@@ -824,6 +820,8 @@
"ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
+ "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
+
"classnames": ["classnames@2.3.1", "", {}, "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="],
"cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
@@ -1454,8 +1452,6 @@
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
- "shallowequal": ["shallowequal@1.1.0", "", {}, "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="],
-
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
@@ -1654,10 +1650,6 @@
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
- "@clickhouse/click-ui/dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="],
-
- "@clickhouse/click-ui/styled-components": ["styled-components@6.3.11", "", { "dependencies": { "@emotion/is-prop-valid": "1.4.0", "@emotion/unitless": "0.10.0", "@types/stylis": "4.2.7", "css-to-react-native": "3.2.0", "csstype": "3.2.3", "postcss": "8.4.49", "shallowequal": "1.1.0", "stylis": "4.3.6", "tslib": "2.8.1" }, "peerDependencies": { "react": ">= 16.8.0", "react-dom": ">= 16.8.0" }, "optionalPeers": ["react-dom"] }, "sha512-opzgceGlQ5rdZdGwf9ddLW7EM2F4L7tgsgLn6fFzQ2JgE5EVQ4HZwNkcgB1p8WfOBx1GEZP3fa66ajJmtXhSrA=="],
-
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/eslintrc/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="],
@@ -2020,8 +2012,6 @@
"xmlbuilder2/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
- "@clickhouse/click-ui/styled-components/postcss": ["postcss@8.4.49", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="],
-
"@eslint/eslintrc/espree/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/eslintrc/minimatch/brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="],
@@ -2136,8 +2126,6 @@
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
- "@clickhouse/click-ui/styled-components/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
-
"@eslint/eslintrc/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"@humanwhocodes/config-array/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
diff --git a/package.json b/package.json
index 78d3e020..f46add31 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
"format:check": "prettier --check 'src/**/*.{ts,tsx,css,json}'"
},
"dependencies": {
- "@clickhouse/click-ui": "0.2.0-rc.4",
+ "@clickhouse/click-ui": "0.9.1",
"@librechat/data-schemas": "^0.0.56",
"@radix-ui/react-dialog": "1.1.15",
"@tailwindcss/vite": "^4.3.1",
diff --git a/src/components/AuthCard.tsx b/src/components/AuthCard.tsx
index 756dd65c..f3712f74 100644
--- a/src/components/AuthCard.tsx
+++ b/src/components/AuthCard.tsx
@@ -60,6 +60,7 @@ export function AuthCard({
if (result.error || !result.authUrl) {
setAutoRedirectFailed(true);
setGeneralError(result.message || localize('com_auth_sso_redirect_failed'));
+ setSsoLoading(false);
return;
}
const authUrl = new URL(result.authUrl);
@@ -71,10 +72,20 @@ export function AuthCard({
.catch(() => {
setAutoRedirectFailed(true);
setGeneralError(localize('com_auth_sso_redirect_failed'));
- })
- .finally(() => setSsoLoading(false));
+ setSsoLoading(false);
+ });
}, [autoRedirectSso, localize, redirectTo]);
+ useEffect(() => {
+ // A bfcache restore (browser Back from the IdP) revives the pre-navigation
+ // React state, so reset the loading flag left true by the outbound redirect.
+ const handlePageShow = (event: PageTransitionEvent) => {
+ if (event.persisted) setSsoLoading(false);
+ };
+ window.addEventListener('pageshow', handlePageShow);
+ return () => window.removeEventListener('pageshow', handlePageShow);
+ }, []);
+
const emailSchema = useMemo(
() => z.string().email(localize('com_auth_email_invalid')),
[localize],
@@ -196,16 +207,14 @@ export function AuthCard({
setSsoLoading(true);
try {
const result = await openidLoginFn();
- if (result.error) {
+ if (result.error || !result.authUrl) {
setGeneralError(result.message || localize('com_auth_login_failed'));
+ setSsoLoading(false);
return;
}
- if (result.authUrl) {
- window.location.href = result.authUrl;
- }
+ window.location.href = result.authUrl;
} catch {
setGeneralError(localize('com_auth_unable_connect'));
- } finally {
setSsoLoading(false);
}
};
@@ -213,7 +222,8 @@ export function AuthCard({
if (showAutoRedirect) {
return (
>
)}
diff --git a/src/components/__tests__/AuthCard.test.tsx b/src/components/__tests__/AuthCard.test.tsx
new file mode 100644
index 00000000..e017cd14
--- /dev/null
+++ b/src/components/__tests__/AuthCard.test.tsx
@@ -0,0 +1,153 @@
+import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { render, screen, waitFor, fireEvent } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import type * as t from '@/types';
+import { openidLoginFn } from '@/server';
+import { AuthCard } from '../AuthCard';
+
+vi.mock('@/server', () => ({
+ adminLoginFn: vi.fn(),
+ adminVerify2FAFn: vi.fn(),
+ openidLoginFn: vi.fn(),
+ openIdCheckOptions: { queryKey: ['openIdCheck'], queryFn: vi.fn() },
+}));
+
+vi.mock('@tanstack/react-router', () => ({
+ useRouter: () => ({ invalidate: vi.fn(), navigate: vi.fn() }),
+}));
+
+vi.mock('@/hooks', () => ({
+ useLocalize: () => (key: string) => key,
+}));
+
+type SsoLoginResult = Awaited>;
+
+const openidLoginFnMock = vi.mocked(openidLoginFn);
+
+function renderAuthCard(props: Partial = {}) {
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+ return render(
+
+
+ ,
+ );
+}
+
+function getSsoButton() {
+ return screen.getByRole('button', { name: 'com_auth_sso_sign_in' });
+}
+
+function getSsoRedirectingButton() {
+ return screen.getByRole('button', { name: 'com_auth_sso_redirecting' });
+}
+
+describe('AuthCard SSO login', () => {
+ const locationStub = { href: 'http://localhost:3000/' };
+
+ beforeEach(() => {
+ locationStub.href = 'http://localhost:3000/';
+ vi.stubGlobal('location', locationStub);
+ });
+
+ afterEach(() => {
+ vi.unstubAllGlobals();
+ vi.clearAllMocks();
+ });
+
+ it('shows the loading state while the SSO login request is pending', async () => {
+ openidLoginFnMock.mockImplementation(() => new Promise(() => {}));
+ renderAuthCard();
+
+ fireEvent.click(getSsoButton());
+
+ await waitFor(() => {
+ const button = getSsoRedirectingButton();
+ expect(button).toHaveAttribute('aria-busy', 'true');
+ expect(button).toBeDisabled();
+ });
+ });
+
+ it('keeps the button loading after resolving and navigates to the auth URL', async () => {
+ const authUrl = 'https://idp.example.com/authorize';
+ openidLoginFnMock.mockResolvedValue({ error: false, authUrl });
+ renderAuthCard();
+
+ fireEvent.click(getSsoButton());
+
+ await waitFor(() => expect(locationStub.href).toBe(authUrl));
+ const button = getSsoRedirectingButton();
+ expect(button).toHaveAttribute('aria-busy', 'true');
+ expect(button).toBeDisabled();
+ });
+
+ it('resets the loading state when the page is restored from the back-forward cache', async () => {
+ const authUrl = 'https://idp.example.com/authorize';
+ openidLoginFnMock.mockResolvedValue({ error: false, authUrl });
+ renderAuthCard();
+
+ fireEvent.click(getSsoButton());
+ await waitFor(() => expect(locationStub.href).toBe(authUrl));
+
+ fireEvent(window, Object.assign(new Event('pageshow'), { persisted: false }));
+ expect(getSsoRedirectingButton()).toBeDisabled();
+
+ fireEvent(window, Object.assign(new Event('pageshow'), { persisted: true }));
+ const button = getSsoButton();
+ expect(button).not.toHaveAttribute('aria-busy');
+ expect(button).toBeEnabled();
+ });
+
+ it('returns the button to non-loading when the request resolves with an error', async () => {
+ openidLoginFnMock.mockResolvedValue({ error: true, message: 'Failed to initiate SSO login' });
+ renderAuthCard();
+
+ fireEvent.click(getSsoButton());
+
+ await waitFor(() =>
+ expect(screen.getByText('Failed to initiate SSO login')).toBeInTheDocument(),
+ );
+ const button = getSsoButton();
+ expect(button).not.toHaveAttribute('aria-busy');
+ expect(button).toBeEnabled();
+ expect(locationStub.href).toBe('http://localhost:3000/');
+ });
+
+ it('returns the button to non-loading when the request rejects', async () => {
+ openidLoginFnMock.mockRejectedValue(new Error('network down'));
+ renderAuthCard();
+
+ fireEvent.click(getSsoButton());
+
+ await waitFor(() => expect(screen.getByText('com_auth_unable_connect')).toBeInTheDocument());
+ const button = getSsoButton();
+ expect(button).not.toHaveAttribute('aria-busy');
+ expect(button).toBeEnabled();
+ });
+});
+
+describe('AuthCard panel width', () => {
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('renders the login panel with fillWidth and a min width', () => {
+ const { container } = renderAuthCard();
+
+ const panel = container.querySelector('.auth-card');
+ expect(panel).not.toBeNull();
+ expect(panel?.style.getPropertyValue('--panel-width')).toBe('100%');
+ expect(panel?.classList.contains('min-w-70')).toBe(true);
+ expect(panel?.classList.contains('max-w-md')).toBe(true);
+ });
+
+ it('renders the auto-redirect panel with fillWidth and a min width', () => {
+ openidLoginFnMock.mockImplementation(() => new Promise(() => {}));
+ const { container } = renderAuthCard({ autoRedirectSso: true });
+
+ const panel = container.querySelector('.auth-card');
+ expect(panel).not.toBeNull();
+ expect(panel?.style.getPropertyValue('--panel-width')).toBe('100%');
+ expect(panel?.classList.contains('min-w-70')).toBe(true);
+ expect(panel?.classList.contains('max-w-md')).toBe(true);
+ });
+});
diff --git a/vitest.config.ts b/vitest.config.ts
index c7e8e233..25a36ac3 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -7,6 +7,11 @@ export default defineConfig({
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test/setup.ts'],
- exclude: ['e2e/**', 'node_modules/**', 'tools/**'],
+ exclude: ['e2e/**', 'node_modules/**', 'tools/**', '.claude/**'],
+ server: {
+ deps: {
+ inline: ['@clickhouse/click-ui'],
+ },
+ },
},
})