fix(wallet): fix rename account modal states

Considerations:

- Change validation mode for StatusText to have validity from the start
- Don't show a color selection if the selected color doesn't match the
  available color palette
- Don't enable button if nothing changed
- Tests
    - Add test for validation mode that shows how the valid property
    behaves based on findings.
    - Fix text to expect valid on no input for the default control

Fixes: #8211
This commit is contained in:
Stefan 2023-01-03 19:25:45 +04:00 committed by Stefan Dunca
parent 297280c467
commit 16af9dfae6
3 changed files with 116 additions and 58 deletions

View File

@ -31,7 +31,7 @@ Item {
Component.onCompleted: setCropRect(Qt.rect(10, 0, sourceSize.width - 20, sourceSize.height)) Component.onCompleted: setCropRect(Qt.rect(10, 0, sourceSize.width - 20, sourceSize.height))
} }
} }
Loader { Loader {
id: testLoader id: testLoader

View File

@ -7,110 +7,162 @@ import StatusQ.Controls.Validators 0.1
import StatusQ.TestHelpers 0.1 import StatusQ.TestHelpers 0.1
Item { Item {
id: root
width: 300 width: 300
height: 100 height: 100
property int _defaultValidationMode function loadControl(test, sourceComponent) {
let testItem = test.createTemporaryObject(sourceComponent, root)
Component.onCompleted: { test.verify(test.waitForRendering(testItem))
_defaultValidationMode = statusInput.validationMode test.mouseClick(testItem)
} return testItem
StatusInput {
id: statusInput
label: "Control under test"
charLimit: 30
placeholderText: `Must match regex(${validators[0].regularExpression.toString()}) and <= 30 chars`
focus: true
validators: [
StatusRegularExpressionValidator {
regularExpression: /^[0-9A-Za-z_\$-\s]*$/
}
]
} }
TestCase { TestCase {
id: regexTC id: regexTC
property StatusInput testControl: null
name: "RegexValidationTest" name: "RegexValidationTest"
when: windowShown when: windowShown
// Component {
// Test guards id: defaultComponent
function init() {
qtOuput.restartCapturing() StatusInput {
mouseClick(statusInput) label: "Control under test"
charLimit: 30
placeholderText: `Must match regex(${validators[0].regularExpression.toString()}) and <= 30 chars`
anchors.fill: parent
focus: true
validators: [
StatusRegularExpressionValidator {
regularExpression: /^[0-9A-Za-z_\$-\s]*$/
}
]
}
} }
function cleanup() { //
statusInput.text = "" // Test guards
statusInput.validationMode = _defaultValidationMode
function init() {
qtOuput.restartCapturing()
regexTC.testControl = root.loadControl(regexTC, defaultComponent)
} }
// //
// Tests // Tests
function test_initial_empty_is_valid() { function test_initial_empty_is_valid() {
verify(statusInput.valid, "Expected valid input") verify(!regexTC.testControl.valid, "Expected valid input")
verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`)
} }
function test_regex_validation() { function test_regex_validation() {
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_1) TestUtils.pressKeyAndWait(regexTC, regexTC.testControl, Qt.Key_1)
verify(statusInput.valid, "Expected valid input") verify(regexTC.testControl.valid, "Expected valid input")
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_Ampersand) TestUtils.pressKeyAndWait(regexTC, regexTC.testControl, Qt.Key_Ampersand)
verify(!statusInput.valid, "Expected invalid input") verify(!regexTC.testControl.valid, "Expected invalid input")
verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`)
} }
function test_no_invalid_input() { function test_no_invalid_input() {
statusInput.validationMode = StatusInput.ValidationMode.IgnoreInvalidInput regexTC.testControl.validationMode = StatusInput.ValidationMode.IgnoreInvalidInput
verify(statusInput.valid, "Expected valid input") verify(regexTC.testControl.valid, "Expected valid input")
verify(statusInput.text.length === 0, "Expected no input") verify(regexTC.testControl.text.length === 0, "Expected no input")
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_2) TestUtils.pressKeyAndWait(regexTC, regexTC.testControl, Qt.Key_2)
verify(statusInput.valid, "Expected valid input") verify(regexTC.testControl.valid, "Expected valid input")
verify(statusInput.text === "2", "Expect one character") verify(regexTC.testControl.text === "2", "Expect one character")
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_Ampersand) TestUtils.pressKeyAndWait(regexTC, regexTC.testControl, Qt.Key_Ampersand)
verify(statusInput.valid, "Expected invalid input") verify(regexTC.testControl.valid, "Expected invalid input")
verify(statusInput.text === "2", "Expect the same input") verify(regexTC.testControl.text === "2", "Expect the same input")
verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`)
} }
// Use case expected in case new validation changes are enabled with old unvalid data // Use case expected in case new validation changes are enabled with old invalid data
function test_user_can_delete_initial_invalid_input() { function test_user_can_delete_initial_invalid_input() {
const appendInvalidChars = "#@!*" const appendInvalidChars = "#@!*"
statusInput.text = "invalid $" + appendInvalidChars regexTC.testControl.text = "invalid $" + appendInvalidChars
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_End) TestUtils.pressKeyAndWait(regexTC, regexTC.testControl, Qt.Key_End)
verify(!statusInput.valid, "Expected invalid input due to characters not matching") verify(!regexTC.testControl.valid, "Expected invalid input due to characters not matching")
// Delete invalid characters to get a valid text // Delete invalid characters to get a valid text
for(let i = 0; i < appendInvalidChars.length; ++i) for(let i = 0; i < appendInvalidChars.length; ++i)
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_Backspace) TestUtils.pressKeyAndWait(regexTC, regexTC.testControl, Qt.Key_Backspace)
verify(statusInput.valid, "Expected valid input") verify(regexTC.testControl.valid, "Expected valid input")
verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`)
} }
} }
TestCase { TestCase {
id: qmlWarnTC id: bindedTC
name: "StatusInput" property StatusInput testControl: null
name: "BindedValuesTest"
when: windowShown when: windowShown
property QtObject dataObject: QtObject {
property string text: "Test text"
property string emoji: "👍"
property string color: "#FF0000"
}
Component {
id: bindedTextComponent
StatusInput {
charLimit: 10
input.isIconSelectable: true
placeholderText: qsTr("Enter an account name...")
text: bindedTC.dataObject.text
input.asset.emoji: bindedTC.dataObject.emoji
input.asset.color: bindedTC.dataObject.color
anchors.fill: parent
focus: true
validators: [
StatusMinLengthValidator {
errorMessage: qsTr("You need to enter an account name")
minLength: 1
},
StatusRegularExpressionValidator {
regularExpression: /^[^<>]+$/
errorMessage: qsTr("This is not a valid account name")
}
]
}
}
// //
// Test guards // Test guards
function initTestCase() { function init() {
} bindedTC.testControl = root.loadControl(regexTC, bindedTextComponent)
function cleanup() {
statusInput.text = ""
statusInput.validationMode = _defaultValidationMode
} }
// //
// Tests // Tests
function test_assigning_valid_value_works() {
qtOuput.restartCapturing()
verify(!testControl.valid, "Expected input not validated yet")
testControl.validationMode = StatusInput.ValidationMode.Always
verify(waitForRendering(testControl))
verify(testControl.valid, "Expected valid input")
bindedTC.dataObject.text = "Test<New"
verify(!testControl.valid, "Expected valid input")
function test_initial_empty_is_valid() {
mouseClick(statusInput)
verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`) verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`)
} }
} }

