feat(@desktop/wallet): Integrate recurring onramp providers in Recurrent tab

fixes #14820
This commit is contained in:
Khushboo Mehta 2024-05-29 18:40:22 +02:00 committed by Khushboo-dev-cpp
parent 3f77a16317
commit aa03edf17c
7 changed files with 140 additions and 50 deletions

View File

@ -7,15 +7,17 @@ type Item* = object
logoUrl: string logoUrl: string
siteUrl: string siteUrl: string
hostname: string hostname: string
recurrentSiteUrl: string
proc initItem*(name, description, fees, logoUrl, siteUrl, proc initItem*(name, description, fees, logoUrl, siteUrl,
hostname: string): Item = hostname, recurrentSiteUrl,: string): Item =
result.name = name result.name = name
result.description = description result.description = description
result.fees = fees result.fees = fees
result.logoUrl = logoUrl result.logoUrl = logoUrl
result.siteUrl = siteUrl result.siteUrl = siteUrl
result.hostname = hostname result.hostname = hostname
result.recurrentSiteUrl = recurrentSiteUrl
proc `$`*(self: Item): string = proc `$`*(self: Item): string =
result = "Item(" result = "Item("
@ -25,6 +27,7 @@ proc `$`*(self: Item): string =
result &= fmt"logoUrl:{self.logoUrl}, " result &= fmt"logoUrl:{self.logoUrl}, "
result &= fmt"siteUrl:{self.siteUrl}" result &= fmt"siteUrl:{self.siteUrl}"
result &= fmt"hostname:{self.hostname}" result &= fmt"hostname:{self.hostname}"
result &= fmt"recurrentSiteUrl:{self.recurrentSiteUrl}"
result &= ")" result &= ")"
method getName*(self: Item): string {.base.} = method getName*(self: Item): string {.base.} =
@ -44,3 +47,6 @@ method getSiteUrl*(self: Item): string {.base.} =
method getHostname*(self: Item): string {.base.} = method getHostname*(self: Item): string {.base.} =
return self.hostname return self.hostname
method getRecurrentSiteUrl*(self: Item): string {.base.} =
return self.recurrentSiteUrl

View File

@ -10,6 +10,7 @@ type
LogoUrl LogoUrl
SiteUrl SiteUrl
Hostname Hostname
RecurrentSiteUrl
QtObject: QtObject:
type type
@ -36,7 +37,8 @@ QtObject:
ModelRole.Fees.int:"fees", ModelRole.Fees.int:"fees",
ModelRole.LogoUrl.int:"logoUrl", ModelRole.LogoUrl.int:"logoUrl",
ModelRole.SiteUrl.int:"siteUrl", ModelRole.SiteUrl.int:"siteUrl",
ModelRole.Hostname.int:"hostname" ModelRole.Hostname.int:"hostname",
ModelRole.RecurrentSiteUrl.int:"recurrentSiteUrl"
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -62,6 +64,8 @@ QtObject:
result = newQVariant(item.getSiteUrl) result = newQVariant(item.getSiteUrl)
of ModelRole.Hostname: of ModelRole.Hostname:
result = newQVariant(item.getHostname) result = newQVariant(item.getHostname)
of ModelRole.RecurrentSiteUrl:
result = newQVariant(item.getRecurrentSiteUrl)
proc setItems*(self: Model, items: seq[Item]) = proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel() self.beginResetModel()

View File

@ -40,7 +40,8 @@ method updateCryptoServices*(self: Module, cryptoServices: seq[CryptoRampDto]) =
w.fees, w.fees,
w.logoUrl, w.logoUrl,
w.siteUrl, w.siteUrl,
w.hostname w.hostname,
w.recurrentSiteUrl,
)) ))
self.view.setItems(items) self.view.setItems(items)

View File

