Chore: Dynamic fee rate optimisations & Cypress tests (#1878)

* changes to fee-selection.html

* cypress tests

* accounting for low non-integer fees

* renaming
This commit is contained in:
Manolis Mandrapilias 2022-09-09 13:07:54 +02:00 committed by GitHub
parent f7b3b557b2
commit c528e90b0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 181 additions and 8 deletions

View file

@ -6,6 +6,7 @@
"spec_configures_nodes.js",
"spec_devices.js",
"spec_ghost_machine.js",
"spec_fees.js",
"spec_rescan.js",
"spec_qr_signing.js",
"spec_wallet_send.js",

View file

@ -0,0 +1 @@
{"result": {"fastestFee": 13, "halfHourFee": 4, "hourFee": 2, "minimumFee": 1}, "error_messages": []}

View file

@ -0,0 +1 @@
{"result": {"fastestFee": 14, "halfHourFee": 10, "hourFee": 7, "minimumFee": 1}, "error_messages": []}

View file

@ -0,0 +1 @@
{"result": {"fastestFee": 1, "halfHourFee": 1, "hourFee": 1, "minimumFee": 1}, "error_messages": []}

View file

@ -0,0 +1 @@
{"result": {"fastestFee": 1.1, "halfHourFee": 1, "hourFee": 1, "minimumFee": 1}, "error_messages": []}

View file

@ -0,0 +1 @@
{"result": {"fastestFee": 9, "halfHourFee": 5, "hourFee": 3, "minimumFee": 1}, "error_messages": []}

View file

@ -0,0 +1,157 @@
// This spec file assumes that the Ghost wallet is available,
// Fetch API values for fees are mocked
describe('Test the fee UI', () => {
before(() => {
Cypress.config('includeShadowDom', true)
cy.visit('/')
})
// Keeps the session cookie alive, Cypress by default clears all cookies before each test
beforeEach(() => {
cy.viewport(1200,660)
Cypress.Cookies.preserveOnce('session')
})
it('Using dynamic mode with normal fees', () => {
// Fees: {"fastestFee": 9, "halfHourFee": 5, "hourFee": 3, "minimumFee": 1}
cy.selectWallet('Ghost wallet')
cy.intercept('GET', '/wallets/fees', { fixture: 'fees/normal_fees.json' })
cy.get('#btn_send').click()
cy.get('#toggle_advanced').click()
// Preselected dynamic fee rate
cy.get('#fee_rate_dynamic_text').should('have.text', '5')
// Speed text
cy.get('#fee_rate_speed_text').should('have.text', 'Fast (30 minutes)')
// Slider
cy.get('#fees_slider').should('have.value', '5')
cy.get('#fees_slider').invoke('attr', 'min').should('eq', '1')
cy.get('#fees_slider').invoke('attr', 'max').should('eq', '12')
// Simulate sliding to 1.5
cy.get('#fees_slider').invoke('val', '1.5').trigger('input')
cy.get('#fees_slider').should('have.value', '1.5')
cy.get('#fee_rate_speed_text').should('have.text', 'Very slow')
// Sliding to 2
cy.get('#fees_slider').invoke('val', '2').trigger('input')
cy.get('#fees_slider').should('have.value', '2')
cy.get('#fee_rate_speed_text').should('have.text', 'Slow')
// Sliding to 11
cy.get('#fees_slider').invoke('val', '11').trigger('input')
cy.get('#fees_slider').should('have.value', '11')
cy.get('#fee_rate_speed_text').should('have.text', 'Overpaid! (10 minutes)')
})
it('Using dynamic mode with low fees', () => {
// Fees: {"fastestFee": 1, "halfHourFee": 1, "hourFee": 1, "minimumFee": 1}
cy.selectWallet('Ghost wallet')
cy.intercept('GET', '/wallets/fees', { fixture: 'fees/low_fees.json' })
cy.get('#btn_send').click()
cy.get('#toggle_advanced').click()
// Preselected dynamic fee rate
cy.get('#fee_rate_dynamic_text').should('have.text', '1')
// Speed text
cy.get('#fee_rate_speed_text').should('have.text', 'Very fast (10 minutes)')
// Slider
cy.get('#fees_slider').should('have.value', '1')
cy.get('#fees_slider').invoke('attr', 'min').should('eq', '1')
cy.get('#fees_slider').invoke('attr', 'max').should('eq', '1.5')
// Simulate sliding to 1.5
cy.get('#fees_slider').invoke('val', '1.5').trigger('input')
cy.get('#fees_slider').should('have.value', '1.5')
cy.get('#fee_rate_speed_text').should('have.text', 'Overpaid! (10 minutes)')
})
it('Using dynamic mode with low non-integer fees', () => {
// Fees: {"fastestFee": 1.1, "halfHourFee": 1, "hourFee": 1, "minimumFee": 1}
cy.selectWallet('Ghost wallet')
cy.intercept('GET', '/wallets/fees', { fixture: 'fees/low_non-integer_fees.json' })
cy.get('#btn_send').click()
cy.get('#toggle_advanced').click()
// Preselected dynamic fee rate
cy.get('#fee_rate_dynamic_text').should('have.text', '1')
// Speed text
cy.get('#fee_rate_speed_text').should('have.text', 'Fast (30 minutes)')
// Slider
cy.get('#fees_slider').should('have.value', '1')
cy.get('#fees_slider').invoke('attr', 'min').should('eq', '1')
cy.get('#fees_slider').invoke('attr', 'max').should('eq', '2')
// Simulate sliding to 1.1
cy.get('#fees_slider').invoke('val', '1.1').trigger('input')
cy.get('#fees_slider').should('have.value', '1.1')
cy.get('#fee_rate_speed_text').should('have.text', 'Very fast (10 minutes)')
})
it('Using dynamic mode with high fees', () => {
// Fees: {"fastestFee": 14, "halfHourFee": 10, "hourFee": 7, "minimumFee": 1}
cy.selectWallet('Ghost wallet')
cy.intercept('GET', '/wallets/fees', { fixture: 'fees/high_fees.json' })
cy.get('#btn_send').click()
cy.get('#toggle_advanced').click()
// Preselected dynamic fee rate
cy.get('#fee_rate_dynamic_text').should('have.text', '7')
// Speed text
cy.get('#fee_rate_speed_text').should('have.text', 'Medium (1 hour)')
// Slider
cy.get('#fees_slider').should('have.value', '7')
cy.get('#fees_slider').invoke('attr', 'min').should('eq', '1')
cy.get('#fees_slider').invoke('attr', 'max').should('eq', '19')
// Simulate sliding to 3
cy.get('#fees_slider').invoke('val', '3').trigger('input')
cy.get('#fees_slider').should('have.value', '3')
cy.get('#fee_rate_speed_text').should('have.text', 'Very slow')
// Simulate sliding to 4.5
cy.get('#fees_slider').invoke('val', '4.5').trigger('input')
cy.get('#fees_slider').should('have.value', '4.5')
cy.get('#fee_rate_speed_text').should('have.text', 'Slow')
})
it('Using dynamic mode with asymmetric fees', () => {
// Fees: {"fastestFee": 13, "halfHourFee": 4, "hourFee": 2, "minimumFee": 1}
cy.selectWallet('Ghost wallet')
cy.intercept('GET', '/wallets/fees', { fixture: 'fees/asymmetric_fees.json' })
cy.get('#btn_send').click()
cy.get('#toggle_advanced').click()
// Preselected dynamic fee rate
cy.get('#fee_rate_dynamic_text').should('have.text', '2')
// Speed text
cy.get('#fee_rate_speed_text').should('have.text', 'Medium (1 hour)')
// Slider
cy.get('#fees_slider').should('have.value', '2')
cy.get('#fees_slider').invoke('attr', 'min').should('eq', '1')
cy.get('#fees_slider').invoke('attr', 'max').should('eq', '18')
// Simulate sliding to 12.5
cy.get('#fees_slider').invoke('val', '12.5').trigger('input')
cy.get('#fees_slider').should('have.value', '12.5')
cy.get('#fee_rate_speed_text').should('have.text', 'Fast (30 minutes)')
// Simulate sliding to 4
cy.get('#fees_slider').invoke('val', '4').trigger('input')
cy.get('#fees_slider').should('have.value', '4')
cy.get('#fee_rate_speed_text').should('have.text', 'Fast (30 minutes)')
// Simulate sliding to 3
cy.get('#fees_slider').invoke('val', '3').trigger('input')
cy.get('#fees_slider').should('have.value', '3')
cy.get('#fee_rate_speed_text').should('have.text', 'Medium (1 hour)')
// Simulate sliding to 1.6
cy.get('#fees_slider').invoke('val', '1.6').trigger('input')
cy.get('#fees_slider').should('have.value', '1.6')
cy.get('#fee_rate_speed_text').should('have.text', 'Slow')
// Simulate sliding to 1.2
cy.get('#fees_slider').invoke('val', '1.2').trigger('input')
cy.get('#fees_slider').should('have.value', '1.2')
cy.get('#fee_rate_speed_text').should('have.text', 'Very slow')
})
it('Using manual mode', () => {
cy.selectWallet('Ghost wallet')
cy.intercept('GET', '/wallets/fees', { fixture: 'fees/normal_fees.json' })
cy.get('#btn_send').click()
cy.get('#toggle_advanced').click()
cy.get('.fee_container').find('#fee_option_manual').click()
cy.get('#fee_manual').find('#fee_rate').should('have.value', '1')
cy.get('#fee_manual').find('#fee_rate').clear( { force: true })
cy.get('#fee_manual').find('#fee_rate').type(5, { force: true })
cy.get('#fee_manual').find('#fee_rate').should('have.value', '5')
cy.get('#fee_manual').find('.note').contains('1 sat/vbyte is the minimal fee rate.')
})
})

View file

@ -33,7 +33,7 @@
</div>
<div id ="fee_dynamic" style="display: {% if fee_options_dynamic %}block{% else %}none{% endif %}">
<div id="blocks"></div>
<input type="range" style="width: 12em" min="{{min_fee}}" max="" value="" step="1" id="fees_slider">
<input type="range" style="width: 12em" min="{{min_fee}}" max="" value="" step="0.1" id="fees_slider">
<input type="hidden" id="fee_rate_dynamic" name="fee_rate_dynamic" value="{{min_fee}}">
<div>
{{ _("Estimated speed:") }} <span id="fee_rate_speed_text"></span>
@ -238,11 +238,21 @@
// Gets executed after fee initialisation
initWithFees() {
// this.feesSlider.min is set above via min_fee
this.feesSlider.max = Math.floor(this.fees["fastestFee"] * 1.4)
let sliderMin = this.fees["minimumFee"]
let sliderMax = this.fees["fastestFee"]
let average = Math.floor((sliderMin + sliderMax) / 2)
this.feesSlider.value = average
// For low fee environment, API results are integers
if (this.fees["fastestFee"] == 1) {
this.feesSlider.max = 1.5
}
else {
let feeSliderMax = Math.floor(this.fees["fastestFee"] * 1.4)
this.feesSlider.max = feeSliderMax >= 2 ? feeSliderMax : 2
}
// Avoid calculating the default value with a very high fastestFee
if (this.fees["fastestFee"] > 2 * this.fees["halfHourFee"]) {
this.feesSlider.value = Math.floor((this.fees["minimumFee"] + this.fees["hourFee"] + this.fees["halfHourFee"]) / 3)
}
else {
this.feesSlider.value = Math.floor((this.fees["minimumFee"] + this.fees["fastestFee"]) / 2)
}
this.dynamicFeeUpdated() // otherwise no text next to estimated speed
}
@ -274,9 +284,9 @@
let hourFee = this.fees["hourFee"]
let halfHourFee = this.fees["halfHourFee"]
let fastestFee = this.fees["fastestFee"]
if (this.feesSlider.value <= minFee + ((hourFee - minFee) / 2)) {
if (this.feesSlider.value < minFee + ((hourFee - minFee) / 2)) {
this.fee_rate_speed_text.innerText = '{{ _("Very slow") }}';
} else if (this.feesSlider.value > minFee + ((hourFee - minFee) / 2) && this.feesSlider.value < hourFee) {
} else if (this.feesSlider.value >= minFee + ((hourFee - minFee) / 2) && this.feesSlider.value < hourFee) {
this.fee_rate_speed_text.innerText = '{{ _("Slow") }}';
} else if (this.feesSlider.value >= hourFee && this.feesSlider.value < halfHourFee) {
this.fee_rate_speed_text.innerText = '{{ _("Medium (1 hour)") }}';