From 97f3534bb77e6db63aa5a57412ad098ed8b3fbb9 Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Tue, 13 Jul 2021 13:05:19 -0300 Subject: [PATCH 1/6] Split and Refactor basic-ui test suite --- packages/test-project/package.json | 6 +- packages/test-project/tests/common/helpers.js | 11 +- packages/test-project/tests/common/tests.js | 31 ++- .../tests/ui-accounts-e2e.test.js | 90 +++++++++ .../tests/ui-transactions-e2e.test.js | 182 ++++++++++++++++++ .../src/components/Account/AddAssetConfirm.ts | 2 +- packages/ui/src/pages/SendAlgos.ts | 2 +- 7 files changed, 312 insertions(+), 12 deletions(-) create mode 100644 packages/test-project/tests/ui-accounts-e2e.test.js create mode 100644 packages/test-project/tests/ui-transactions-e2e.test.js diff --git a/packages/test-project/package.json b/packages/test-project/package.json index eae79c13..4ca24ab2 100644 --- a/packages/test-project/package.json +++ b/packages/test-project/package.json @@ -14,10 +14,12 @@ "scripts": { "github": "jest -i --group=github", "basic-dapp": "jest --group=basic-dapp", - "basic-ui": "jest --group=basic-ui", + "ui": "jest --group=ui", + "ui/accounts": "jest --group=ui/accounts", + "ui/transactions": "jest --group=ui/transactions", "app-dapp": "jest --group=app-dapp", - "dapp/multisig": "jest --group=dapp/multisig", "dapp": "jest --group=dapp", + "dapp/multisig": "jest --group=dapp/multisig", "coveragetest": "jest --coverage=true --coverageDirectory ../test-project/coverage --projects ../crypto ../extension ../storage ../common ../dapp --runInBand && bash -c \"start chrome \"$(realpath ./coverage/lcov-report/index.html\"\")", "test": "jest -i --group=-github --group=-dapp-storage" } diff --git a/packages/test-project/tests/common/helpers.js b/packages/test-project/tests/common/helpers.js index b218e477..c1fe031b 100644 --- a/packages/test-project/tests/common/helpers.js +++ b/packages/test-project/tests/common/helpers.js @@ -26,15 +26,23 @@ async function selectAccount(account) { await extensionPage.waitForTimeout(500); } +async function openAccountDetails(account) { + await selectAccount(account); + await extensionPage.waitForSelector('#accountName'); + await expect(extensionPage.$eval('#accountName', (e) => e.innerText)).resolves.toBe(account.name); + await extensionPage.click('#showDetails'); +} + async function goBack() { await extensionPage.click('#goBack'); - await extensionPage.waitForTimeout(500); + await extensionPage.waitForTimeout(250); } async function closeModal() { const modalSelector = '.modal.is-active button.modal-close'; await extensionPage.waitForSelector(modalSelector); await extensionPage.click(modalSelector); + await extensionPage.waitForTimeout(250); } // Dapp Helpers @@ -155,6 +163,7 @@ function appendSignToMultisigTransaction(partialTransaction, msigParams, mnemoni module.exports = { openExtension, selectAccount, + openAccountDetails, goBack, closeModal, getPopup, diff --git a/packages/test-project/tests/common/tests.js b/packages/test-project/tests/common/tests.js index cc2db8b7..8cb15e68 100644 --- a/packages/test-project/tests/common/tests.js +++ b/packages/test-project/tests/common/tests.js @@ -1,5 +1,5 @@ const { wallet, extension } = require('./constants'); -const { selectAccount, goBack, closeModal, getPopup } = require('./helpers'); +const { openAccountDetails, goBack, closeModal, getPopup } = require('./helpers'); // Common Tests async function WelcomePage() { @@ -55,13 +55,12 @@ async function ImportAccount(account) { await extensionPage.click('#authButton'); }); + VerifyAccount(account); +} + +async function VerifyAccount(account) { test(`Verify Account Info (${account.name})`, async () => { - await selectAccount(account); - await extensionPage.waitForSelector('#accountName'); - await expect(extensionPage.$eval('#accountName', (e) => e.innerText)).resolves.toBe( - account.name - ); - await extensionPage.click('#showDetails'); + await openAccountDetails(account); await expect(extensionPage.$eval('#accountAddress', (e) => e.innerText)).resolves.toBe( account.address ); @@ -70,6 +69,22 @@ async function ImportAccount(account) { }); } +async function DeleteAccount(account) { + test(`Delete Account (${account.name})`, async () => { + await openAccountDetails(account); + await extensionPage.click('#deleteAccount'); + await extensionPage.type('#enterPassword', wallet.password); + await extensionPage.waitForTimeout(200); + await extensionPage.click('#authButton'); + }); + + test('Verify Account Deleted', async () => { + await extensionPage.waitForSelector('#addAccount'); + const accountSelector = '#account_' + account.name.replace(/\s/g, ''); + await expect(extensionPage.select(accountSelector)).rejects.toThrow(); + }); +} + // Dapp Tests async function ConnectAlgoSigner() { test('Expose Authorize Functions', async () => { @@ -108,5 +123,7 @@ module.exports = { SelectTestNetLedger, CreateWallet, ImportAccount, + VerifyAccount, + DeleteAccount, ConnectAlgoSigner, }; diff --git a/packages/test-project/tests/ui-accounts-e2e.test.js b/packages/test-project/tests/ui-accounts-e2e.test.js new file mode 100644 index 00000000..97af9573 --- /dev/null +++ b/packages/test-project/tests/ui-accounts-e2e.test.js @@ -0,0 +1,90 @@ +/** + * Basic e2e tests for the AlgoSigner UI + * + * @group ui/accounts + */ + +const { wallet } = require('./common/constants'); +const { openExtension } = require('./common/helpers'); +const { CreateWallet, VerifyAccount, DeleteAccount } = require('./common/tests'); + +describe('Wallet Setup', () => { + beforeAll(async () => { + await openExtension(); + }); + + CreateWallet(); +}); + +// Create a new account in AlgoSigner +describe('Create Account', () => { + const createdAccount = { + name: 'Created-Account', + }; + let mnemonicArray = []; + + test('Create An Account, Step 1 - Enter Account Name', async () => { + await extensionPage.waitForSelector('#addAccount'); + await extensionPage.click('#addAccount'); + await extensionPage.waitForSelector('#createAccount'); + await extensionPage.click('#createAccount'); + await extensionPage.type('#setAccountName', createdAccount.name); + await extensionPage.click('#nextStep'); + }); + + test('Create An Account, Step 2 - Get Mnemonic', async () => { + await extensionPage.click('#accountAddress'); + createdAccount.address = await extensionPage.$eval('#accountAddress', (e) => e.innerText); // setup for another test + + for (let i = 1; i <= 25; i++) { + mnemonicArray[i - 1] = await extensionPage.$eval( + `[data-key-index="${i}"]`, + (e) => e.dataset['word'] + ); + } + await extensionPage.waitForSelector('#recordCheckbox'); + await extensionPage.click('#recordCheckbox'); + await extensionPage.click('#nextStep'); + }); + + test('Create An Account, Step 3 - Use Mnemonic', async () => { + await extensionPage.waitForSelector('#enterMnemonic'); + + // We test the remove word functionality by first filling with random words + const randomWords = []; + for (let i = 0; i < 5; i++) { + randomWords.push(mnemonicArray[i * 5 + Math.round(Math.random() * 4)]); + await extensionPage.click(`#${randomWords[i]}:not([disabled]):not(.is-link)`); + } + await expect(extensionPage.$eval('#enterMnemonic', (e) => e.value)).resolves.toBe( + randomWords.join(' ') + ); + await extensionPage.waitForTimeout(200); + for (let i = 0; i < 5; i++) { + await extensionPage.click(`button.is-small.is-link`); + } + await expect(extensionPage.$eval('#enterMnemonic', (e) => e.value)).resolves.toBe(''); + await extensionPage.waitForTimeout(200); + + // We build the actual mnemonic + for (let i = 0; i < 25; i++) { + await extensionPage.click(`#${mnemonicArray[i]}:not([disabled]):not(.is-link)`); + } + await expect(extensionPage.$eval('#enterMnemonic', (e) => e.value)).resolves.toBe( + mnemonicArray.join(' ') + ); + + await extensionPage.click('#nextStep'); + }); + + test('Create an Account, Step 4 - Write Account into Storage', async () => { + await extensionPage.waitForSelector('#enterPassword'); + await extensionPage.type('#enterPassword', wallet.password); + await extensionPage.waitForTimeout(200); + await extensionPage.click('#authButton'); + }); + + VerifyAccount(createdAccount); + + DeleteAccount(createdAccount); +}); diff --git a/packages/test-project/tests/ui-transactions-e2e.test.js b/packages/test-project/tests/ui-transactions-e2e.test.js new file mode 100644 index 00000000..40a6762d --- /dev/null +++ b/packages/test-project/tests/ui-transactions-e2e.test.js @@ -0,0 +1,182 @@ +/** + * Basic e2e tests for the AlgoSigner UI + * + * @group ui/transactions + */ + +const { accounts, wallet } = require('./common/constants'); +const { openExtension, selectAccount, closeModal, goBack } = require('./common/helpers'); +const { CreateWallet, ImportAccount, DeleteAccount } = require('./common/tests'); + +describe('Wallet Setup', () => { + beforeAll(async () => { + await openExtension(); + }); + + CreateWallet(); + + ImportAccount(accounts.ui); +}); + +describe('UI Transactions Tests', () => { + const amount = Math.ceil(Math.random() * 9); // txn size, modify multiplier for bulk + + let txId; // returned tx id from send txn + let txTitle; // for tx verification + + beforeEach(async () => { + jest.setTimeout(15000); + }); + + afterAll(async () => { + extensionPage.close(); + }); + + const verifyTransaction = async () => { + const txSelector = `[data-transaction-id="${txId}"]`; + await extensionPage.waitForSelector(txSelector); + await extensionPage.click(txSelector); + await expect(extensionPage.$eval('#txTitle', (e) => e.innerText)).resolves.toBe(txTitle); + await expect( + extensionPage.$eval( + '.modal.is-active [data-transaction-id]', + (e) => e.dataset['transactionId'] + ) + ).resolves.toBe(txId); + await expect( + extensionPage.$eval( + '.modal.is-active [data-transaction-sender]', + (e) => e.dataset['transactionSender'] + ) + ).resolves.toBe(accounts.ui.address); + await closeModal(); + }; + + const returnToAccount = async () => { + await extensionPage.waitForTimeout(1000); + await extensionPage.click('#backToAccount'); + await extensionPage.waitForTimeout(1000); + return; + }; + + test('Send Algos Transaction', async () => { + await selectAccount(accounts.ui); + + await extensionPage.click('#sendTransfer'); + await extensionPage.waitForTimeout(100); + await extensionPage.type('#transferAmount', amount.toString()); + await extensionPage.type('#toAddress', accounts.ui.address); + await extensionPage.type('#note', 'AutoTest Send Algo'); + await extensionPage.click('#submitTransfer'); + await extensionPage.waitForSelector('#enterPassword'); + await extensionPage.type('#enterPassword', wallet.password); + await extensionPage.waitForSelector('#authButton'); + await extensionPage.click('#authButton'); + await extensionPage.waitForSelector('#txId'); + // setup tx details for next test + txId = await extensionPage.$eval('#txId', (e) => e.innerText); + txTitle = 'Payment'; + await returnToAccount(); + }); + + test('Verify transaction', verifyTransaction); + + test('Check Asset details', async () => { + const assetSelector = '[data-asset-id]'; + await extensionPage.waitForSelector(assetSelector); + const assetId = await extensionPage.$eval(assetSelector, (e) => e.dataset['assetId']); + const assetBalance = await extensionPage.$eval(assetSelector, (e) => e.dataset['assetBalance']); + await extensionPage.click(assetSelector); + await expect( + extensionPage.$eval(`.modal ${assetSelector}`, (e) => e.dataset['assetId']) + ).resolves.toBe(assetId); + await expect( + extensionPage.$eval(`.modal ${assetSelector}`, (e) => e.dataset['assetBalance']) + ).resolves.toBe(assetBalance); + await closeModal(); + const thereIsFullAssetList = await extensionPage.$('#showAssets'); + if (thereIsFullAssetList) { + await extensionPage.click('#showAssets'); + await extensionPage.waitForSelector(`.modal [data-asset-id="${assetId}"]`); + await closeModal(); + } + await goBack(); + }); + + test('Send Asset Transaction', async () => { + await selectAccount(accounts.ui); + + await extensionPage.click('#sendTransfer'); + await extensionPage.waitForTimeout(100); + await extensionPage.click('#selectAsset'); + await extensionPage.waitForTimeout(100); + await extensionPage.waitForSelector('#asset-13169404'); + await extensionPage.click('#asset-13169404'); + await extensionPage.waitForTimeout(500); + // Test correct decimal handling + await extensionPage.type('#transferAmount', `0.000000000${amount}`); + const actualAmount = await extensionPage.$eval('#transferAmount', (e) => { + const inputValue = e.value; + e.value = ''; + return inputValue; + }); + expect(actualAmount).toMatch('0.000000'); + // Test actual transfer + await extensionPage.type('#transferAmount', `0.00000${amount}`); + await extensionPage.type('#toAddress', accounts.ui.address); + await extensionPage.type('#note', 'AutoTest Send E2E Asset'); + await extensionPage.click('#submitTransfer'); + await extensionPage.waitForSelector('#enterPassword'); + await extensionPage.type('#enterPassword', wallet.password); + await extensionPage.waitForSelector('#authButton'); + await extensionPage.click('#authButton'); + await extensionPage.waitForSelector('#txId'); + txId = await extensionPage.$eval('#txId', (e) => e.innerText); + txTitle = 'Asset transfer'; + await returnToAccount(); + }); + + test('Verify transaction', verifyTransaction); + + test('Transaction Errors: OverSpend', async () => { + await extensionPage.click('#sendTransfer'); + await extensionPage.waitForSelector('#transferAmount'); + await extensionPage.type('#transferAmount', '900000'); + await extensionPage.type('#toAddress', accounts.ui.address); + await extensionPage.type('#note', 'AutoTest Overspend Algo'); + await extensionPage.click('#submitTransfer'); + await extensionPage.waitForSelector('#enterPassword'); + await extensionPage.type('#enterPassword', wallet.password); + await extensionPage.waitForSelector('#authButton'); + await extensionPage.click('#authButton'); + await extensionPage.waitForSelector('#tx-error'); + + let pageError = await extensionPage.$eval('#tx-error', (e) => e.innerText); + await expect(pageError).toMatch("Overspending. Your account doesn't have sufficient funds."); + + await closeModal(); + await goBack(); + }); + + test('Transaction Errors: Invalid Field - Amount', async () => { + await extensionPage.click('#sendTransfer'); + await extensionPage.waitForSelector('#transferAmount'); + await extensionPage.type('#transferAmount', '9999999999.999999'); + await extensionPage.type('#toAddress', accounts.ui.address); + await extensionPage.type('#note', 'AutoTest Invalid Amount'); + await extensionPage.click('#submitTransfer'); + await extensionPage.waitForSelector('#enterPassword'); + await extensionPage.type('#enterPassword', wallet.password); + await extensionPage.waitForSelector('#authButton'); + await extensionPage.click('#authButton'); + await extensionPage.waitForSelector('#tx-error'); + + let pageError = await extensionPage.$eval('#tx-error', (e) => e.innerText); + expect(pageError).toMatch('One or more fields are not valid. Please check and try again.'); + + await closeModal(); + await goBack(); + }); + + DeleteAccount(accounts.ui); +}); diff --git a/packages/ui/src/components/Account/AddAssetConfirm.ts b/packages/ui/src/components/Account/AddAssetConfirm.ts index f5dc15cd..1fa729da 100644 --- a/packages/ui/src/components/Account/AddAssetConfirm.ts +++ b/packages/ui/src/components/Account/AddAssetConfirm.ts @@ -122,7 +122,7 @@ const AddAssetConfirm: FunctionalComponent = (props: any) => { From c6eca29bd9049939059b388df456b140d7f0b9c1 Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Wed, 14 Jul 2021 11:32:15 -0300 Subject: [PATCH 5/6] Various fixes --- packages/test-project/package.json | 2 +- .../test-project/tests/dapp-signtxn.test.js | 68 +++++++++---------- .../tests/ui-accounts-e2e.test.js | 3 +- .../tests/ui-transactions-e2e.test.js | 2 + 4 files changed, 39 insertions(+), 36 deletions(-) diff --git a/packages/test-project/package.json b/packages/test-project/package.json index ba4125ec..b045583d 100644 --- a/packages/test-project/package.json +++ b/packages/test-project/package.json @@ -18,7 +18,7 @@ "ui/accounts": "jest --group=ui/accounts", "ui/transactions": "jest --group=ui/transactions", "app-dapp": "jest --group=app-dapp", - "dapp": "jest --group=dapp", + "dapp": "jest --group=dapp --group=-dapp-storage", "dapp/multisig": "jest --group=dapp/multisig", "dapp/signtxn": "jest --group=dapp/signtxn", "coveragetest": "jest --coverage=true --coverageDirectory ../test-project/coverage --projects ../crypto ../extension ../storage ../common ../dapp --runInBand && bash -c \"start chrome \"$(realpath ./coverage/lcov-report/index.html\"\")", diff --git a/packages/test-project/tests/dapp-signtxn.test.js b/packages/test-project/tests/dapp-signtxn.test.js index 2ff9edb6..e5accff6 100644 --- a/packages/test-project/tests/dapp-signtxn.test.js +++ b/packages/test-project/tests/dapp-signtxn.test.js @@ -224,42 +224,42 @@ describe('Single and Global Transaction Use cases', () => { expect(decodedTransaction.msig.subsig.length).toBe(3); expect(decodedTransaction.msig.subsig[0]).toHaveProperty('s'); expect(decodedTransaction.msig.subsig[1]).not.toHaveProperty('s'); + expect(decodedTransaction.msig.subsig[2]).not.toHaveProperty('s'); + }); +}); - describe('Group Transactions Use cases', () => { - test('Group Transaction with Reference Transaction', async () => { - const tx1 = buildSdkTx({ - type: 'pay', - from: account1.address, - to: account2.address, - amount: Math.ceil(Math.random() * 1000), - ...ledgerParams, - }); - const tx2 = buildSdkTx({ - type: 'pay', - from: account2.address, - to: account1.address, - amount: Math.ceil(Math.random() * 1000), - ...ledgerParams, - }); - const tx3 = buildSdkTx({ - type: 'pay', - from: account1.address, - to: account2.address, - amount: Math.ceil(Math.random() * 1000), - ...ledgerParams, - }); - - unsignedTransactions = await algosdk.assignGroupID([tx1, tx2, tx3]); - unsignedTransactions = unsignedTransactions.map((txn) => prepareWalletTx(txn)); - unsignedTransactions[2].signers = []; +describe('Group Transactions Use cases', () => { + test('Group Transaction with Reference Transaction', async () => { + const tx1 = buildSdkTx({ + type: 'pay', + from: account1.address, + to: account2.address, + amount: Math.ceil(Math.random() * 1000), + ...ledgerParams, + }); + const tx2 = buildSdkTx({ + type: 'pay', + from: account2.address, + to: account1.address, + amount: Math.ceil(Math.random() * 1000), + ...ledgerParams, + }); + const tx3 = buildSdkTx({ + type: 'pay', + from: account1.address, + to: account2.address, + amount: Math.ceil(Math.random() * 1000), + ...ledgerParams, + }); - const signedTransactions = await signTxn(unsignedTransactions); - await expect(signedTransactions[2]).toBeNull(); - await expect(signedTransactions.filter((i) => i).length).toBe(2); - }); + unsignedTransactions = await algosdk.assignGroupID([tx1, tx2, tx3]); + unsignedTransactions = unsignedTransactions.map((txn) => prepareWalletTx(txn)); + unsignedTransactions[2].signers = []; - // @TODO: Add errors for mismatches, incomplete groups, etc - }); - expect(decodedTransaction.msig.subsig[2]).not.toHaveProperty('s'); + const signedTransactions = await signTxn(unsignedTransactions); + await expect(signedTransactions[2]).toBeNull(); + await expect(signedTransactions.filter((i) => i).length).toBe(2); }); + + // @TODO: Add errors for mismatches, incomplete groups, etc }); diff --git a/packages/test-project/tests/ui-accounts-e2e.test.js b/packages/test-project/tests/ui-accounts-e2e.test.js index 97af9573..1f4aee7e 100644 --- a/packages/test-project/tests/ui-accounts-e2e.test.js +++ b/packages/test-project/tests/ui-accounts-e2e.test.js @@ -48,7 +48,8 @@ describe('Create Account', () => { }); test('Create An Account, Step 3 - Use Mnemonic', async () => { - await extensionPage.waitForSelector('#enterMnemonic'); + // Wait for mnemonic word buttons to be available + await extensionPage.waitForSelector('[data-index]:not([disabled])'); // We test the remove word functionality by first filling with random words const randomWords = []; diff --git a/packages/test-project/tests/ui-transactions-e2e.test.js b/packages/test-project/tests/ui-transactions-e2e.test.js index 40a6762d..577cf923 100644 --- a/packages/test-project/tests/ui-transactions-e2e.test.js +++ b/packages/test-project/tests/ui-transactions-e2e.test.js @@ -176,6 +176,8 @@ describe('UI Transactions Tests', () => { await closeModal(); await goBack(); + // Extra goBack to main screen + await goBack(); }); DeleteAccount(accounts.ui); From 479bc61792f6e3f99f152fe44664459b7fccdb9a Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Wed, 14 Jul 2021 11:47:23 -0300 Subject: [PATCH 6/6] Remove unnecesary logging --- packages/test-project/tests/common/tests.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/test-project/tests/common/tests.js b/packages/test-project/tests/common/tests.js index 3abc020d..efa66992 100644 --- a/packages/test-project/tests/common/tests.js +++ b/packages/test-project/tests/common/tests.js @@ -112,11 +112,7 @@ async function ConnectAlgoSigner() { // Atomic txs Approval try { await popup.waitForTimeout(500); - const txAmount = await popup.$eval('.dropdown-trigger span', (e) => { - console.log(e); - return +e.innerText.slice(-1); - }); - console.log(txAmount); + const txAmount = await popup.$eval('.dropdown-trigger span', (e) => +e.innerText.slice(-1)); for (let i = 0; i < txAmount; i++) { await popup.click('#toggleApproval');