Skip to content

Commit

Permalink
Merge branch '01/08/25-serialize-and-deserialize-state' into ser/dese…
Browse files Browse the repository at this point in the history
…r-with-new-state-management
  • Loading branch information
hardingjam committed Jan 27, 2025
2 parents 88aafb4 + 155f494 commit 70603dc
Show file tree
Hide file tree
Showing 20 changed files with 749 additions and 314 deletions.
160 changes: 74 additions & 86 deletions packages/ui-components/src/__tests__/DeploymentSteps.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import { render, screen, waitFor } from '@testing-library/svelte';
import DeploymentSteps from '../lib/components/deployment/DeploymentSteps.svelte';
import { DotrainOrderGui } from '@rainlanguage/orderbook/js_api';
import userEvent from '@testing-library/user-event';

import type { ComponentProps } from 'svelte';

export type DeploymentStepsProps = ComponentProps<DeploymentSteps>;

vi.mock('@rainlanguage/orderbook/js_api', () => ({
DotrainOrderGui: {
Expand Down Expand Up @@ -566,115 +569,100 @@ describe('DeploymentSteps', () => {
vi.clearAllMocks();
});

it('renders strategy URL input and load button initially', () => {
render(DeploymentSteps);
expect(screen.getByPlaceholderText('Enter URL to .rain file')).toBeInTheDocument();
const loadButton = screen.getByText('Load Strategy');
expect(loadButton).toBeInTheDocument();
expect(loadButton).toBeDisabled();
});

it('enables load button when URL is entered', async () => {
render(DeploymentSteps);
const urlInput = screen.getByPlaceholderText('Enter URL to .rain file');
const loadButton = screen.getByText('Load Strategy');

await userEvent.type(urlInput, 'https://example.com/strategy.rain');
await waitFor(() => {
expect(loadButton).not.toBeDisabled();
it('shows deployment details when provided', async () => {
const mockDeployments = ['flare-sflr-wflr'];
(DotrainOrderGui.getDeploymentKeys as Mock).mockResolvedValue(mockDeployments);
(DotrainOrderGui.chooseDeployment as Mock).mockResolvedValue({
getSelectTokens: () => []
});
});

it('loads strategy from URL when button is clicked', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
text: () => Promise.resolve(JSON.stringify(dotrain))
const deploymentDetails = {
name: 'SFLR<>WFLR on Flare',
description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.'
};

render(DeploymentSteps, {
props: {
dotrain,
deployment: 'flare-sflr-wflr',
deploymentDetails
}
});

render(DeploymentSteps);
const urlInput = screen.getByPlaceholderText('Enter URL to .rain file');
const loadButton = screen.getByText('Load Strategy');
await userEvent.clear(urlInput);
await fireEvent.input(urlInput, { target: { value: 'https://example.com/strategy.rain' } });
await userEvent.click(loadButton);

await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith('https://example.com/strategy.rain');
expect(screen.getByText('SFLR<>WFLR on Flare')).toBeInTheDocument();
expect(
screen.getByText('Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.')
).toBeInTheDocument();
});
});

it('shows deployments dropdown after strategy is loaded', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
text: () => Promise.resolve(JSON.stringify(dotrain))
it('shows select tokens section when tokens need to be selected', async () => {
const mockSelectTokens = ['token1', 'token2'];
(DotrainOrderGui.chooseDeployment as Mock).mockResolvedValue({
getSelectTokens: () => mockSelectTokens
});

const mockDeployments = [
{ key: 'deployment1', label: 'Deployment 1' },
{ key: 'deployment2', label: 'Deployment 2' }
];

(DotrainOrderGui.getDeploymentKeys as Mock).mockResolvedValue(mockDeployments);

render(DeploymentSteps);
const urlInput = screen.getByPlaceholderText('Enter URL to .rain file');
const loadButton = screen.getByText('Load Strategy');

await userEvent.type(urlInput, 'https://example.com/strategy.rain');
await userEvent.click(loadButton);
render(DeploymentSteps, {
props: {
dotrain,
deployment: 'flare-sflr-wflr',
deploymentDetails: { name: 'Deployment 1', description: 'Description 1' }
}
});

await waitFor(() => {
expect(screen.getByText('Select Deployment')).toBeInTheDocument();
expect(screen.getByText('Select a deployment')).toBeInTheDocument();
expect(screen.getByText('Select Tokens')).toBeInTheDocument();
expect(
screen.getByText('Select the tokens that you want to use in your order.')
).toBeInTheDocument();
});
});

it('handles URL fetch errors', async () => {
global.fetch = vi.fn().mockRejectedValue(new Error('Failed to fetch'));

render(DeploymentSteps);
const urlInput = screen.getByPlaceholderText('Enter URL to .rain file');
const loadButton = screen.getByText('Load Strategy');

await userEvent.type(urlInput, 'https://example.com/strategy.rain');
await userEvent.click(loadButton);
it('shows error message when GUI initialization fails', async () => {
(DotrainOrderGui.chooseDeployment as Mock).mockRejectedValue(
new Error('Failed to initialize GUI')
);

render(DeploymentSteps, {
props: {
dotrain,
deployment: 'flare-sflr-wflr',
deploymentDetails: { name: 'Deployment 1', description: 'Description 1' }
}
});

await waitFor(() => {
expect(screen.getByText('No valid strategy exists at this URL')).toBeInTheDocument();
expect(screen.getByText('Error loading GUI')).toBeInTheDocument();
expect(screen.getByText('Failed to initialize GUI')).toBeInTheDocument();
});
});

it('initializes GUI when deployment is selected', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
text: () => Promise.resolve(JSON.stringify(dotrain))
it('shows deploy strategy button when all required fields are filled', async () => {
(DotrainOrderGui.chooseDeployment as Mock).mockResolvedValue({
getSelectTokens: () => [],
getCurrentDeployment: () => ({
deployment: {
order: {
inputs: [],
outputs: []
}
},
deposits: []
}),
getAllFieldDefinitions: () => []
});

const mockDeployments: string[] = ['deployment1', 'deployment2'];

(DotrainOrderGui.getDeploymentKeys as Mock).mockResolvedValue(mockDeployments);

render(DeploymentSteps);
const urlInput = screen.getByPlaceholderText('Enter URL to .rain file');
const loadButton = screen.getByText('Load Strategy');

await userEvent.type(urlInput, 'https://example.com/strategy.rain');
await userEvent.click(loadButton);

await waitFor(() => {
expect(screen.getByText('Select Deployment')).toBeInTheDocument();
expect(screen.getByText('Select a deployment')).toBeInTheDocument();
render(DeploymentSteps, {
props: {
dotrain,
deployment: 'flare-sflr-wflr',
deploymentDetails: { name: 'Deployment 1', description: 'Description 1' }
}
});

const dropdownButton = screen.getByTestId('dropdown-button');
await userEvent.click(dropdownButton);
const dropdown = screen.getByTestId('dropdown');
await userEvent.click(dropdown);
const deploymentOption = screen.getByText('deployment1');
await userEvent.click(deploymentOption);

await waitFor(() => {
expect(DotrainOrderGui.chooseDeployment).toHaveBeenCalled();
expect(screen.getByText('Deploy Strategy')).toBeInTheDocument();
});
});
});
34 changes: 34 additions & 0 deletions packages/ui-components/src/__tests__/DeploymentTile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { render, screen } from '@testing-library/svelte';
import { describe, it, expect, vi } from 'vitest';
import { goto } from '$app/navigation';
import DeploymentTile from '../lib/components/deployment/DeploymentTile.svelte';

// Mock the goto function
vi.mock('$app/navigation', () => ({
goto: vi.fn()
}));

describe('DeploymentTile', () => {
const mockProps = {
strategyName: 'test-strategy',
key: 'test-key',
name: 'Test Deployment',
description: 'This is a test deployment description'
};

it('renders the deployment name and description', () => {
render(DeploymentTile, mockProps);

expect(screen.getByText('Test Deployment')).toBeInTheDocument();
expect(screen.getByText('This is a test deployment description')).toBeInTheDocument();
});

it('navigates to the correct URL when clicked', async () => {
const { getByRole } = render(DeploymentTile, mockProps);

const button = getByRole('button');
await button.click();

expect(goto).toHaveBeenCalledWith('/deploy/test-strategy/test-key');
});
});
71 changes: 71 additions & 0 deletions packages/ui-components/src/__tests__/DeploymentsSection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { render, screen } from '@testing-library/svelte';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import DeploymentsSection from '../lib/components/deployment/DeploymentsSection.svelte';
import { DotrainOrderGui } from '@rainlanguage/orderbook/js_api';

// Mock the DotrainOrderGui
vi.mock('@rainlanguage/orderbook/js_api', () => ({
DotrainOrderGui: {
getDeploymentDetails: vi.fn()
}
}));

describe('DeploymentsSection', () => {
beforeEach(() => {
vi.clearAllMocks();
});

it('should render deployments when data is available', async () => {
const mockDeployments = new Map([
['key1', { name: 'Deployment 1', description: 'Description 1' }],
['key2', { name: 'Deployment 2', description: 'Description 2' }]
]);

vi.mocked(DotrainOrderGui.getDeploymentDetails).mockResolvedValue(mockDeployments);

render(DeploymentsSection, {
props: {
dotrain: 'test-dotrain',
strategyName: 'Test Strategy'
}
});

// Wait for deployments to load
const deployment1 = await screen.findByText('Deployment 1');
const deployment2 = await screen.findByText('Deployment 2');

expect(deployment1).toBeInTheDocument();
expect(deployment2).toBeInTheDocument();
});

it('should handle error when fetching deployments fails', async () => {
vi.mocked(DotrainOrderGui.getDeploymentDetails).mockRejectedValue(new Error('API Error'));

render(DeploymentsSection, {
props: {
dotrain: 'test-dotrain',
strategyName: 'Test Strategy'
}
});

const errorMessage = await screen.findByText(
'Error loading deployments: Error getting deployments.'
);
expect(errorMessage).toBeInTheDocument();
});

it('should fetch deployments when dotrain prop changes', async () => {
const { rerender } = render(DeploymentsSection, {
props: {
dotrain: '',
strategyName: 'Test Strategy'
}
});

expect(DotrainOrderGui.getDeploymentDetails).not.toHaveBeenCalled();

await rerender({ dotrain: 'new-dotrain', strategyName: 'Test Strategy' });

expect(DotrainOrderGui.getDeploymentDetails).toHaveBeenCalledWith('new-dotrain');
});
});
Loading

0 comments on commit 70603dc

Please sign in to comment.