Skip to content

Commit

Permalink
Merge pull request #1301 from rainlanguage/02/14/25-disclaime-modal
Browse files Browse the repository at this point in the history
02/14/25 Add disclaimer modal and move + test calldata generation
  • Loading branch information
hardyjosh authored Feb 15, 2025
2 parents 1428044 + 3c5984f commit a71fec3
Show file tree
Hide file tree
Showing 14 changed files with 510 additions and 112 deletions.
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

0 comments on commit a71fec3

Please sign in to comment.