mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 08:15:17 +00:00
test(accounts): cover codex free-tier identity labels
This commit is contained in:
1 parent
32c16a3739
commit
d5868e0688
3 files changed
+130
-10
No files matched your search
@@ -20,7 +20,7 @@ vi.mock('@/hooks/use-cliproxy-stats', async () => {
|
|||||||
const mockedUseAccountQuota = vi.mocked(useAccountQuota);
|
const mockedUseAccountQuota = vi.mocked(useAccountQuota);
|
||||||
const mockedUseAccountQuotas = vi.mocked(useAccountQuotas);
|
const mockedUseAccountQuotas = vi.mocked(useAccountQuotas);
|
||||||
|
|
||||||
function makeCodexQuota(planType: 'plus' | 'team', fiveHour: number, weekly: number) {
|
function makeCodexQuota(planType: 'free' | 'plus' | 'team', fiveHour: number, weekly: number) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
planType,
|
planType,
|
||||||
@@ -76,7 +76,9 @@ const groupedAccount: AccountData = {
|
|||||||
failureCount: 0,
|
failureCount: 0,
|
||||||
audience: 'business',
|
audience: 'business',
|
||||||
audienceLabel: 'Business',
|
audienceLabel: 'Business',
|
||||||
detailLabel: null,
|
detailLabel: 'Workspace 04a0f049',
|
||||||
|
compactDetailLabel: '04a0f049',
|
||||||
|
inlineLabel: 'Business · Workspace 04a0f049',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'personal@example.com',
|
id: 'personal@example.com',
|
||||||
@@ -87,11 +89,27 @@ const groupedAccount: AccountData = {
|
|||||||
failureCount: 1,
|
failureCount: 1,
|
||||||
audience: 'personal',
|
audience: 'personal',
|
||||||
audienceLabel: 'Personal',
|
audienceLabel: 'Personal',
|
||||||
detailLabel: null,
|
detailLabel: 'Free',
|
||||||
|
compactDetailLabel: 'Free',
|
||||||
|
inlineLabel: 'Personal · Free',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const groupedAccountWithProPersonal: AccountData = {
|
||||||
|
...groupedAccount,
|
||||||
|
variants: groupedAccount.variants?.map((variant) =>
|
||||||
|
variant.audience === 'personal'
|
||||||
|
? {
|
||||||
|
...variant,
|
||||||
|
detailLabel: 'Pro',
|
||||||
|
compactDetailLabel: 'Pro',
|
||||||
|
inlineLabel: 'Personal · Pro',
|
||||||
|
}
|
||||||
|
: variant
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
describe('AccountCard grouped quota tooltip', () => {
|
describe('AccountCard grouped quota tooltip', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockedUseAccountQuota.mockReturnValue({
|
mockedUseAccountQuota.mockReturnValue({
|
||||||
@@ -105,13 +123,13 @@ describe('AccountCard grouped quota tooltip', () => {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: makeCodexQuota('plus', 64, 42),
|
data: makeCodexQuota('free', 64, 42),
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
},
|
},
|
||||||
] as ReturnType<typeof useAccountQuotas>);
|
] as ReturnType<typeof useAccountQuotas>);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows provider quota tooltip content for each grouped personal/business row on hover', async () => {
|
it('keeps grouped Codex account labels distinct and shows quota tooltips for each variant', async () => {
|
||||||
render(
|
render(
|
||||||
<AccountCard
|
<AccountCard
|
||||||
account={groupedAccount}
|
account={groupedAccount}
|
||||||
@@ -130,7 +148,12 @@ describe('AccountCard grouped quota tooltip', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
await userEvent.hover(screen.getByText('Business'));
|
expect(
|
||||||
|
screen.getByTitle('Business · Workspace 04a0f049 • Personal · Free')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Biz')).toBeInTheDocument();
|
||||||
|
|
||||||
|
await userEvent.hover(screen.getByText('Business · Workspace 04a0f049'));
|
||||||
const businessPlan = (await screen.findAllByText('Plan: team')).find((node) =>
|
const businessPlan = (await screen.findAllByText('Plan: team')).find((node) =>
|
||||||
node.closest('[data-slot="tooltip-content"]')
|
node.closest('[data-slot="tooltip-content"]')
|
||||||
);
|
);
|
||||||
@@ -141,11 +164,35 @@ describe('AccountCard grouped quota tooltip', () => {
|
|||||||
expect(tooltipContent?.className).toContain('text-popover-foreground');
|
expect(tooltipContent?.className).toContain('text-popover-foreground');
|
||||||
expect(tooltipContent?.className).toContain('max-w-[calc(100vw-2rem)]');
|
expect(tooltipContent?.className).toContain('max-w-[calc(100vw-2rem)]');
|
||||||
|
|
||||||
await userEvent.hover(screen.getByText('Personal'));
|
await userEvent.hover(screen.getByText('Personal · Free'));
|
||||||
const personalPlan = (await screen.findAllByText('Plan: plus')).find((node) =>
|
const personalPlan = (await screen.findAllByText('Plan: free')).find((node) =>
|
||||||
node.closest('[data-slot="tooltip-content"]')
|
node.closest('[data-slot="tooltip-content"]')
|
||||||
);
|
);
|
||||||
expect(personalPlan).toBeInTheDocument();
|
expect(personalPlan).toBeInTheDocument();
|
||||||
expect(screen.getAllByText('Weekly usage limit').length).toBeGreaterThan(0);
|
expect(screen.getAllByText('Weekly usage limit').length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps richer grouped personal detail when quota planType is coarser runtime evidence', () => {
|
||||||
|
render(
|
||||||
|
<AccountCard
|
||||||
|
account={groupedAccountWithProPersonal}
|
||||||
|
zone="left"
|
||||||
|
originalIndex={0}
|
||||||
|
isHovered={false}
|
||||||
|
isDragging={false}
|
||||||
|
offset={{ x: 0, y: 0 }}
|
||||||
|
showDetails={false}
|
||||||
|
privacyMode={false}
|
||||||
|
onMouseEnter={() => undefined}
|
||||||
|
onMouseLeave={() => undefined}
|
||||||
|
onPointerDown={() => undefined}
|
||||||
|
onPointerMove={() => undefined}
|
||||||
|
onPointerUp={() => undefined}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByTitle('Business · Workspace 04a0f049 • Personal · Pro')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Personal · Pro')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Personal · Free')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { render, screen } from '@tests/setup/test-utils';
|
import { render, screen } from '@tests/setup/test-utils';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { AccountSurfaceCard } from '@/components/account/shared/account-surface-card';
|
import { AccountSurfaceCard } from '@/components/account/shared/account-surface-card';
|
||||||
import type { GeminiCliQuotaResult } from '@/lib/api-client';
|
import type { CodexQuotaResult, GeminiCliQuotaResult } from '@/lib/api-client';
|
||||||
|
|
||||||
function createGeminiQuotaResult(
|
function createGeminiQuotaResult(
|
||||||
overrides: Partial<GeminiCliQuotaResult> = {}
|
overrides: Partial<GeminiCliQuotaResult> = {}
|
||||||
@@ -29,6 +29,16 @@ function createGeminiQuotaResult(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createCodexQuotaResult(overrides: Partial<CodexQuotaResult> = {}): CodexQuotaResult {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
windows: [],
|
||||||
|
planType: 'free',
|
||||||
|
lastUpdated: Date.now(),
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe('AccountSurfaceCard', () => {
|
describe('AccountSurfaceCard', () => {
|
||||||
it('prefers live quota entitlement tier over a stale account tier for Gemini badges', () => {
|
it('prefers live quota entitlement tier over a stale account tier for Gemini badges', () => {
|
||||||
render(
|
render(
|
||||||
@@ -46,4 +56,40 @@ describe('AccountSurfaceCard', () => {
|
|||||||
|
|
||||||
expect(screen.getByText('pro')).toBeInTheDocument();
|
expect(screen.getByText('pro')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows both personal identity and free-tier detail for compact Codex cards', () => {
|
||||||
|
render(
|
||||||
|
<AccountSurfaceCard
|
||||||
|
mode="compact"
|
||||||
|
provider="codex"
|
||||||
|
accountId="user@example.com#free"
|
||||||
|
email="user@example.com"
|
||||||
|
displayEmail="user@example.com"
|
||||||
|
tokenFile="codex-user@example.com-free.json"
|
||||||
|
showQuota={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Pers')).toBeInTheDocument();
|
||||||
|
expect(screen.getByTitle('Personal')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Free')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps richer token-derived Codex personal detail when live quota planType is coarser', () => {
|
||||||
|
render(
|
||||||
|
<AccountSurfaceCard
|
||||||
|
mode="compact"
|
||||||
|
provider="codex"
|
||||||
|
accountId="user@example.com#pro"
|
||||||
|
email="user@example.com"
|
||||||
|
displayEmail="user@example.com"
|
||||||
|
tokenFile="codex-user@example.com-pro.json"
|
||||||
|
quota={createCodexQuotaResult({ planType: 'free' })}
|
||||||
|
showQuota={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Pro')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Free')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -16,7 +16,7 @@ function makeAccount(overrides: Partial<OAuthAccount> & Pick<OAuthAccount, 'id'
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('buildAccountVisualGroups', () => {
|
describe('buildAccountVisualGroups', () => {
|
||||||
it('orders grouped codex variants by audience consistently', () => {
|
it('preserves grouped codex variant identity details while ordering by audience', () => {
|
||||||
const groups = buildAccountVisualGroups([
|
const groups = buildAccountVisualGroups([
|
||||||
makeAccount({
|
makeAccount({
|
||||||
id: 'kaidu.kd@gmail.com#free',
|
id: 'kaidu.kd@gmail.com#free',
|
||||||
@@ -33,9 +33,36 @@ describe('buildAccountVisualGroups', () => {
|
|||||||
'business',
|
'business',
|
||||||
'personal',
|
'personal',
|
||||||
]);
|
]);
|
||||||
|
expect(groups[0]?.variants?.map((variant) => variant.inlineLabel)).toEqual([
|
||||||
|
'Business · Workspace 04a0f049',
|
||||||
|
'Personal · Free',
|
||||||
|
]);
|
||||||
|
expect(groups[0]?.variants?.map((variant) => variant.compactDetailLabel)).toEqual([
|
||||||
|
'04a0f049',
|
||||||
|
'Free',
|
||||||
|
]);
|
||||||
expect(groups[0]?.memberIds).toEqual([
|
expect(groups[0]?.memberIds).toEqual([
|
||||||
'kaidu.kd@gmail.com#04a0f049-team',
|
'kaidu.kd@gmail.com#04a0f049-team',
|
||||||
'kaidu.kd@gmail.com#free',
|
'kaidu.kd@gmail.com#free',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps multiple personal codex plans distinct inside the same grouped card', () => {
|
||||||
|
const groups = buildAccountVisualGroups([
|
||||||
|
makeAccount({
|
||||||
|
id: 'kaidu.kd@gmail.com#plus',
|
||||||
|
tokenFile: 'codex-kaidu.kd@gmail.com-plus.json',
|
||||||
|
}),
|
||||||
|
makeAccount({
|
||||||
|
id: 'kaidu.kd@gmail.com#free',
|
||||||
|
tokenFile: 'codex-kaidu.kd@gmail.com-free.json',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(groups).toHaveLength(1);
|
||||||
|
expect(groups[0]?.variants?.map((variant) => variant.inlineLabel)).toEqual([
|
||||||
|
'Personal · Free',
|
||||||
|
'Personal · Plus',
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user