mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-01-12 22:53:27 +08:00
feat: add base swap page and routing
This commit is contained in:
@@ -4,3 +4,5 @@ SEGMENT_WRITE_KEY=segmentwritekey
|
||||
SENTRY_DSN=sentrydsn
|
||||
TRANSAK_API_KEY=transakapikey
|
||||
WALLET_ENVIRONMENT=development
|
||||
|
||||
SWAP_ENABLED=true
|
||||
|
||||
2
.tool-versions
Normal file
2
.tool-versions
Normal file
@@ -0,0 +1,2 @@
|
||||
nodejs 16.13.0
|
||||
yarn 1.22.19
|
||||
1
src/app/features/swap/index.ts
Normal file
1
src/app/features/swap/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './use-swap-feature';
|
||||
5
src/app/features/swap/use-swap-feature.ts
Normal file
5
src/app/features/swap/use-swap-feature.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export function useSwapFeature() {
|
||||
return {
|
||||
swapIsEnabled: !!process.env.SWAP_ENABLED
|
||||
};
|
||||
}
|
||||
@@ -2,17 +2,23 @@ import { Suspense } from 'react';
|
||||
|
||||
import { Stack, StackProps } from '@stacks/ui';
|
||||
|
||||
import { useSwapFeature } from '@app/features/swap';
|
||||
|
||||
import { BuyButton } from './buy-button';
|
||||
import { ReceiveButton } from './receive-button';
|
||||
import { SendButton } from './send-button';
|
||||
import { SwapButton } from './swap-button';
|
||||
|
||||
export function HomeActions(props: StackProps) {
|
||||
const { swapIsEnabled } = useSwapFeature();
|
||||
|
||||
return (
|
||||
<Suspense fallback={<></>}>
|
||||
<Stack isInline mt={['base', 'base', 'unset']} spacing="base-tight" {...props}>
|
||||
<SendButton />
|
||||
<ReceiveButton />
|
||||
<BuyButton />
|
||||
{swapIsEnabled && <SwapButton />}
|
||||
</Stack>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
26
src/app/pages/home/components/swap-button.tsx
Normal file
26
src/app/pages/home/components/swap-button.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ButtonProps } from '@stacks/ui';
|
||||
import { HomePageSelectors } from '@tests/selectors/home.selectors';
|
||||
|
||||
import { RouteUrls } from '@shared/route-urls';
|
||||
|
||||
import { PrimaryButton } from '@app/components/primary-button';
|
||||
|
||||
import { HomeActionButton } from './tx-button';
|
||||
import { FiRefreshCw } from 'react-icons/fi';
|
||||
|
||||
export function SwapButton(props: ButtonProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<HomeActionButton
|
||||
label="Swap"
|
||||
icon={FiRefreshCw}
|
||||
buttonComponent={PrimaryButton}
|
||||
data-testid={HomePageSelectors.SwapBtn}
|
||||
onClick={() => navigate(RouteUrls.Swap)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
2
src/app/pages/swap/index.ts
Normal file
2
src/app/pages/swap/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './swap';
|
||||
export * from './swap-routes';
|
||||
29
src/app/pages/swap/swap-container.tsx
Normal file
29
src/app/pages/swap/swap-container.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import { Flex } from '@stacks/ui';
|
||||
|
||||
import { whenPageMode } from '@app/common/utils';
|
||||
import { ModalHeader } from '@app/components/modal-header';
|
||||
import { useRouteHeader } from '@app/common/hooks/use-route-header';
|
||||
|
||||
import { CENTERED_FULL_PAGE_MAX_WIDTH } from '@app/components/global-styles/full-page-styles';
|
||||
|
||||
export function SwapContainer() {
|
||||
useRouteHeader(<ModalHeader hideActions defaultGoBack title="Swap" />, true);
|
||||
|
||||
return whenPageMode({
|
||||
full: (
|
||||
<Flex
|
||||
maxHeight="90vh"
|
||||
border={['unset', '1px solid']}
|
||||
borderRadius={['unset', '16px']}
|
||||
borderColor={['unset', '#DCDDE2']}
|
||||
maxWidth={['100%', CENTERED_FULL_PAGE_MAX_WIDTH]}
|
||||
minWidth={['100%', CENTERED_FULL_PAGE_MAX_WIDTH]}
|
||||
>
|
||||
<Outlet />
|
||||
</Flex>
|
||||
),
|
||||
popup: <Outlet />,
|
||||
});
|
||||
}
|
||||
20
src/app/pages/swap/swap-routes.tsx
Normal file
20
src/app/pages/swap/swap-routes.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Route } from "react-router-dom";
|
||||
|
||||
import { RouteUrls } from "@shared/route-urls";
|
||||
import { AccountGate } from "@app/routes/account-gate";
|
||||
|
||||
import { Swap } from "./swap";
|
||||
import { SwapContainer } from "./swap-container";
|
||||
|
||||
export const swapRoutes = (
|
||||
<Route element={<SwapContainer />}>
|
||||
<Route
|
||||
path={RouteUrls.Swap}
|
||||
element={
|
||||
<AccountGate>
|
||||
<Swap />
|
||||
</AccountGate>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
);
|
||||
19
src/app/pages/swap/swap.tsx
Normal file
19
src/app/pages/swap/swap.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Text } from "@app/components/typography";
|
||||
import { Flex } from "@stacks/ui";
|
||||
import { SwapCryptoAssetSelectors } from "@tests/selectors/swap.selectors";
|
||||
|
||||
export function Swap() {
|
||||
return (
|
||||
<Flex
|
||||
data-testid={SwapCryptoAssetSelectors.SwapPageReady}
|
||||
alignItems={['left', 'center']}
|
||||
maxHeight={['unset', '85vh']}
|
||||
flexDirection="column"
|
||||
justifyContent="start"
|
||||
overflowY="auto"
|
||||
flexGrow={1}
|
||||
>
|
||||
<Text marginTop="15px" marginBottom="15px">Coming soon!</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -55,6 +55,7 @@ import { SendInscriptionSummary } from '@app/pages/send/ordinal-inscription/sent
|
||||
import { sendCryptoAssetFormRoutes } from '@app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes';
|
||||
import { SignOutConfirmDrawer } from '@app/pages/sign-out-confirm/sign-out-confirm';
|
||||
import { StacksMessageSigningRequest } from '@app/pages/stacks-message-signing-request/stacks-message-signing-request';
|
||||
import { swapRoutes } from '@app/pages/swap';
|
||||
import { TransactionRequest } from '@app/pages/transaction-request/transaction-request';
|
||||
import { UnauthorizedRequest } from '@app/pages/unauthorized-request/unauthorized-request';
|
||||
import { Unlock } from '@app/pages/unlock';
|
||||
@@ -353,6 +354,18 @@ function useAppRoutes() {
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={RouteUrls.RpcSignBip322Message}
|
||||
lazy={async () => {
|
||||
const { RpcSignBip322MessageRoute } = await import(
|
||||
'@app/pages/rpc-sign-bip322-message/rpc-sign-bip322-message'
|
||||
);
|
||||
return { Component: RpcSignBip322MessageRoute };
|
||||
}}
|
||||
/>
|
||||
|
||||
{swapRoutes}
|
||||
|
||||
{/* Catch-all route redirects to onboarding */}
|
||||
<Route path="*" element={<Navigate replace to={RouteUrls.Onboarding} />} />
|
||||
</Route>
|
||||
|
||||
@@ -106,4 +106,7 @@ export enum RouteUrls {
|
||||
// Shared legacy and rpc request routes
|
||||
RequestError = '/request-error',
|
||||
UnauthorizedRequest = '/unauthorized-request',
|
||||
|
||||
// Swap routes
|
||||
Swap = '/swap',
|
||||
}
|
||||
|
||||
7
tests/fixtures/fixtures.ts
vendored
7
tests/fixtures/fixtures.ts
vendored
@@ -1,8 +1,11 @@
|
||||
import { BrowserContext, test as base, chromium } from '@playwright/test';
|
||||
|
||||
import { GlobalPage } from '@tests/page-object-models/global.page';
|
||||
import { HomePage } from '@tests/page-object-models/home.page';
|
||||
import { OnboardingPage } from '@tests/page-object-models/onboarding.page';
|
||||
import { SendPage } from '@tests/page-object-models/send.page';
|
||||
import { SwapPage } from '@tests/page-object-models/swap.page';
|
||||
|
||||
import path from 'path';
|
||||
|
||||
interface TestFixtures {
|
||||
@@ -12,6 +15,7 @@ interface TestFixtures {
|
||||
homePage: HomePage;
|
||||
onboardingPage: OnboardingPage;
|
||||
sendPage: SendPage;
|
||||
swapPage: SwapPage
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,4 +59,7 @@ export const test = base.extend<TestFixtures>({
|
||||
sendPage: async ({ page }, use) => {
|
||||
await use(new SendPage(page));
|
||||
},
|
||||
swapPage: async ({ page }, use) => {
|
||||
await use(new SwapPage(page));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,6 +12,8 @@ export class HomePage {
|
||||
readonly drawerActionButton: Locator;
|
||||
readonly receiveButton: Locator;
|
||||
readonly sendButton: Locator;
|
||||
readonly swapButton: Locator;
|
||||
|
||||
readonly testNetworkSelector: string = createTestSelector(
|
||||
WalletDefaultNetworkConfigurationIds.testnet
|
||||
);
|
||||
@@ -21,12 +23,17 @@ export class HomePage {
|
||||
this.drawerActionButton = page.getByTestId(HomePageSelectors.DrawerHeaderActionBtn);
|
||||
this.receiveButton = page.getByTestId(HomePageSelectors.ReceiveCryptoAssetBtn);
|
||||
this.sendButton = page.getByTestId(HomePageSelectors.SendCryptoAssetBtn);
|
||||
this.swapButton = page.getByTestId(HomePageSelectors.SwapBtn);
|
||||
}
|
||||
|
||||
async goToReceiveModal() {
|
||||
await this.page.getByTestId(HomePageSelectors.ReceiveCryptoAssetBtn).click();
|
||||
}
|
||||
|
||||
async goToSwapPage() {
|
||||
await this.page.getByTestId(HomePageSelectors.SwapBtn).click();
|
||||
}
|
||||
|
||||
// Open issue with Playwright's ability to copyToClipboard from legacy tests:
|
||||
// https://github.com/microsoft/playwright/issues/8114#issuecomment-1103317576
|
||||
// Also, an open issue to consistently determine `isMac` in the workaround:
|
||||
|
||||
18
tests/page-object-models/swap.page.ts
Normal file
18
tests/page-object-models/swap.page.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Locator, Page } from '@playwright/test';
|
||||
import { SwapCryptoAssetSelectors } from '@tests/selectors/swap.selectors';
|
||||
|
||||
import { createTestSelector } from '@tests/utils';
|
||||
|
||||
export class SwapPage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async waitForSendPageReady() {
|
||||
await this.page.waitForSelector(createTestSelector(SwapCryptoAssetSelectors.SwapPageReady), {
|
||||
state: 'attached',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,5 @@ export enum HomePageSelectors {
|
||||
SendCryptoAssetBtn = 'send-crypto-asset-btn',
|
||||
ActivityTabBtn = 'tab-activity',
|
||||
BalancesTabBtn = 'tab-balances',
|
||||
SwapBtn = 'swap-btn',
|
||||
}
|
||||
|
||||
3
tests/selectors/swap.selectors.ts
Normal file
3
tests/selectors/swap.selectors.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export enum SwapCryptoAssetSelectors {
|
||||
SwapPageReady = 'swap-page-ready',
|
||||
}
|
||||
17
tests/specs/swap/swap.spec.ts
Normal file
17
tests/specs/swap/swap.spec.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { test } from '../../fixtures/fixtures';
|
||||
|
||||
test.describe('swap', () => {
|
||||
test.beforeEach(async ({ extensionId, globalPage, homePage, onboardingPage, swapPage }) => {
|
||||
await globalPage.setupAndUseApiCalls(extensionId);
|
||||
await onboardingPage.signInWithTestAccount(extensionId);
|
||||
await homePage.enableTestMode();
|
||||
await homePage.swapButton.click();
|
||||
await swapPage.waitForSendPageReady();
|
||||
});
|
||||
|
||||
test.describe('swap', () => {
|
||||
test('that it shows swap page', async ({ sendPage }) => {
|
||||
await test.expect(sendPage.page.getByText('Coming soon!')).toBeVisible();
|
||||
});
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user