mirror of
https://github.com/luxfi/safe-wallet.git
synced 2026-10-30 15:19:21 +00:00
* fix(e2e): restore popup settle time and fix NFT click reliability
The previous cy.wait removal in closeOutreachPopup, acceptCookies2, and
closeSecurityNotice replaced delays with cy.get('body', { timeout })
which resolves immediately since body always exists, removing the settle
time needed for async popups to render before the conditional jQuery
check. This caused spending_limits tests to fail. Pin ubuntu-24.04 in
the smoke workflow to prevent Cypress Cloud parallel-group-params-mismatch
from differing runner OS versions. Add scrollIntoView and force click
for the inactive NFT icon inside a Tooltip wrapper.
* chore(ci): disable Cypress Cloud recording and split specs manually
Cypress Cloud parallel mode fails with parallel-group-params-mismatch
when GitHub runners have different Ubuntu minor versions (24.04.3 vs
24.04.4). Pinning ubuntu-24.04 doesn't help since both versions exist
under that label.
Disable recording to bypass the cross-machine validation and split
specs across containers using modular arithmetic so each container
runs a unique subset (~4 specs each).
* fix(e2e): revert cy.wait removals that caused test failures
Revert the Cypress page object changes from 47762e16c that replaced
cy.wait() calls with cy.get('body', { timeout }) patterns. The
replacement didn't work because body always exists immediately, removing
the settle time needed for async UI elements (popups, modals, animations)
to render before conditional jQuery checks.
This restores the original cy.wait() calls in: main.page.js,
address_book.page.js, assets.pages.js, dashboard.pages.js,
load_safe.pages.js, sidebar.pages.js, address_book.cy.js, and
load_safe.cy.js.
* fix(e2e): fix test selectors conflicting with keepMounted CaptchaModal
The CaptchaModal uses keepMounted on its ModalDialog, which means
data-testid="modal-title" (with text "Security check") is always
present in the DOM even when the modal is closed. This breaks:
- spending_limits: cy.get(modalTitle).should('have.text', 'New transaction')
finds the CaptchaModal's title first and fails the text check.
Fix: use cy.contains(modalTitle, text) to match on both selector and text.
- nfts: cy.get(modalTitle).should('not.exist') fails because the
CaptchaModal's title always exists in the DOM.
Fix: use should('not.be.visible') instead of should('not.exist').
---------
Co-authored-by: Hanzo Dev <[email protected]>
246 lines
6.8 KiB
JavaScript
246 lines
6.8 KiB
JavaScript
import * as constants from '../../support/constants.js'
|
|
import * as main from './main.page.js'
|
|
import staticSafes from '../../fixtures/safes/static.js'
|
|
|
|
// Re-export common selectors from main.page.js for backward compatibility
|
|
export const tableContainer = main.tableContainer
|
|
export const tableRow = main.tableRow
|
|
|
|
export const addressBookRecipient = '[data-testid="address-book-recipient"]'
|
|
const beameriFrameContainer = '#beamerOverlay .iframeCointaner'
|
|
const beamerInput = 'input[id="beamer"]'
|
|
const exportModalBtn = '[data-testid="export-modal-btn"]'
|
|
export const editEntryBtn = 'button[aria-label="Edit entry"]'
|
|
export const deleteEntryBtn = 'button[aria-label="Delete entry"]'
|
|
export const deleteEntryModalBtnSection = '.MuiDialogActions-root'
|
|
const importBtn = '[data-testid="import-btn"]'
|
|
const uploadErrorMsg = '[data-testid="error-message"]'
|
|
const modalSummaryMessage = '[data-testid="summary-message"]'
|
|
const saveBtn = '[data-testid="save-btn"]'
|
|
const divInput = '[data-testid="name-input"]'
|
|
const exportSummary = '[data-testid="export-summary"]'
|
|
const sendBtn = '[data-testid="send-btn"]'
|
|
export const entryDialog = '[data-testid="entry-dialog"]'
|
|
|
|
//TODO Move to specific component
|
|
const moreActionIcon = '[data-testid="MoreHorizIcon"]'
|
|
|
|
export const acceptSelection = main.acceptSelectionStr
|
|
export const addressBook = 'Address book'
|
|
const createEntryBtn = 'New entry'
|
|
export const delteEntryModaldeleteBtn = 'Delete'
|
|
const exportBtn = 'Export'
|
|
// const saveBtn = 'Save'
|
|
const whatsNewBtnStr = "What's new"
|
|
const beamrCookiesStr = 'accept the "Beamer" cookies'
|
|
const headerImportBtnStr = 'Import'
|
|
const mandatoryNameStr = 'Name *'
|
|
const nameSortBtn = 'Name'
|
|
const addressortBtn = 'Address'
|
|
const addToAddressBookStr = 'Add to address book'
|
|
|
|
export const emptyCSVFile = '../fixtures/address_book_empty_test.csv'
|
|
export const nonCSVFile = '../fixtures/balances.json'
|
|
export const duplicatedCSVFile = 'address_book_duplicated.csv'
|
|
export const validCSVFile = '../fixtures/address_book_test.csv'
|
|
export const networksCSVFile = '../fixtures/address_book_networks.csv'
|
|
export const addedSafesCSVFile = '../fixtures/address_book_addedsafes.csv'
|
|
|
|
const sortSafe1 = 'AA Safe'
|
|
const sortSafe2 = 'BB Safe'
|
|
|
|
export const entries = [
|
|
'0x6E834E9D04ad6b26e1525dE1a37BFd9b215f40B7',
|
|
'test-sepolia-3',
|
|
'0xf405BC611F4a4c89CCB3E4d083099f9C36D966f8',
|
|
'sepolia-test-4',
|
|
'0x03042B890b99552b60A073F808100517fb148F60',
|
|
'sepolia-test-5',
|
|
'0xBd69b0a9DC90eB6F9bAc3E4a5875f437348b6415',
|
|
'assets-test-sepolia',
|
|
]
|
|
|
|
export function clickOnNextPageBtn() {
|
|
cy.get(main.nextPageBtn).click()
|
|
}
|
|
|
|
export function clickOnPrevPageBtn() {
|
|
cy.get(main.previousPageBtn).click()
|
|
}
|
|
|
|
export function verifyCountOfSafes(count) {
|
|
main.verifyElementsCount(tableRow, count)
|
|
}
|
|
export function verifyRecipientData(data) {
|
|
main.verifyValuesExist(addressBookRecipient, data)
|
|
}
|
|
|
|
export function clickOnSendBtn() {
|
|
cy.get(sendBtn).click()
|
|
}
|
|
|
|
export function clickOnMoreActionsBtn() {
|
|
cy.get(moreActionIcon).click()
|
|
}
|
|
|
|
export function clickOnAddToAddressBookBtn() {
|
|
cy.get('li span').contains(addToAddressBookStr).click()
|
|
}
|
|
|
|
export function verifyExportMessage(count) {
|
|
let msg = `${count} address book`
|
|
cy.get(exportSummary).should('contain', msg)
|
|
}
|
|
|
|
export function clickOnNameSortBtn() {
|
|
cy.get(tableContainer).contains(nameSortBtn).click()
|
|
cy.wait(500)
|
|
}
|
|
|
|
export function clickOnAddrressSortBtn() {
|
|
cy.get(tableContainer).contains(addressortBtn).click()
|
|
cy.wait(500)
|
|
}
|
|
|
|
export function verifyEntriesOrder(option = 'ascending') {
|
|
let address = constants.DEFAULT_OWNER_ADDRESS
|
|
let name = sortSafe1
|
|
if (option == 'descending') {
|
|
address = constants.RECIPIENT_ADDRESS
|
|
name = sortSafe2
|
|
}
|
|
|
|
cy.get(tableRow).eq(0).contains(address)
|
|
cy.get(tableRow).eq(0).contains(name)
|
|
}
|
|
|
|
export function addEntryByENS(name, ens) {
|
|
typeInName(name)
|
|
typeInAddress(ens)
|
|
clickOnSaveEntryBtn()
|
|
verifyNewEntryAdded(name, staticSafes.SEP_STATIC_SAFE_6)
|
|
}
|
|
|
|
export function verifyModalSummaryMessage(entryCount, chainCount) {
|
|
cy.get(modalSummaryMessage).should(
|
|
'contain',
|
|
`Found ${entryCount} entries on ${chainCount} ${chainCount > 1 ? 'chains' : 'chain'}`,
|
|
)
|
|
}
|
|
export const uploadErrorMessages = {
|
|
fileType: 'File type must be text/csv',
|
|
emptyFile: 'No entries found in address book',
|
|
}
|
|
|
|
export function verifyUploadExportMessage(msg) {
|
|
main.verifyValuesExist(uploadErrorMsg, msg)
|
|
}
|
|
|
|
export function verifyImportBtnStatus(status) {
|
|
main.verifyElementsStatus([importBtn], status)
|
|
}
|
|
|
|
export function verifyNumberOfRows(number) {
|
|
main.verifyElementsCount(tableRow, number)
|
|
}
|
|
|
|
export function clickOnImportFileBtn() {
|
|
cy.contains(headerImportBtnStr).click()
|
|
}
|
|
|
|
export function importCSVFile(file) {
|
|
cy.get('[type="file"]').selectFile(`cypress/fixtures/${file}`, { force: true })
|
|
}
|
|
|
|
export function clickOnImportBtn() {
|
|
cy.get(importBtn).click()
|
|
}
|
|
|
|
export function verifyDataImported(data) {
|
|
main.verifyValuesExist(tableContainer, data)
|
|
}
|
|
|
|
export function clickOnExportFileBtn() {
|
|
cy.contains(exportBtn).should('be.enabled').click()
|
|
}
|
|
|
|
export function confirmExport() {
|
|
cy.get(exportModalBtn).click()
|
|
}
|
|
|
|
export function clickOnCreateEntryBtn() {
|
|
cy.contains(createEntryBtn).click()
|
|
}
|
|
|
|
export function typeInName(name) {
|
|
cy.get(main.nameInput).type(name)
|
|
}
|
|
|
|
export function typeInAddress(address) {
|
|
cy.get(main.addressInput).type(address)
|
|
}
|
|
|
|
export function clickOnSaveEntryBtn() {
|
|
cy.get(saveBtn).click()
|
|
}
|
|
|
|
export function verifyNewEntryAdded(name, address) {
|
|
cy.contains(name).should('exist')
|
|
cy.contains(address).should('exist')
|
|
}
|
|
|
|
export function addEntry(name, address) {
|
|
typeInName(name)
|
|
typeInAddress(address)
|
|
clickOnSaveEntryBtn()
|
|
verifyNewEntryAdded(name, address)
|
|
}
|
|
|
|
export function clickOnEditEntryBtn() {
|
|
cy.get(editEntryBtn).click({ force: true })
|
|
}
|
|
|
|
export function typeInNameInput(name) {
|
|
cy.get(main.nameInput).clear().type(name).should('have.value', name)
|
|
}
|
|
|
|
export function verifyNameWasChanged(name, editedName) {
|
|
cy.get(name).should('not.exist')
|
|
cy.contains(editedName).should('exist')
|
|
}
|
|
|
|
export function clickDeleteEntryButton() {
|
|
cy.get(deleteEntryBtn).click({ force: true })
|
|
}
|
|
|
|
export function clickDeleteEntryModalDeleteButton() {
|
|
cy.get(deleteEntryModalBtnSection).contains(delteEntryModaldeleteBtn).click()
|
|
}
|
|
|
|
export function verifyEditedNameNotExists(name) {
|
|
cy.get(name).should('not.exist')
|
|
}
|
|
|
|
export function clickOnWhatsNewBtn(force = false) {
|
|
cy.contains(whatsNewBtnStr).click({ force })
|
|
}
|
|
|
|
export function acceptBeamerCookies() {
|
|
cy.contains(beamrCookiesStr)
|
|
}
|
|
|
|
export function verifyBeamerIsChecked() {
|
|
cy.get(beamerInput).should('be.checked')
|
|
}
|
|
|
|
export function verifyBeameriFrameExists() {
|
|
cy.wait(1000)
|
|
cy.get(beameriFrameContainer).should('exist')
|
|
}
|
|
|
|
export function verifyEmptyOwnerNameNotAllowed() {
|
|
cy.get(main.nameInput).clear()
|
|
main.verifyElementsStatus([saveBtn], constants.enabledStates.disabled)
|
|
cy.get(divInput).contains(mandatoryNameStr)
|
|
}
|