Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional watched account tests #396

Merged
merged 18 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/ui/cypress/fixtures/watchAccounts/watchMultisigs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { watchSignatories } from './watchSignatories'

export const watchMultisigs = {
'multisig-with-pure': {
name: 'Multisig With Pure',
address: '5Fa3UUF3S6SVdXZtPrCw2tGUqxJiRJLxEGfujozfZ4xFeAKn',
pureAddress: '5EfdqwwuyjjtEa4UhdjbZJu3UxHEHbzh8LMRvE13xTD7z6Wd',
threshold: 2,
signatories: [watchSignatories[0].address, watchSignatories[1].address]
},

'multisig-without-pure': {
name: 'Multisig No Pure',
address: '5GysXAKXrGjNvpQruKWH3RwxtYrJqqWLN1A15gUMht6EXmzC',
threshold: 2,
signatories: [watchSignatories[2].address, watchSignatories[3].address]
}
}
29 changes: 29 additions & 0 deletions packages/ui/cypress/fixtures/watchAccounts/watchSignatories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const watchSignatories = [
// signatories of multisig-with-pure
{
address: '5GGjPYsz8B8mxAzNScFNDPkZ1g97VWFCPCMexPSkPnibPBez',
name: 'Pure Signatory 1',
type: 'sr25519',
mnemonic: 'citizen heavy warrior cattle enter chef label split differ seek turtle gorilla'
},
{
address: '5EkbU3anZKYP98aXF5MvmCUxvwvM4kxp7osc2Xhj1wHYL6ym',
name: 'Pure Signatory 2',
type: 'sr25519',
mnemonic: 'script spoon elder spawn kite burst theme property hip fatal flight amount'
},

// signatories of multisig-without-pure
{
address: '5HfzjVSWj6mxBnqgJhPfUTpkAJKro9BKToxXB3nozbu2MTpV',
name: 'No Pure Signatory 1',
type: 'sr25519',
mnemonic: 'spring banana desert horse ecology resist tag matrix burden heart stereo fix'
},
{
address: '5Df1JyC6KSbjSp3pQEn85PCnvTtknGiN7JyE7bSZ9zqNL76E',
name: 'No Pure Signatory 2',
type: 'sr25519',
mnemonic: 'mutual pluck punch boy gym key brush dune master aunt track dynamic'
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const accountDisplay = {
asnaith marked this conversation as resolved.
Show resolved Hide resolved
identicon: () => cy.get('[data-cy=icon-identicon]'),
pureBadge: () => cy.get('[data-cy=badge-pure]'),
multisigBadge: () => cy.get('[data-cy=badge-multisig]'),
nameLabel: () => cy.get('[data-cy=label-account-name]'),
addressLabel: () => cy.get('[data-cy=label-account-address]'),
}
4 changes: 2 additions & 2 deletions packages/ui/cypress/support/page-objects/landingPage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const landingPage = {
watchAccountButton: () => cy.get('[data-cy=button-watch-address]'),
accountsOrRpcLoader: () => cy.get('[data-cy="loader-accounts-rpc-connection"]'),
noAccountFoundError: () => cy.get('[data-cy="text-no-account-found"]')
accountsOrRpcLoader: () => cy.get('[data-cy=loader-accounts-rpc-connection]'),
noAccountFoundError: () => cy.get('[data-cy=text-no-account-found]')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const editNamesModal = {
body: () => cy.get('[data-cy=modal-edit-names]'),
inputEditPureName: () => cy.get('[data-cy=input-edit-pure-name]'),
inputEditMultisigName: () => cy.get('[data-cy=input-edit-multisig-name]'),
inputEditSignatoryName: () => cy.get('[data-cy=input-edit-signatory-name]'),
saveButton:() => cy.get('[data-cy=button-save-edited-names]'),
}

8 changes: 7 additions & 1 deletion packages/ui/cypress/support/page-objects/multisigPage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export const multisigPage = {
newTransactionButton: () => cy.get('[data-cy="button-new-transaction"]')
// header elements
accountHeader: () => cy.get('[data-cy=header-account]'),
seeOverviewButton: () => cy.get('[data-cy=button-see-overview]'),
newTransactionButton: () => cy.get('[data-cy=button-new-transaction]'),
optionsMenuButton: () => cy.get('[data-cy=button-options-menu]'),
editNamesMenuOption: () => cy.get('[data-cy=menu-option-edit-names]'),
subscanMenuOption: () => cy.get('[data-cy=menu-option-subscan]')
}
8 changes: 4 additions & 4 deletions packages/ui/cypress/support/page-objects/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const notifications = {
successNotificationIcon: () => cy.get('[data-cy="notification-icon-success"]'),
errorNotificationIcon: () => cy.get('[data-cy="notification-icon-error"]'),
loadingNotificationIcon: () => cy.get('[data-cy="notification-icon-loading"]'),
notificationWrapper: () => cy.get('[data-cy="notification-wrapper"]')
successNotificationIcon: () => cy.get('[data-cy=notification-icon-success]'),
errorNotificationIcon: () => cy.get('[data-cy=notification-icon-error]'),
loadingNotificationIcon: () => cy.get('[data-cy=notification-icon-loading]'),
notificationWrapper: () => cy.get('[data-cy=notification-wrapper]')
}
8 changes: 4 additions & 4 deletions packages/ui/cypress/support/page-objects/sendTxModal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const sendTxModal = {
sendTxTitle: () => cy.get('[data-cy="title-send-tx"]'),
fieldTo: () => cy.get('[data-cy="field-to"]'),
fieldAmount: () => cy.get('[data-cy="field-amount"]'),
buttonSend: () => cy.get('[data-cy="button-send"]')
sendTxTitle: () => cy.get('[data-cy=title-send-tx]'),
fieldTo: () => cy.get('[data-cy=field-to]'),
fieldAmount: () => cy.get('[data-cy=field-amount]'),
buttonSend: () => cy.get('[data-cy=button-send]')
}
3 changes: 0 additions & 3 deletions packages/ui/cypress/support/page-objects/settingsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ export const settingsPage = {
accountNameInput: () => cy.get('[data-cy=input-account-name]'),
addButton: () => cy.get('[data-cy=button-add-watched-account]'),
accountContainer: () => cy.get('[data-cy=container-account-details]', { timeout: 20000 }),
accountIcon: () => cy.get('[data-cy=icon-identicon]'),
accountNameLabel: () => cy.get('[data-cy=label-account-name]'),
accountAddressLabel: () => cy.get('[data-cy=label-account-address]'),
accountDeleteButton: () => cy.get('[data-cy=button-delete-watched-account]'),
errorLabel: () => cy.get('[data-cy=label-watch-account-error]')
}
10 changes: 8 additions & 2 deletions packages/ui/cypress/support/page-objects/topMenuItems.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
export const topMenuItems = {
connectButton: () => cy.get('[data-cy="button-menu-connect"]'),
multiproxySelector: () => cy.get('[data-cy="select-multiproxy"]')
homeButton: () => cy.get('[data-cy=button-navigate-home]'),
newMultisigButton: () => cy.get('[data-cy=button-new-multisig]'),
settingsButton: () => cy.get('[data-cy=button-navigate-settings]'),
overviewButton: () => cy.get('[data-cy=button-navigate-overview]'),
aboutButton: () => cy.get('[data-cy=button-navigate-about]'),
connectButton: () => cy.get('[data-cy=button-menu-connect]'),
multiproxySelector: () => cy.get('[data-cy=select-multiproxy]', { timeout: 20000 }),
multiproxySelectorOption: () => cy.get('[data-cy=select-multiproxy-option]')
}
165 changes: 160 additions & 5 deletions packages/ui/cypress/tests/watched-accounts.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { addresses } from '../fixtures/accounts'
import { accountDisplay } from '../support/page-objects/components/accountDisplay'
import { landingPageUrl, settingsPageWatchAccountUrl } from '../fixtures/landingData'
import { landingPage } from '../support/page-objects/landingPage'
import { settingsPage } from '../support/page-objects/settingsPage'
import { topMenuItems } from '../support/page-objects/topMenuItems'
import { watchMultisigs } from '../fixtures/watchAccounts/watchMultisigs'
import { multisigPage } from '../support/page-objects/multisigPage'
import { editNamesModal } from '../support/page-objects/modals/editNamesModal'

const addWatchAccount = (address: string, name?: string) => {
settingsPage.accountAddressInput().type(`${address}{enter}`, { delay: 20 })
Expand All @@ -19,9 +24,9 @@ describe('Watched Accounts', () => {
landingPage.watchAccountButton().click()
addWatchAccount(addresses.Alice, 'Alice')
settingsPage.accountContainer().within(() => {
settingsPage.accountIcon().should('be.visible')
settingsPage.accountAddressLabel().should('be.visible')
settingsPage.accountNameLabel().should('be.visible')
accountDisplay.identicon().should('be.visible')
accountDisplay.addressLabel().should('be.visible')
accountDisplay.nameLabel().should('be.visible')
settingsPage.accountDeleteButton().should('be.visible')
})
})
Expand All @@ -33,8 +38,8 @@ describe('Watched Accounts', () => {
// now remove it
settingsPage.accountContainer().within(() => {
settingsPage.accountDeleteButton().click()
settingsPage.accountIcon().should('not.exist')
settingsPage.accountAddressLabel().should('not.exist')
accountDisplay.identicon().should('not.exist')
accountDisplay.addressLabel().should('not.exist')
})
settingsPage.accountContainer().should('have.length', 0)
})
Expand All @@ -58,4 +63,154 @@ describe('Watched Accounts', () => {
settingsPage.accountContainer().should('have.length', 0)
settingsPage.addButton().should('be.disabled')
})

it('can see the provided name displayed for a watched multisig', () => {
cy.visit(settingsPageWatchAccountUrl)
addWatchAccount(
watchMultisigs['multisig-without-pure'].address,
watchMultisigs['multisig-without-pure'].name)
// ensure the multisig name is displayed in the settings account container
settingsPage.accountContainer().within(() => {
accountDisplay.nameLabel()
.should('be.visible')
.should('have.text', watchMultisigs['multisig-without-pure'].name)
})
// ensure the name is included in the selectable drop-down option
topMenuItems.multiproxySelector()
.should('be.visible')
.first().click()
topMenuItems.multiproxySelectorOption().within(() => {
accountDisplay.nameLabel()
.should('have.text', watchMultisigs['multisig-without-pure'].name)
})
// ensure the name is displayed in the home page header
topMenuItems.homeButton().click()
multisigPage.accountHeader().within(() => {
accountDisplay.nameLabel()
.should('have.text', watchMultisigs['multisig-without-pure'].name)
})
})

it('can see the provided name displayed for a watched pure', () => {
cy.visit(settingsPageWatchAccountUrl)
addWatchAccount(
watchMultisigs['multisig-with-pure'].pureAddress,
watchMultisigs['multisig-with-pure'].name)
// ensure the multisig name is displayed in the settings account container
settingsPage.accountContainer().within(() => {
accountDisplay.nameLabel()
.should('be.visible')
.should('have.text', watchMultisigs['multisig-with-pure'].name)
})
// ensure the name is included in the selectable drop-down option
topMenuItems.multiproxySelector()
.should('be.visible')
.first().click()
topMenuItems.multiproxySelectorOption().within(() => {
accountDisplay.nameLabel()
.should('have.text', watchMultisigs['multisig-with-pure'].name)
})
// navigate to the multisig page and ensure the name is included in the home page header
topMenuItems.homeButton().click()
multisigPage.accountHeader().within(() => {
accountDisplay.nameLabel()
.should('have.text', watchMultisigs['multisig-with-pure'].name)
})
})

it('can see the identicon and badge displayed for a watched multisig', () => {
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
cy.visit(settingsPageWatchAccountUrl)
addWatchAccount(
watchMultisigs['multisig-without-pure'].address,
watchMultisigs['multisig-without-pure'].name)
// ensure the multisig badge is displayed in the selectable drop-down option
topMenuItems.multiproxySelector()
.should('be.visible')
.first().click()
topMenuItems.multiproxySelectorOption().within(() => {
accountDisplay.identicon().should('be.visible')
accountDisplay.multisigBadge().should('be.visible')
accountDisplay.pureBadge().should('not.exist')
})
// navigate to the home page and ensure the multisig badge is included in the header
topMenuItems.homeButton().click()
multisigPage.accountHeader().within(() => {
accountDisplay.identicon().should('be.visible')
accountDisplay.multisigBadge().should('be.visible')
accountDisplay.pureBadge().should('not.exist')
})
})
asnaith marked this conversation as resolved.
Show resolved Hide resolved

it('can see the identicon and badge displayed for a watched pure proxy', () => {
cy.visit(settingsPageWatchAccountUrl)
addWatchAccount(
watchMultisigs['multisig-with-pure'].pureAddress,
watchMultisigs['multisig-with-pure'].name)
// ensure the pure badge is displayed in the selectable drop-down option
topMenuItems.multiproxySelector()
.should('be.visible')
.first().click()
topMenuItems.multiproxySelectorOption().within(() => {
accountDisplay.identicon().should('be.visible')
accountDisplay.pureBadge().should('be.visible')
accountDisplay.multisigBadge().should('not.exist')
})
// navigate to the home page and ensure the pure badge is included in the header
topMenuItems.homeButton().click()
multisigPage.accountHeader().within(() => {
accountDisplay.identicon().should('be.visible')
accountDisplay.pureBadge().should('be.visible')
accountDisplay.multisigBadge().should('not.exist')
})
})

it('can edit the name of a watched pure', () => {
cy.visit(settingsPageWatchAccountUrl)
addWatchAccount(
watchMultisigs['multisig-with-pure'].pureAddress,
watchMultisigs['multisig-with-pure'].name)
// navigate to the home page and edit the name
topMenuItems.homeButton().click()
multisigPage.optionsMenuButton().click()
multisigPage.editNamesMenuOption().click()
editNamesModal.body().should('be.visible')
editNamesModal.inputEditPureName().type(`{selectall}{del}${`Edited Name Test`}`)
cy.wait(500)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another annoying situation where this 500ms wait is needed before clicking the save button, without it the test will fail because the name in the header never updates. With this slight wait, it works every time.

I understand we never want to never use waits but this is another instance where I couldn't go further without one. Open to suggestions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see what we can do here. It's weird that the input are so sensitive. Maybe we can wait until the text is visible in the input or something. I'll check.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to spend time on this, sorry that I didn't get to it today, will check asap and report back.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I learned something pretty 🤯 today with cy.clock and cy.tick. The reason you had to wait, is because of the way we handle the storage of names. We don't store names on press, we debounce it. Without de-bouncing this field, it appeared unresponsive (I remember adding this). There's a debounce of 300ms here

const debouncedNameChange = useMemo(() => debounce(onNameChange, 300), [onNameChange])

Now thanks to cy.clock and tick we can bend time.. and make things happen quicker 🤷
https://docs.cypress.io/api/commands/tick

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Lykhoyda I'll try to use this in the connect function instead of waitUntil. Because it is actually retrying stuff every 200ms, so it's not really elegant

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I learned something pretty 🤯 today with cy.clock and cy.tick. The reason you had to wait, is because of the way we handle the storage of names. We don't store names on press, we debounce it. Without de-bouncing this field, it appeared unresponsive (I remember adding this).

@Tbaut This is great! The explanation totally makes sense. Thank you for working this out, this is something I would have never considered but now we can look out for it in other scenarios! 🙏

(cc @juans-chainsafe - there's some good info in the comment above, we can look out for this in other frontend projects when encountering those scenarios where we think we need a cy.wait).

editNamesModal.saveButton()
.should('be.enabled')
.click()
// ensure the edited name is now displayed in the home page header
multisigPage.accountHeader().within(() => {
accountDisplay.nameLabel()
.should('have.text', 'Edited Name Test')
})
// navigate to settings and ensure the edited name is displayed
cy.visit(settingsPageWatchAccountUrl)
settingsPage.accountContainer().within(() => {
accountDisplay.nameLabel()
.should('have.text', 'Edited Name Test')
})
})

it('can see a subscan link for a watched pure', () => {
cy.visit(settingsPageWatchAccountUrl)
addWatchAccount(
watchMultisigs['multisig-with-pure'].pureAddress,
watchMultisigs['multisig-with-pure'].name)
// navigate to the home page and edit the name
topMenuItems.homeButton().click()
multisigPage.optionsMenuButton().click()
multisigPage.subscanMenuOption().should('be.visible')
})
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very basic test atm, do we want to try and remove the target on the link so that the tab loads in the same window and then check that the subscan URL contains the correct address....or is that too much?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine that you meant to write this for the subscan test. I agree that it'd be better to actually test that the link is for the right address at least. I didn't research it but I guess it's possible. Removing the target sounds like hack though 🤷

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated: @Tbaut Juan has a nice solution for checking external links on another FE project. The same technique works well for us

Copy link
Collaborator

@Tbaut Tbaut Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup that's great, I figured there must have been some ways to stub this but didn't check 👍


it('can not see the "New Transaction" button when only a watched account', () => {
cy.visit(settingsPageWatchAccountUrl)
addWatchAccount(
watchMultisigs['multisig-with-pure'].pureAddress,
watchMultisigs['multisig-with-pure'].name)
// navigate to the home page and edit the name
topMenuItems.homeButton().click()
multisigPage.accountHeader().should('be.visible')
multisigPage.newTransactionButton().should('not.exist')
})
})
4 changes: 3 additions & 1 deletion packages/ui/src/components/AccountEditName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ interface Props {
className?: string
address: string
onNameChange: (args: OnChangeArgs) => void
testId?: string
}

const AccountEditName = ({ address, onNameChange, className }: Props) => {
const AccountEditName = ({ address, onNameChange, className, testId }: Props) => {
const { getNamesWithExtension } = useAccountNames()
const { ownAddressList } = useAccounts()
const [name, setName] = useState(getNamesWithExtension(address) || '')
Expand Down Expand Up @@ -66,6 +67,7 @@ const AccountEditName = ({ address, onNameChange, className }: Props) => {
onChange={onChange}
disabled={isExtensionAccount}
value={name}
data-cy={`input-edit-${testId}-name`}
asnaith marked this conversation as resolved.
Show resolved Hide resolved
// onKeyDown={handleSpecialKeys}
/>
</Grid>
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const Header = ({ handleDrawerOpen }: Props) => {
<NavLink
key={name}
to={path}
data-cy={`button-navigate-${name.toLowerCase().replace(/ /g, '-')}`}
asnaith marked this conversation as resolved.
Show resolved Hide resolved
>
{name}
</NavLink>
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/IdenticonBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const IdenticonBadge = ({
color="primary"
badgeContent={badge}
anchorOrigin={{ horizontal: 'left', vertical: 'top' }}
data-cy={badge === AccountBadge.PURE ? 'badge-pure' : 'badge-multisig'}
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
>
<AccountIcon />
</BadgeStyled>
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/components/OptionsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const OptionsMenu = ({ className, options, menuButtonBorder }: Props) => {
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleMenuClick}
data-cy="button-options-menu"
>
<MoreVertIcon />
</ButtonWithIconStyled>
Expand All @@ -74,6 +75,7 @@ const OptionsMenu = ({ className, options, menuButtonBorder }: Props) => {
className="menuEntry"
key={option.text}
onClick={() => handleClick(option.onClick)}
data-cy={`menu-option-${option.text.toLowerCase().replace(/ /g, '-')}`}
>
<ListItemIcon>{option.icon}</ListItemIcon>
<ListItemText>{option.text}</ListItemText>
Expand Down
Loading