Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

02/14/25 Add disclaimer modal and move + test calldata generation #1301

Merged
merged 18 commits into from
Feb 15, 2025
Merged
29 changes: 23 additions & 6 deletions packages/ui-components/src/__tests__/DeploymentSteps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { render, screen, waitFor } from '@testing-library/svelte';
import DeploymentSteps from '../lib/components/deployment/DeploymentSteps.svelte';
import { DotrainOrderGui, type Scenario } from '@rainlanguage/orderbook/js_api';

import type { ComponentProps } from 'svelte';
import { writable } from 'svelte/store';
import type { AppKit } from '@reown/appkit';
import type { ConfigSource } from '../lib/typeshare/config';
import type { DeploymentArgs } from '$lib/types/transaction';
import type { DisclaimerModal } from '$lib';

const { mockWagmiConfigStore, mockConnectedStore } = await vi.hoisted(
() => import('../lib/__mocks__/stores')
);
Expand Down Expand Up @@ -634,7 +636,10 @@ describe('DeploymentSteps', () => {
wagmiConfig: mockWagmiConfigStore,
wagmiConnected: mockConnectedStore,
appKitModal: writable({} as AppKit),
handleDeployModal: vi.fn(),
handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void,
handleDisclaimerModal: vi.fn() as unknown as (
args: ComponentProps<DisclaimerModal>
) => void,
settings: writable({} as ConfigSource),
handleUpdateGuiState: vi.fn()
}
Expand All @@ -660,7 +665,10 @@ describe('DeploymentSteps', () => {
wagmiConfig: mockWagmiConfigStore,
wagmiConnected: mockConnectedStore,
appKitModal: writable({} as AppKit),
handleDeployModal: vi.fn(),
handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void,
handleDisclaimerModal: vi.fn() as unknown as (
args: ComponentProps<DisclaimerModal>
) => void,
settings: writable({} as ConfigSource),
handleUpdateGuiState: vi.fn()
}
Expand All @@ -686,7 +694,10 @@ describe('DeploymentSteps', () => {
wagmiConfig: mockWagmiConfigStore,
wagmiConnected: mockConnectedStore,
appKitModal: writable({} as AppKit),
handleDeployModal: vi.fn(),
handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void,
handleDisclaimerModal: vi.fn() as unknown as (
args: ComponentProps<DisclaimerModal>
) => void,
settings: writable({} as ConfigSource),
handleUpdateGuiState: vi.fn()
}
Expand Down Expand Up @@ -723,7 +734,10 @@ describe('DeploymentSteps', () => {
wagmiConfig: mockWagmiConfigStore,
wagmiConnected: mockConnectedStore,
appKitModal: writable({} as AppKit),
handleDeployModal: vi.fn(),
handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void,
handleDisclaimerModal: vi.fn() as unknown as (
args: ComponentProps<DisclaimerModal>
) => void,
settings: writable({} as ConfigSource),
handleUpdateGuiState: vi.fn()
}
Expand Down Expand Up @@ -758,7 +772,10 @@ describe('DeploymentSteps', () => {
wagmiConfig: mockWagmiConfigStore,
wagmiConnected: mockConnectedStore,
appKitModal: writable({} as AppKit),
handleDeployModal: vi.fn(),
handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void,
handleDisclaimerModal: vi.fn() as unknown as (
args: ComponentProps<DisclaimerModal>
) => void,
settings: writable({} as ConfigSource),
handleUpdateGuiState: vi.fn()
}
Expand Down
25 changes: 25 additions & 0 deletions packages/ui-components/src/__tests__/DisclaimerModal.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
import DisclaimerModal from '../lib/components/deployment/DisclaimerModal.svelte';

describe('DisclaimerModal', () => {
const mockOnAccept = vi.fn();

beforeEach(() => {
vi.clearAllMocks();
});

it('calls onAccept when accepting disclaimer', async () => {
render(DisclaimerModal, {
props: {
open: true,
onAccept: mockOnAccept
}
});

const deployButton = await screen.findByText('Deploy');
await fireEvent.click(deployButton);

expect(mockOnAccept).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import {
getDeploymentTransactionArgs,
AddOrderErrors
} from '../lib/components/deployment/getDeploymentTransactionArgs';
import { getAccount } from '@wagmi/core';
import type { Config } from '@wagmi/core';
import type { DotrainOrderGui, OrderIO } from '@rainlanguage/orderbook/js_api';

// Mock wagmi/core
vi.mock('@wagmi/core', () => ({
getAccount: vi.fn()
}));

describe('getDeploymentTransactionArgs', () => {
let mockGui: DotrainOrderGui;
let mockWagmiConfig: Config;
let mockTokenOutputs: OrderIO[];

beforeEach(() => {
vi.clearAllMocks();

// Mock GUI with successful responses
mockGui = {
generateApprovalCalldatas: vi.fn().mockResolvedValue([{ token: '0x123', amount: '1000' }]),
generateDepositAndAddOrderCalldatas: vi.fn().mockResolvedValue({
deposit: '0xdeposit',
addOrder: '0xaddOrder'
}),
getCurrentDeployment: vi.fn().mockReturnValue({
deployment: {
order: {
network: { 'chain-id': 1 },
orderbook: { address: '0xorderbook' }
}
}
}),
getTokenInfo: vi.fn().mockResolvedValue({
address: '0x123',
symbol: 'TEST'
})
} as unknown as DotrainOrderGui;

mockWagmiConfig = {} as Config;
(getAccount as Mock).mockReturnValue({ address: '0xuser' });

mockTokenOutputs = [{ token: { key: 'token1' } }] as OrderIO[];
});

describe('successful cases', () => {
it('should successfully return deployment transaction args', async () => {
const result = await getDeploymentTransactionArgs(mockGui, mockWagmiConfig, mockTokenOutputs);

expect(result).toEqual({
approvals: [{ token: '0x123', amount: '1000', symbol: 'TEST' }],
deploymentCalldata: {
deposit: '0xdeposit',
addOrder: '0xaddOrder'
},
orderbookAddress: '0xorderbook',
chainId: 1
});

expect(mockGui.generateApprovalCalldatas).toHaveBeenCalledWith('0xuser');
expect(mockGui.generateDepositAndAddOrderCalldatas).toHaveBeenCalled();
});
});

describe('input validation errors', () => {
it('should throw MISSING_GUI when GUI is null', async () => {
await expect(
getDeploymentTransactionArgs(null, mockWagmiConfig, mockTokenOutputs)
).rejects.toThrow(AddOrderErrors.MISSING_GUI);
});

it('should throw MISSING_CONFIG when wagmiConfig is undefined', async () => {
await expect(
getDeploymentTransactionArgs(mockGui, undefined, mockTokenOutputs)
).rejects.toThrow(AddOrderErrors.MISSING_CONFIG);
});

it('should throw NO_WALLET when wallet address is not found', async () => {
(getAccount as Mock).mockReturnValue({ address: null });
await expect(
getDeploymentTransactionArgs(mockGui, mockWagmiConfig, mockTokenOutputs)
).rejects.toThrow(AddOrderErrors.NO_WALLET);
});
});

describe('deployment errors', () => {
it('should throw INVALID_CHAIN_ID when chain ID is missing', async () => {
mockGui.getCurrentDeployment = vi.fn().mockReturnValue({
deployment: {
order: {
network: {},
orderbook: { address: '0xorderbook' }
}
}
});

await expect(
getDeploymentTransactionArgs(mockGui, mockWagmiConfig, mockTokenOutputs)
).rejects.toThrow(AddOrderErrors.INVALID_CHAIN_ID);
});

it('should throw MISSING_ORDERBOOK when orderbook address is missing', async () => {
mockGui.getCurrentDeployment = vi.fn().mockReturnValue({
deployment: {
order: {
network: { 'chain-id': 1 },
orderbook: {}
}
}
});

await expect(
getDeploymentTransactionArgs(mockGui, mockWagmiConfig, mockTokenOutputs)
).rejects.toThrow(AddOrderErrors.MISSING_ORDERBOOK);
});
});

describe('approval and calldata errors', () => {
it('should throw APPROVAL_FAILED when generateApprovalCalldatas fails', async () => {
mockGui.generateApprovalCalldatas = vi.fn().mockRejectedValue(new Error('Approval error'));

await expect(
getDeploymentTransactionArgs(mockGui, mockWagmiConfig, mockTokenOutputs)
).rejects.toThrow(`${AddOrderErrors.APPROVAL_FAILED}: Approval error`);
});

it('should throw DEPLOYMENT_FAILED when generateDepositAndAddOrderCalldatas fails', async () => {
mockGui.generateDepositAndAddOrderCalldatas = vi
.fn()
.mockRejectedValue(new Error('Deployment error'));

await expect(
getDeploymentTransactionArgs(mockGui, mockWagmiConfig, mockTokenOutputs)
).rejects.toThrow(`${AddOrderErrors.DEPLOYMENT_FAILED}: Deployment error`);
});
});

describe('token info errors', () => {
it('should throw TOKEN_INFO_FAILED when token key is missing', async () => {
const invalidTokenOutputs = [{ token: {} }] as OrderIO[];

await expect(
getDeploymentTransactionArgs(mockGui, mockWagmiConfig, invalidTokenOutputs)
).rejects.toThrow(`${AddOrderErrors.TOKEN_INFO_FAILED}: Token key is missing`);
});

it('should throw TOKEN_INFO_FAILED when getTokenInfo fails', async () => {
mockGui.getTokenInfo = vi.fn().mockRejectedValue(new Error('Token info error'));

await expect(
getDeploymentTransactionArgs(mockGui, mockWagmiConfig, mockTokenOutputs)
).rejects.toThrow(`${AddOrderErrors.TOKEN_INFO_FAILED}: Token info error`);
});

it('should throw TOKEN_INFO_FAILED when token info is not found for approval', async () => {
mockGui.getTokenInfo = vi.fn().mockResolvedValue({
address: '0x456', // Different address than the approval token
symbol: 'TEST'
});

await expect(
getDeploymentTransactionArgs(mockGui, mockWagmiConfig, mockTokenOutputs)
).rejects.toThrow(
`${AddOrderErrors.TOKEN_INFO_FAILED}: Token info not found for address: 0x123`
);
});
});
});
Loading