/**
* @vitest-environment jsdom
*/
import React from 'react';
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { act, cleanup, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/vitest';
let mockDraftContracts: any[] = [];
let mockClientContracts: any[] = [];
let mockDraftResumeData: any = null;
let mockSearchParams = new URLSearchParams('tab=contracts&subtab=drafts');
const mockRouter = {
push: vi.fn(),
replace: vi.fn(),
};
vi.mock('next/navigation', () => ({
useRouter: () => mockRouter,
useSearchParams: () => mockSearchParams,
}));
vi.mock('@alga-psa/ui/components/CustomTabs', () => ({
default: ({
tabs,
defaultTab,
}: {
tabs: Array<{ id?: string; label: string; content: React.ReactNode }>;
defaultTab: string;
}) => {
const tab =
tabs.find((t) => t.id === defaultTab)
?? tabs.find((t) => t.label === defaultTab)
?? tabs[0];
return (
{tabs.map((t) => t.label).join(' | ')}
{tab?.content}
);
},
}));
vi.mock('../src/components/billing-dashboard/contracts/template-wizard/TemplateWizard', () => ({
TemplateWizard: () => null,
}));
vi.mock('../src/components/billing-dashboard/contracts/ContractDialog', () => ({
ContractDialog: () => null,
}));
vi.mock('../src/components/billing-dashboard/contracts/ContractWizard', () => ({
ContractWizard: ({ open, onComplete }: { open: boolean; onComplete?: () => void }) =>
open ? (
) : null,
}));
vi.mock('@alga-psa/billing/actions/contractActions', () => ({
deleteContract: vi.fn(async () => undefined),
getContractTemplates: vi.fn(async () => []),
getContractsWithClients: vi.fn(async () => mockClientContracts),
getDraftContracts: vi.fn(async () => mockDraftContracts),
updateContract: vi.fn(async () => undefined),
}));
vi.mock('@alga-psa/billing/actions/contractWizardActions', () => ({
getDraftContractForResume: vi.fn(async () => mockDraftResumeData),
}));
describe('Contracts activation flow (draft -> active)', () => {
beforeEach(() => {
document.body.removeAttribute('data-scroll-locked');
document.body.removeAttribute('style');
cleanup();
vi.clearAllMocks();
});
beforeAll(() => {
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
Object.defineProperty(HTMLElement.prototype, 'clientWidth', {
configurable: true,
get() {
return 1200;
},
});
});
it('activated contract no longer appears in Drafts tab (T047)', async () => {
mockSearchParams = new URLSearchParams('tab=contracts&subtab=drafts');
mockDraftResumeData = {
contract_id: 'contract-1',
is_draft: true,
client_id: 'client-1',
contract_name: 'Draft Alpha',
start_date: '2026-01-01',
currency_code: 'USD',
enable_proration: false,
fixed_services: [],
product_services: [],
hourly_services: [],
usage_services: [],
};
mockDraftContracts = [
{
contract_id: 'contract-1',
contract_name: 'Draft Alpha',
client_name: 'Acme Co',
created_at: new Date(2026, 0, 1),
updated_at: new Date(2026, 0, 2),
},
];
mockClientContracts = [];
const Contracts = (await import('../src/components/billing-dashboard/contracts/Contracts')).default;
render();
expect(await screen.findByText('Draft Alpha')).toBeInTheDocument();
const user = userEvent.setup();
await act(async () => {
await user.click(await screen.findByRole('button', { name: /open menu/i }));
});
await act(async () => {
await user.click(await screen.findByText('Resume'));
});
await act(async () => {
await user.click(await screen.findByText('Complete Wizard'));
});
await waitFor(() => {
expect(screen.queryByText('Draft Alpha')).not.toBeInTheDocument();
});
expect(
screen.getByText('No draft contracts. Start creating a new contract to save as draft.'),
).toBeInTheDocument();
});
it('activated contract appears in Client Contracts tab (T048)', async () => {
mockSearchParams = new URLSearchParams('tab=contracts&subtab=drafts');
mockDraftResumeData = {
contract_id: 'contract-1',
is_draft: true,
client_id: 'client-1',
contract_name: 'Draft Alpha',
start_date: '2026-01-01',
currency_code: 'USD',
enable_proration: false,
fixed_services: [],
product_services: [],
hourly_services: [],
usage_services: [],
};
mockDraftContracts = [
{
contract_id: 'contract-1',
contract_name: 'Draft Alpha',
client_name: 'Acme Co',
created_at: new Date(2026, 0, 1),
updated_at: new Date(2026, 0, 2),
},
];
mockClientContracts = [];
const Contracts = (await import('../src/components/billing-dashboard/contracts/Contracts')).default;
const ui = render();
const user = userEvent.setup();
expect(await screen.findByText('Draft Alpha')).toBeInTheDocument();
await act(async () => {
await user.click(await screen.findByRole('button', { name: /open menu/i }));
});
await act(async () => {
await user.click(await screen.findByText('Resume'));
});
await act(async () => {
await user.click(await screen.findByText('Complete Wizard'));
});
// Switch to the Client Contracts tab after activation + refresh.
mockSearchParams = new URLSearchParams('tab=contracts&subtab=client-contracts');
ui.rerender();
await waitFor(() => {
expect(screen.getByText('Draft Alpha')).toBeInTheDocument();
});
});
});