@ -10,6 +10,7 @@ type
logoUrl*: string logoUrl*: string
siteUrl*: string siteUrl*: string
hostname*: string hostname*: string
recurrentSiteUrl*: string
proc newDto*( proc newDto*(
name: string, name: string,
@ -17,7 +18,8 @@ proc newDto*(
fees: string, fees: string,
logoUrl: string, logoUrl: string,
siteUrl: string, siteUrl: string,
hostname: string hostname: string,
recurrentSiteUrl: string
): CryptoRampDto = ): CryptoRampDto =
return CryptoRampDto( return CryptoRampDto(
name: name, name: name,
@ -25,7 +27,8 @@ proc newDto*(
fees: fees, fees: fees,
logoUrl: logoUrl, logoUrl: logoUrl,
siteUrl: siteUrl, siteUrl: siteUrl,
hostname: hostname hostname: hostname,
recurrentSiteUrl: recurrentSiteUrl
) )
proc `$`*(self: CryptoRampDto): string = proc `$`*(self: CryptoRampDto): string =
@ -36,6 +39,7 @@ proc `$`*(self: CryptoRampDto): string =
result &= fmt"logoUrl:{self.logoUrl}, " result &= fmt"logoUrl:{self.logoUrl}, "
result &= fmt"siteUrl:{self.siteUrl}, " result &= fmt"siteUrl:{self.siteUrl}, "
result &= fmt"hostname:{self.hostname}" result &= fmt"hostname:{self.hostname}"
result &= fmt"recurrentSiteUrl:{self.recurrentSiteUrl}"
result &= ")" result &= ")"
proc toCryptoRampDto*(jsonObj: JsonNode): CryptoRampDto = proc toCryptoRampDto*(jsonObj: JsonNode): CryptoRampDto =
@ -46,3 +50,4 @@ proc toCryptoRampDto*(jsonObj: JsonNode): CryptoRampDto =
discard jsonObj.getProp("logoUrl", result.logoUrl) discard jsonObj.getProp("logoUrl", result.logoUrl)
discard jsonObj.getProp("siteUrl", result.siteUrl) discard jsonObj.getProp("siteUrl", result.siteUrl)
discard jsonObj.getProp("hostname", result.hostname) discard jsonObj.getProp("hostname", result.hostname)
discard jsonObj.getProp("recurrentSiteUrl", result.recurrentSiteUrl)

View File

@ -1,7 +1,10 @@
import QtQuick 2.15 import QtQuick 2.15
import QtTest 1.15 import QtTest 1.15
import SortFilterProxyModel 0.2
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import Models 1.0 import Models 1.0
import utils 1.0 import utils 1.0
@ -14,13 +17,23 @@ Item {
height: 800 height: 800
OnRampProvidersModel{ OnRampProvidersModel{
id: onRampProvidersModal id: _onRampProvidersModel
}
SortFilterProxyModel {
id: recurrentOnRampProvidersModel
sourceModel: _onRampProvidersModel
filters: ValueFilter {
roleName: "recurrentSiteUrl"
value: ""
inverted: true
}
} }
Component { Component {
id: componentUnderTest id: componentUnderTest
BuyCryptoModal { BuyCryptoModal {
onRampProvidersModel: onRampProvidersModal onRampProvidersModel: _onRampProvidersModel
onClosed: destroy() onClosed: destroy()
} }
} }
@ -47,6 +60,32 @@ Item {
verify(!!controlUnderTest.opened) verify(!!controlUnderTest.opened)
} }
function testDelegateItems(providersList, modelToCompareAgainst) {
for(let i =0; i< providersList.count; i++) {
let delegateUnderTest = providersList.itemAtIndex(i)
verify(!!delegateUnderTest)
compare(delegateUnderTest.title, modelToCompareAgainst.get(i).name)
compare(delegateUnderTest.subTitle, modelToCompareAgainst.get(i).description)
compare(delegateUnderTest.asset.name, modelToCompareAgainst.get(i).logoUrl)
const feesText = findChild(delegateUnderTest, "feesText")
verify(!!feesText)
compare(feesText.text, modelToCompareAgainst.get(i).fees)
const externalLinkIcon = findChild(delegateUnderTest, "externalLinkIcon")
verify(!!externalLinkIcon)
compare(externalLinkIcon.icon, "tiny/external")
compare(externalLinkIcon.color, Theme.palette.baseColor1)
// Hover over the item and check hovered state
mouseMove(delegateUnderTest, delegateUnderTest.width/2, delegateUnderTest.height/2)
verify(delegateUnderTest.sensor.containsMouse)
compare(externalLinkIcon.color, Theme.palette.directColor1)
verify(delegateUnderTest.color, Theme.palette.baseColor2)
}
}
function test_launchAndCloseModal() { function test_launchAndCloseModal() {
launchPopup() launchPopup()
@ -64,7 +103,7 @@ Item {
verify(!!footer) verify(!!footer)
compare(footer.rightButtons.count, 1) compare(footer.rightButtons.count, 1)
compare(footer.rightButtons.get(0).text, qsTr("Done")) compare(footer.rightButtons.get(0).text, qsTr("Done"))
mouseClick(footer.rightButtons.get(0), Qt.LeftButton) mouseClick(footer.rightButtons.get(0))
// popup should be closed // popup should be closed
verify(!controlUnderTest.opened) verify(!controlUnderTest.opened)
@ -78,6 +117,11 @@ Item {
const tabBar = findChild(controlUnderTest, "tabBar") const tabBar = findChild(controlUnderTest, "tabBar")
verify(!!tabBar) verify(!!tabBar)
// find providers list
const providersList = findChild(controlUnderTest, "providersList")
waitForRendering(providersList)
verify(!!providersList)
// should have 2 items // should have 2 items
compare(tabBar.count, 2) compare(tabBar.count, 2)
@ -90,57 +134,88 @@ Item {
// item 1 should have text "Recurrent" // item 1 should have text "Recurrent"
compare(tabBar.itemAt(1).text, qsTr("Recurrent")) compare(tabBar.itemAt(1).text, qsTr("Recurrent"))
// TODO: this will be implemnted under https://github.com/status-im/status-desktop/issues/14820 // close popup
// until then this list will be empty controlUnderTest.close()
mouseClick(tabBar.itemAt(1), Qt.LeftButton) verify(!controlUnderTest.opened)
compare(tabBar.currentIndex, 1) }
function test_modalContent_OneTime_tab() {
// Launch modal
launchPopup()
// find tab bar
const tabBar = findChild(controlUnderTest, "tabBar")
verify(!!tabBar)
// find providers list
const providersList = findChild(controlUnderTest, "providersList") const providersList = findChild(controlUnderTest, "providersList")
waitForRendering(providersList) waitForRendering(providersList)
verify(!!providersList) verify(!!providersList)
compare(providersList.count, 0)
// check data on 1st tab -------------------------------------------------------- mouseClick(tabBar.itemAt(0))
mouseClick(tabBar.itemAt(0), Qt.LeftButton)
compare(tabBar.currentIndex, 0) compare(tabBar.currentIndex, 0)
waitForRendering(providersList)
verify(!!providersList)
// verify that 3 items are listed // verify that 3 items are listed
compare(providersList.count, 3) compare(providersList.count, 3)
// check if delegate contents are as expected // check if delegate contents are as expected
for(let i =0; i< providersList.count; i++) { testDelegateItems(providersList, _onRampProvidersModel)
let delegateUnderTest = providersList.itemAtIndex(i)
verify(!!delegateUnderTest)
compare(delegateUnderTest.title, onRampProvidersModal.get(i).name) let delegateUnderTest = providersList.itemAtIndex(0)
compare(delegateUnderTest.subTitle, onRampProvidersModal.get(i).description) verify(!!delegateUnderTest)
compare(delegateUnderTest.asset.name, onRampProvidersModal.get(i).logoUrl)
const feesText = findChild(delegateUnderTest, "feesText")
verify(!!feesText)
compare(feesText.text, onRampProvidersModal.get(i).fees)
const externalLinkIcon = findChild(delegateUnderTest, "externalLinkIcon")
verify(!!externalLinkIcon)
compare(externalLinkIcon.icon, "tiny/external")
compare(externalLinkIcon.color, Theme.palette.baseColor1)
// Hover over the item and check hovered state
mouseMove(delegateUnderTest, delegateUnderTest.width/2, delegateUnderTest.height/2)
verify(delegateUnderTest.sensor.containsMouse)
compare(externalLinkIcon.color, Theme.palette.directColor1)
verify(delegateUnderTest.color, Theme.palette.baseColor2)
}
// test mouse click // test mouse click
tryCompare(notificationSpy, "count", 0) tryCompare(notificationSpy, "count", 0)
mouseClick(providersList.itemAtIndex(0)) mouseClick(delegateUnderTest)
tryCompare(notificationSpy, "count", 1) tryCompare(notificationSpy, "count", 1)
compare(notificationSpy.signalArguments[0][0],onRampProvidersModal.get(0).siteUrl) compare(notificationSpy.signalArguments[0][0], _onRampProvidersModel.get(0).siteUrl)
compare(notificationSpy.signalArguments[0][1],onRampProvidersModal.get(0).hostname) compare(notificationSpy.signalArguments[0][1], _onRampProvidersModel.get(0).hostname)
notificationSpy.clear()
// popup should be closed
verify(!controlUnderTest.opened)
}
function test_modalContent_recurrent_tab() {
// Launch modal
launchPopup()
// find tab bar
const tabBar = findChild(controlUnderTest, "tabBar")
verify(!!tabBar)
// find providers list
const providersList = findChild(controlUnderTest, "providersList")
waitForRendering(providersList)
verify(!!providersList)
// check data in "Recurrent" tab --------------------------------------------------------
mouseClick(tabBar.itemAt(1))
compare(tabBar.currentIndex, 1)
waitForRendering(providersList)
verify(!!providersList)
// verify that 1 item is listed
compare(providersList.count, 1)
// check if delegate contents are as expected
testDelegateItems(providersList, recurrentOnRampProvidersModel)
let delegateUnderTest = providersList.itemAtIndex(0)
verify(!!delegateUnderTest)
// test mouse click
tryCompare(notificationSpy, "count", 0)
verify(controlUnderTest.opened)
mouseClick(delegateUnderTest)
tryCompare(notificationSpy, "count", 1)
compare(notificationSpy.signalArguments[0][0], recurrentOnRampProvidersModel.get(0).recurrentSiteUrl)
compare(notificationSpy.signalArguments[0][1], recurrentOnRampProvidersModel.get(0).hostname)
notificationSpy.clear()
// popup should be closed
verify(!controlUnderTest.opened)
} }
} }
} }

View File

@ -9,7 +9,7 @@ ListModel {
logoUrl: ModelsData.onRampProviderImages.ramp, logoUrl: ModelsData.onRampProviderImages.ramp,
siteUrl: "https://ramp.network/buy?hostApiKey=zrtf9u2uqebeyzcs37fu5857tktr3eg9w5tffove&swapAsset=DAI,ETH,USDC,USDT", siteUrl: "https://ramp.network/buy?hostApiKey=zrtf9u2uqebeyzcs37fu5857tktr3eg9w5tffove&swapAsset=DAI,ETH,USDC,USDT",
hostname: "ramp.network", hostname: "ramp.network",
recurrentSiteURL: "" recurrentSiteUrl: ""
}, },
{ {
name: "MoonPay", name: "MoonPay",
@ -18,7 +18,7 @@ ListModel {
logoUrl: ModelsData.onRampProviderImages.moonPay, logoUrl: ModelsData.onRampProviderImages.moonPay,
siteUrl: "https://buy.moonpay.com/?apiKey=pk_live_YQC6CQPA5qqDu0unEwHJyAYQyeIqFGR", siteUrl: "https://buy.moonpay.com/?apiKey=pk_live_YQC6CQPA5qqDu0unEwHJyAYQyeIqFGR",
hostname: "moonpay.com", hostname: "moonpay.com",
recurrentSiteURL: "https://buy.moonpay.com/?apiKey=pk_live_YQC6CQPA5qqDu0unEwHJyAYQyeIqFGR", recurrentSiteUrl: "https://buy.moonpay.com/?apiKey=pk_live_ABCCQPA5qqDu0unEwHJyAYQyeIqFGR",
}, },
{ {
name: "Latamex", name: "Latamex",
@ -27,7 +27,7 @@ ListModel {
logoUrl: ModelsData.onRampProviderImages.latamex, logoUrl: ModelsData.onRampProviderImages.latamex,
siteUrl: "https://latamex.com/", siteUrl: "https://latamex.com/",
hostname: "latamex.com", hostname: "latamex.com",
recurrentSiteURL: "", recurrentSiteUrl: "",
} }
] ]

View File

@ -46,12 +46,11 @@ StatusDialog {
Layout.fillHeight: true Layout.fillHeight: true
model: SortFilterProxyModel { model: SortFilterProxyModel {
sourceModel: !!root.onRampProvidersModel ? root.onRampProvidersModel : null sourceModel: !!root.onRampProvidersModel ? root.onRampProvidersModel : null
// TODO: this temporary and changed under https://github.com/status-im/status-desktop/issues/14820
// when recurrentSiteURL is available
filters: ValueFilter { filters: ValueFilter {
enabled: tabBar.currentIndex enabled: tabBar.currentIndex
roleName: "name" roleName: "recurrentSiteUrl"
value: "" value: ""
inverted: true
} }
} }
delegate: StatusListItem { delegate: StatusListItem {
@ -78,7 +77,7 @@ StatusDialog {
} }
] ]
onClicked: { onClicked: {
let url = tabBar.currentIndex ? recurrentSiteURL : siteUrl let url = tabBar.currentIndex ? recurrentSiteUrl : siteUrl
Global.openLinkWithConfirmation(url, hostname) Global.openLinkWithConfirmation(url, hostname)
root.close() root.close()
} }