Skip to content

Commit

Permalink
wallet-ext: enable e2e tests (MystenLabs#14123)
Browse files Browse the repository at this point in the history
## Description 

* adds support for v1 requests for test faucet

closes [APPS-1751](https://mysten.atlassian.net/browse/APPS-1751)

## Test Plan 

How did you test the new or updated feature?

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [ ] protocol change
- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
pchrysochoidis authored Oct 10, 2023
1 parent c32ec6e commit 7694d49
Show file tree
Hide file tree
Showing 16 changed files with 199 additions and 82 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/wallet/src/background/auto-lock-accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ export const notifyUserActive = throttle(
() => {
setupAutoLockAlarm();
},
{ noLeading: true },
{ noTrailing: true },
);
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export function AutoLockSelector({ disabled }: AutoLockSelectorProps) {
disabled={disabled || !timerEnabled}
type="number"
{...register('autoLock.timer')}
data-testid="auto-lock-timer"
/>
<SelectField
disabled={disabled || !timerEnabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function LockUnlockButton({ isLocked, onClick, isLoading }: LockUnlockBut
<button
className="appearance-none bg-transparent border-none cursor-pointer text-steel hover:text-hero-dark ml-auto flex items-center justify-center"
onClick={onClick}
data-testid={isLocked ? 'unlock-account-button' : 'lock-account-button'}
>
{isLoading ? (
<LoadingIndicator />
Expand Down
12 changes: 8 additions & 4 deletions apps/wallet/src/ui/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const HIDDEN_MENU_PATHS = [
'/apps/disconnectapp',
];

const notifyUserActiveInterval = 15 * 1000; // 15 seconds
const notifyUserActiveInterval = 5 * 1000; // 5 seconds

const App = () => {
const dispatch = useAppDispatch();
Expand Down Expand Up @@ -137,9 +137,13 @@ const App = () => {
if (!autoLockEnabled) {
return;
}
const sendUpdateThrottled = throttle(notifyUserActiveInterval, () => {
backgroundClient.notifyUserActive();
});
const sendUpdateThrottled = throttle(
notifyUserActiveInterval,
() => {
backgroundClient.notifyUserActive();
},
{ noTrailing: true },
);
document.addEventListener('mousemove', sendUpdateThrottled);
document.addEventListener('keydown', sendUpdateThrottled);
return () => {
Expand Down
6 changes: 3 additions & 3 deletions apps/wallet/tests/balanceChanges.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { expect, test } from './fixtures';
import { createWallet, importWallet } from './utils/auth';
import { generateKeypairFromMnemonic, requestSuiFromFaucet } from './utils/localnet';

test.skip();

const receivedAddressMnemonic = [
'beef',
'beef',
Expand Down Expand Up @@ -40,12 +38,14 @@ const currentWalletMnemonic = [
const COIN_TO_SEND = 20;

test('request SUI from local faucet', async ({ page, extensionUrl }) => {
const timeout = 30_000;
test.setTimeout(timeout);
await createWallet(page, extensionUrl);
await page.getByRole('navigation').getByRole('link', { name: 'Coins' }).click();

const originalBalance = await page.getByTestId('coin-balance').textContent();
await page.getByTestId('faucet-request-button').click();
await expect(page.getByText(/SUI Received/i)).toBeVisible();
await expect(page.getByText(/SUI Received/i)).toBeVisible({ timeout });
await expect(page.getByTestId('coin-balance')).not.toHaveText(`${originalBalance}SUI`);
});

Expand Down
4 changes: 2 additions & 2 deletions apps/wallet/tests/demo-app/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function App() {
<button
onClick={async () => {
setError(null);
const txb = getDemoTransaction(accounts[0]?.address);
const txb = getDemoTransaction(accounts[0]?.address || '');
try {
await suiWallet.features[
'sui:signAndExecuteTransactionBlock'
Expand All @@ -112,7 +112,7 @@ function App() {
<button
onClick={async () => {
setError(null);
const txb = getDemoTransaction(accounts[0]?.address);
const txb = getDemoTransaction(accounts[0]?.address || '');
try {
await suiWallet.features['sui:signTransactionBlock'].signTransactionBlock({
transactionBlock: txb,
Expand Down
39 changes: 16 additions & 23 deletions apps/wallet/tests/lock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,32 @@
import { expect, test } from './fixtures';
import { createWallet } from './utils/auth';

test.skip();

test('wallet unlock', async ({ page, context, extensionUrl }) => {
test.skip(true, 'Skip while zkLogin work is in progress');

test('account lock-unlock', async ({ page, context, extensionUrl }) => {
await createWallet(page, extensionUrl);
await page.getByTestId('menu').click();
await page.getByRole('button', { name: /Lock Wallet/ }).click();
await page.getByLabel('Enter Password').fill('mystenlabs');
await page.getByRole('button', { name: /Unlock Wallet/ }).click();
await expect(page.getByTestId('coin-page')).toBeVisible();
await page.getByTestId('lock-account-button').click();
await page.getByTestId('unlock-account-button').click();
await page.getByPlaceholder('Password').fill('mystenlabs');
await page.getByRole('button', { name: /Unlock/ }).click();
await expect(page.getByTestId('coin-balance')).toBeVisible();
});

test('wallet auto-lock', async ({ page, extensionUrl }) => {
test.skip(true, 'Skip while zkLogin work is in progress');

test.skip(
process.env.CI !== 'true',
'Runs only on CI since it takes at least 1 minute to complete',
);
test.setTimeout(65 * 1000);
await createWallet(page, extensionUrl);
await page.getByTestId('menu').click();
await page.getByLabel(/Open settings menu/).click();
await page.getByText(/Auto-lock/).click();
await page.getByPlaceholder(/Auto lock minutes/i).fill('1');
await page.getByRole('button', { name: /Save/i }).click();
await page.getByText(/Auto lock updated/i);
await page.evaluate(() => {
Object.defineProperty(document, 'visibilityState', {
value: 'hidden',
});
document.dispatchEvent(new Event('visibilitychange'));
});
await page.getByLabel(/Auto-lock after I am inactive for/i).click();
await page.getByTestId('auto-lock-timer').fill('1');
await page.getByRole('combobox').click();
await page.getByRole('option', { name: /Minute/ }).click();
await page.getByText('Save').click();
await page.getByText(/Saved/i);
await page.getByTestId('close-icon').click();
await page.getByLabel(/Close settings menu/).click();
await page.waitForTimeout(60 * 1000);
await expect(page.getByRole('button', { name: /Unlock Wallet/ })).toBeVisible();
await expect(page.getByRole('button', { name: /Unlock/ })).toBeVisible();
});
19 changes: 4 additions & 15 deletions apps/wallet/tests/onboarding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,18 @@
// SPDX-License-Identifier: Apache-2.0

import { expect, test } from './fixtures';
import { createWallet } from './utils/auth';
import { createWallet, importWallet } from './utils/auth';
import { generateKeypair } from './utils/localnet';

test.skip();

test('create new wallet', async ({ page, extensionUrl }) => {
await createWallet(page, extensionUrl);
await expect(page.getByTestId('apps-page')).toBeVisible();
await page.getByRole('navigation').getByRole('link', { name: 'Coins' }).click();
await expect(page.getByTestId('coin-page')).toBeVisible();
});

test('import wallet', async ({ page, extensionUrl }) => {
const { mnemonic, keypair } = await generateKeypair();

await page.goto(extensionUrl);
await page.getByRole('link', { name: /Get Started/ }).click();
await page.getByRole('link', { name: /Import an Existing Wallet/ }).click();
await page.getByLabel('Enter your 12-word Recovery Phrase').type(mnemonic);
await page.getByRole('button', { name: /Continue/ }).click();
await page.getByLabel('Create Password').fill('mystenlabs');
await page.getByLabel('Confirm Password').fill('mystenlabs');
await page.getByRole('button', { name: /Import/ }).click();
await page.getByRole('link', { name: /Open Sui Wallet/ }).click();
await page.getByTestId('bullshark-dismiss').click();
importWallet(page, extensionUrl, mnemonic);
await page.getByRole('navigation').getByRole('link', { name: 'Coins' }).click();
await expect(
page.getByText(keypair.getPublicKey().toSuiAddress().slice(0, 6)).first(),
Expand Down
2 changes: 0 additions & 2 deletions apps/wallet/tests/sites-to-cs-messaging.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { expect, test } from './fixtures';
import { createWallet } from './utils/auth';
import { demoDappConnect } from './utils/dapp-connect';

test.skip();

function getInAppMessage(page: Page, id: string) {
return page.evaluate(
(anId) =>
Expand Down
11 changes: 5 additions & 6 deletions apps/wallet/tests/staking.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { createWallet } from './utils/auth';
const TEST_TIMEOUT = 45 * 1000;
const STAKE_AMOUNT = 100;

test.skip('staking', async ({ page, extensionUrl }) => {
test.setTimeout(TEST_TIMEOUT);
test('staking', async ({ page, extensionUrl }) => {
test.setTimeout(4 * TEST_TIMEOUT);

await createWallet(page, extensionUrl);

Expand All @@ -27,13 +27,12 @@ test.skip('staking', async ({ page, extensionUrl }) => {
await expect(page.getByTestId('transaction-status')).toHaveText('Transaction Success');

await page.getByTestId('close-icon').click();
await expect(page.getByText(`Currently Staked${STAKE_AMOUNT} SUI`)).toBeVisible();

await page.getByText(`Currently Staked${STAKE_AMOUNT} SUI`).click();
await expect(page.getByText(/Starts Earning now/)).toBeVisible({
await expect(page.getByText(`Currently Staked${STAKE_AMOUNT} SUI`)).toBeVisible({
timeout: TEST_TIMEOUT,
});

await page.getByText(`Currently Staked${STAKE_AMOUNT} SUI`).click();
await expect(page.getByTestId('stake-card')).toBeVisible({ timeout: 3 * TEST_TIMEOUT });
await page.getByTestId('stake-card').click();
await page.getByTestId('unstake-button').click();
await page.getByRole('button', { name: 'Unstake Now' }).click();
Expand Down
2 changes: 0 additions & 2 deletions apps/wallet/tests/tabs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import { expect, test } from './fixtures';
import { createWallet } from './utils/auth';

test.skip();

test('Assets tab', async ({ page, extensionUrl }) => {
await createWallet(page, extensionUrl);
await page.getByRole('navigation').getByRole('link', { name: 'Assets' }).click();
Expand Down
37 changes: 17 additions & 20 deletions apps/wallet/tests/utils/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,27 @@ export const PASSWORD = 'mystenlabs';

export async function createWallet(page: Page, extensionUrl: string) {
await page.goto(extensionUrl);
await page.getByRole('link', { name: /Get Started/ }).click();
await page.getByRole('link', { name: /Create a New Wallet/ }).click();
await page.getByLabel('Create Password').fill('mystenlabs');
await page.getByLabel('Confirm Password').fill('mystenlabs');
await page.locator('label', { has: page.locator('input[type=checkbox]') }).click();
await page.getByRole('link', { name: /More Options/ }).click();
await page.getByRole('link', { name: /Create a new Passphrase Account/ }).click();
await page.getByLabel('Create Account Password').fill('mystenlabs');
await page.getByLabel('Confirm Account Password').fill('mystenlabs');
await page.getByLabel('I read and agreed to the').click();
await page.getByRole('button', { name: /Create Wallet/ }).click();
await page.locator('label', { has: page.locator('input[type=checkbox]') }).click();
await page.getByRole('link', { name: /Open Sui Wallet/ }).click();
await page.getByTestId('bullshark-dismiss').click();
}

export async function importWallet(page: Page, extensionUrl: string, mnemonic: string[]) {
export async function importWallet(page: Page, extensionUrl: string, mnemonic: string | string[]) {
await page.goto(extensionUrl);
await page.getByRole('link', { name: /Get Started/ }).click();
await page.getByRole('link', { name: /Import an Existing Wallet/ }).click();
const inputs = await page.locator('input[type=password]');
const inputsCount = await inputs.count();
for (let i = 0; i < inputsCount; i++) {
await inputs.nth(i).fill(mnemonic[i]);
}
await page.getByRole('button', { name: /Continue/ }).click();
await page.getByLabel('Create Password').fill(PASSWORD);
await page.getByLabel('Confirm Password').fill(PASSWORD);
await page.getByRole('button', { name: /Import/ }).click();
await page.getByRole('link', { name: /Open Sui Wallet/ }).click();
await page.getByTestId('bullshark-dismiss').click();
await page.getByRole('link', { name: /More Options/ }).click();
await page.getByRole('link', { name: /Import Passphrase/ }).click();
await page
.getByPlaceholder('Password')
.first()
.type(typeof mnemonic === 'string' ? mnemonic : mnemonic.join(' '));
await page.getByRole('button', { name: /Add Account/ }).click();
await page.getByLabel('Create Account Password').fill(PASSWORD);
await page.getByLabel('Confirm Account Password').fill(PASSWORD);
await page.getByLabel('I read and agreed to the').click();
await page.getByRole('button', { name: /Create Wallet/ }).click();
}
82 changes: 81 additions & 1 deletion crates/sui-cluster-test/src/faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use fastcrypto::encoding::{Encoding, Hex};
use std::collections::HashMap;
use std::env;
use std::sync::Arc;
use sui_faucet::{Faucet, FaucetConfig, FaucetResponse, SimpleFaucet};
use sui_faucet::{
BatchFaucetResponse, BatchStatusFaucetResponse, Faucet, FaucetConfig, FaucetResponse,
SimpleFaucet,
};
use sui_types::base_types::SuiAddress;
use sui_types::crypto::KeypairTraits;
use tracing::{debug, info, info_span, Instrument};
Expand Down Expand Up @@ -51,6 +54,8 @@ impl FaucetClientFactory {
#[async_trait]
pub trait FaucetClient {
async fn request_sui_coins(&self, request_address: SuiAddress) -> FaucetResponse;
async fn batch_request_sui_coins(&self, request_address: SuiAddress) -> BatchFaucetResponse;
async fn get_batch_send_status(&self, task_id: Uuid) -> BatchStatusFaucetResponse;
}

/// Client for a remote faucet that is accessible by POST requests
Expand Down Expand Up @@ -96,6 +101,63 @@ impl FaucetClient for RemoteFaucetClient {
panic!("Failed to get gas tokens with error: {}", error)
};

faucet_response
}
async fn batch_request_sui_coins(&self, request_address: SuiAddress) -> BatchFaucetResponse {
let gas_url = format!("{}/v1/gas", self.remote_url);
debug!("Getting coin from remote faucet {}", gas_url);
let data = HashMap::from([("recipient", Hex::encode(request_address))]);
let map = HashMap::from([("FixedAmountRequest", data)]);

let auth_header = match env::var("FAUCET_AUTH_HEADER") {
Ok(val) => val,
_ => "".to_string(),
};

let response = reqwest::Client::new()
.post(&gas_url)
.header("Authorization", auth_header)
.json(&map)
.send()
.await
.unwrap_or_else(|e| panic!("Failed to talk to remote faucet {:?}: {:?}", gas_url, e));
let full_bytes = response.bytes().await.unwrap();
let faucet_response: BatchFaucetResponse = serde_json::from_slice(&full_bytes)
.map_err(|e| anyhow::anyhow!("json deser failed with bytes {:?}: {e}", full_bytes))
.unwrap();

if let Some(error) = faucet_response.error {
panic!("Failed to get gas tokens with error: {}", error)
};

faucet_response
}
async fn get_batch_send_status(&self, task_id: Uuid) -> BatchStatusFaucetResponse {
let status_url = format!("{}/v1/status/{}", self.remote_url, task_id);
debug!(
"Checking status for task {} from remote faucet {}",
task_id.to_string(),
status_url
);

let auth_header = match env::var("FAUCET_AUTH_HEADER") {
Ok(val) => val,
_ => "".to_string(),
};

let response = reqwest::Client::new()
.get(&status_url)
.header("Authorization", auth_header)
.send()
.await
.unwrap_or_else(|e| {
panic!("Failed to talk to remote faucet {:?}: {:?}", status_url, e)
});
let full_bytes = response.bytes().await.unwrap();
let faucet_response: BatchStatusFaucetResponse = serde_json::from_slice(&full_bytes)
.map_err(|e| anyhow::anyhow!("json deser failed with bytes {:?}: {e}", full_bytes))
.unwrap();

faucet_response
}
}
Expand All @@ -122,4 +184,22 @@ impl FaucetClient for LocalFaucetClient {

receipt.into()
}
async fn batch_request_sui_coins(&self, request_address: SuiAddress) -> BatchFaucetResponse {
let receipt = self
.simple_faucet
.batch_send(Uuid::new_v4(), request_address, &[200_000_000_000; 5])
.await
.unwrap_or_else(|err| panic!("Failed to get gas tokens with error: {}", err));

receipt.into()
}
async fn get_batch_send_status(&self, task_id: Uuid) -> BatchStatusFaucetResponse {
let status = self
.simple_faucet
.get_batch_send_status(task_id)
.await
.unwrap_or_else(|err| panic!("Failed to get gas tokens with error: {}", err));

status.into()
}
}
1 change: 1 addition & 0 deletions crates/sui-test-validator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ axum.workspace = true
tower.workspace = true
tower-http.workspace = true
http.workspace = true
uuid.workspace = true

sui-faucet.workspace = true
sui-cluster-test.workspace = true
Expand Down
Loading

0 comments on commit 7694d49

Please sign in to comment.