View File

@ -48,6 +48,7 @@ StatusModal {
StatusInput { StatusInput {
id: accountNameInput id: accountNameInput
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
input.edit.objectName: "renameAccountNameInput" input.edit.objectName: "renameAccountNameInput"
input.isIconSelectable: true input.isIconSelectable: true
@ -56,6 +57,9 @@ StatusModal {
input.asset.emoji: currentAccount.emoji input.asset.emoji: currentAccount.emoji
input.asset.color: currentAccount.color input.asset.color: currentAccount.color
input.asset.name: !currentAccount.emoji ? "filled-account": "" input.asset.name: !currentAccount.emoji ? "filled-account": ""
validationMode: StatusInput.ValidationMode.Always
onIconClicked: { onIconClicked: {
popup.emojiPopup.open() popup.emojiPopup.open()
popup.emojiPopup.x = popup.x + accountNameInput.x + Style.current.padding popup.emojiPopup.x = popup.x + accountNameInput.x + Style.current.padding
@ -87,12 +91,12 @@ StatusModal {
if(model[i] === currentAccount.color) if(model[i] === currentAccount.color)
return i return i
} }
return -1
} }
onSelectedColorChanged: { onSelectedColorChanged: {
if(selectedColor !== currentAccount.color) { if(selectedColor !== currentAccount.color) {
accountNameInput.input.asset.color = selectedColor accountNameInput.input.asset.color = selectedColor
} }
} }
} }
@ -109,6 +113,8 @@ StatusModal {
text: qsTr("Change Name") text: qsTr("Change Name")
enabled: accountNameInput.text !== "" && accountNameInput.valid enabled: accountNameInput.text !== "" && accountNameInput.valid
&& (accountNameInput.text !== currentAccount.name
|| (accountColorInput.selectedColorIndex >= 0 && accountColorInput.selectedColor !== currentAccount.color))
MessageDialog { MessageDialog {
id: changeError id: changeError
@ -117,7 +123,7 @@ StatusModal {
standardButtons: StandardButton.Ok standardButtons: StandardButton.Ok
} }
onClicked : { onClicked : {
if (!accountNameInput.valid) { if (!accountNameInput.valid) {
return return
} }