|
|
|
@ -1,4 +1,5 @@
|
|
|
|
|
import QtQuick 2.14
|
|
|
|
|
import QtQuick.Controls 2.14
|
|
|
|
|
import QtQml 2.14
|
|
|
|
|
import QtTest 1.0
|
|
|
|
|
|
|
|
|
@ -6,16 +7,524 @@ import utils 1.0
|
|
|
|
|
import shared.status 1.0
|
|
|
|
|
import shared.stores 1.0
|
|
|
|
|
|
|
|
|
|
import TextUtils 1.0
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
id: root
|
|
|
|
|
width: 600
|
|
|
|
|
height: 400
|
|
|
|
|
|
|
|
|
|
Component {
|
|
|
|
|
id: componentUnderTest
|
|
|
|
|
StatusChatInput {
|
|
|
|
|
property var globalUtils: globalUtilsMock
|
|
|
|
|
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: implicitHeight
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
usersStore: QtObject {
|
|
|
|
|
property var usersModel: ListModel {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
|
id: testData
|
|
|
|
|
readonly property var multiLineText: ["Typing on first row", "Typing on the second row", "Typing on the third row"]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestCase {
|
|
|
|
|
name: "StatusChatInputInitialization"
|
|
|
|
|
when: windowShown
|
|
|
|
|
|
|
|
|
|
property StatusChatInput controlUnderTest: null
|
|
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
|
controlUnderTest = createTemporaryObject(componentUnderTest, root)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Scenario: StatusChatInput initialisation
|
|
|
|
|
//Given a new instance of StatusChatInput
|
|
|
|
|
//When there is no keyboard input
|
|
|
|
|
//Then the StatucChatInput raw text is empty
|
|
|
|
|
//But the StatusChatInput is configured to support Rich Text
|
|
|
|
|
function test_empty_chat_input() {
|
|
|
|
|
verify(controlUnderTest.textInput.length == 0, `Expected 0 text length, received: ${controlUnderTest.textInput.length}`)
|
|
|
|
|
verify(controlUnderTest.getPlainText() == "", `Expected empty string, received: ${controlUnderTest.getPlainText()}`)
|
|
|
|
|
verify(controlUnderTest.textInput.textFormat == TextEdit.RichText, "Expected text input format to be Rich")
|
|
|
|
|
verify(controlUnderTest.textInput.text.startsWith("<!DOCTYPE HTML PUBLIC"), "Expected text input format to be Rich")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestCase {
|
|
|
|
|
id: statusChatInputKeyboardInputExpectedAsText
|
|
|
|
|
name: "StatusChatInputKeyboardInputExpectedAsText"
|
|
|
|
|
when: windowShown
|
|
|
|
|
|
|
|
|
|
property StatusChatInput controlUnderTest: null
|
|
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
|
Utils.globalUtilsInst = globalUtilsMock
|
|
|
|
|
controlUnderTest = createTemporaryObject(componentUnderTest, root)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Scenario: StatusChatInput will display any typed Ascii visible character
|
|
|
|
|
//Given a new instance of StatusChatInput
|
|
|
|
|
//When the user is typing any Ascii visible character as <text>
|
|
|
|
|
//Then the text is displayed as it is typed
|
|
|
|
|
//And the input text is not modified by mentions processor
|
|
|
|
|
//And the input text is not modified by emoji processor
|
|
|
|
|
//
|
|
|
|
|
//Example:
|
|
|
|
|
//text
|
|
|
|
|
//(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~)
|
|
|
|
|
function test_keyboard_any_ascii_input() {
|
|
|
|
|
var expectations_after_typed_text = (typedText) => {
|
|
|
|
|
verify(controlUnderTest.getPlainText() === typedText,
|
|
|
|
|
`Expected text: ${typedText}, received: ${controlUnderTest.getPlainText()}`)
|
|
|
|
|
verify(controlUnderTest.textInput.text === controlUnderTest.getTextWithPublicKeys(),
|
|
|
|
|
`Expected text: ${controlUnderTest.textInput.text}, received: ${controlUnderTest.getTextWithPublicKeys()}`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
testHelper.when_text_is_typed(statusChatInputKeyboardInputExpectedAsText,
|
|
|
|
|
testHelper.get_all_ascii_characters(),
|
|
|
|
|
expectations_after_typed_text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scenario outline: User can navigate in chat input using keyboard
|
|
|
|
|
// Given the user has typed text on multiple lines in StatusChatInput
|
|
|
|
|
// """
|
|
|
|
|
// Typing on first row
|
|
|
|
|
// Typing on the second row
|
|
|
|
|
// Typing on the third row
|
|
|
|
|
//
|
|
|
|
|
// """
|
|
|
|
|
// When the user hits <direction>
|
|
|
|
|
// The cursor will move inside the text area to the <direction>
|
|
|
|
|
//
|
|
|
|
|
// Examples:
|
|
|
|
|
// |direction|
|
|
|
|
|
// |left|
|
|
|
|
|
// |right|
|
|
|
|
|
// |up|
|
|
|
|
|
// |down|
|
|
|
|
|
|
|
|
|
|
// When the user hits "left"
|
|
|
|
|
// The cursor will move inside the text area to the "left"
|
|
|
|
|
// When the user hits "right"
|
|
|
|
|
// The cursor will move inside the text area to the "right"
|
|
|
|
|
// When the user hits "up"
|
|
|
|
|
// The cursor will move "up" inside the text area, on the previous line if any
|
|
|
|
|
// When the user hits "down"
|
|
|
|
|
// The cursor will move "down" inside the text area, on the next line if any
|
|
|
|
|
function test_user_can_navigate_with_keys() {
|
|
|
|
|
|
|
|
|
|
var expect_row_to_be_added = (rowNb) => {
|
|
|
|
|
const expectedText = testData.multiLineText.slice(0, rowNb).join("\n") + "\n"
|
|
|
|
|
|
|
|
|
|
compare(controlUnderTest.getPlainText(), expectedText)
|
|
|
|
|
compare(controlUnderTest.textInput.lineCount, rowNb + 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
testHelper.when_multiline_text_is_typed(statusChatInputKeyboardInputExpectedAsText,
|
|
|
|
|
testData.multiLineText,
|
|
|
|
|
expect_row_to_be_added,
|
|
|
|
|
(typedRowText) => {})
|
|
|
|
|
|
|
|
|
|
compare(controlUnderTest.textInput.cursorPosition,
|
|
|
|
|
controlUnderTest.textInput.length,
|
|
|
|
|
"Expected cursor position at the end of text")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Right)
|
|
|
|
|
compare(controlUnderTest.textInput.cursorPosition,
|
|
|
|
|
controlUnderTest.textInput.length,
|
|
|
|
|
"Expected cursor not to move as we already are at the end of text")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Left)
|
|
|
|
|
compare(controlUnderTest.textInput.cursorPosition,
|
|
|
|
|
controlUnderTest.textInput.length - 1,
|
|
|
|
|
"Expected cursor to move left")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Up)
|
|
|
|
|
compare(controlUnderTest.textInput.cursorPosition, 42,
|
|
|
|
|
"Expected cursor to be on the on the second row, two chars from the end")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Up)
|
|
|
|
|
compare(controlUnderTest.textInput.cursorPosition, 19,
|
|
|
|
|
"Expected cursor to be on the on end of the first line")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Down)
|
|
|
|
|
compare(controlUnderTest.textInput.cursorPosition,
|
|
|
|
|
42,
|
|
|
|
|
"Expected cursor to be back on the second row, two chars from the end")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Down)
|
|
|
|
|
compare(controlUnderTest.textInput.cursorPosition,
|
|
|
|
|
controlUnderTest.textInput.length - 1,
|
|
|
|
|
"Expected cursor to be back at the end of second line")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Down)
|
|
|
|
|
compare(controlUnderTest.textInput.cursorPosition,
|
|
|
|
|
controlUnderTest.textInput.length,
|
|
|
|
|
"Expected cursor to be back at the end of text")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scenario: User can select text using keyboard
|
|
|
|
|
// Given the user has typed text on multiple lines in StatusChatInput
|
|
|
|
|
// """
|
|
|
|
|
// Typing on first row
|
|
|
|
|
// Typing on the second row
|
|
|
|
|
// Typing on the third row
|
|
|
|
|
|
|
|
|
|
// """
|
|
|
|
|
// When the user holds shift key
|
|
|
|
|
// When the user hits <direction>
|
|
|
|
|
// The cursor will move inside the text area to the <direction>
|
|
|
|
|
// And the selected text will be <selectedText>
|
|
|
|
|
|
|
|
|
|
// Examples:
|
|
|
|
|
// |direction| selectedText |
|
|
|
|
|
// |right| "" |
|
|
|
|
|
// |left| "\u2028" |
|
|
|
|
|
// |up| "ow\u2028Typing on the third row\u2028" |
|
|
|
|
|
// |down| "\u2028" |
|
|
|
|
|
function test_user_can_select_text() {
|
|
|
|
|
|
|
|
|
|
testHelper.given_multiline_text_is_typed(statusChatInputKeyboardInputExpectedAsText,
|
|
|
|
|
testData.multiLineText)
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Right, Qt.ShiftModifier)
|
|
|
|
|
compare(controlUnderTest.textInput.selectedText, "", "No selection expected")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Left, Qt.ShiftModifier)
|
|
|
|
|
compare(controlUnderTest.textInput.selectedText, "\u2028", "Expected line separator.")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Up, Qt.ShiftModifier)
|
|
|
|
|
compare(controlUnderTest.textInput.selectedText, "ow\u2028Typing on the third row\u2028")
|
|
|
|
|
|
|
|
|
|
keyClick(Qt.Key_Down, Qt.ShiftModifier)
|
|
|
|
|
compare(controlUnderTest.textInput.selectedText, "\u2028", "Expected line separator.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scenario: The user can select all text in StatusChatInput
|
|
|
|
|
// Given the user has typed text in StatusChatInput
|
|
|
|
|
// """
|
|
|
|
|
// Typing on first row
|
|
|
|
|
// Typing on the second row
|
|
|
|
|
// Typing on the third row
|
|
|
|
|
|
|
|
|
|
// """
|
|
|
|
|
// When the user hits select all shortcut
|
|
|
|
|
// Then all the text is selected
|
|
|
|
|
function test_user_can_select_all_text() {
|
|
|
|
|
testHelper.given_multiline_text_is_typed(statusChatInputKeyboardInputExpectedAsText,
|
|
|
|
|
testData.multiLineText)
|
|
|
|
|
|
|
|
|
|
keySequence(StandardKey.SelectAll)
|
|
|
|
|
compare(controlUnderTest.textInput.selectedText, testData.multiLineText.join("\u2028") + "\u2028")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scenario: The user can cut text in StatusChatInput
|
|
|
|
|
// Given the user has selected all text in StatusChatInput
|
|
|
|
|
// """
|
|
|
|
|
// Typing on first row
|
|
|
|
|
// Typing on the second row
|
|
|
|
|
// Typing on the third row
|
|
|
|
|
|
|
|
|
|
// """
|
|
|
|
|
// When the user hits cut shortcut
|
|
|
|
|
// Then all selected text is removed
|
|
|
|
|
// And his clipboard contains the initially selected text
|
|
|
|
|
function test_user_can_cut_text() {
|
|
|
|
|
testHelper.given_multiline_text_is_typed(statusChatInputKeyboardInputExpectedAsText,
|
|
|
|
|
testData.multiLineText)
|
|
|
|
|
|
|
|
|
|
keySequence(StandardKey.SelectAll)
|
|
|
|
|
keySequence(StandardKey.Cut)
|
|
|
|
|
compare(controlUnderTest.getPlainText(), "")
|
|
|
|
|
|
|
|
|
|
keySequence(StandardKey.Paste)
|
|
|
|
|
compare(controlUnderTest.getPlainText(), testData.multiLineText.join("\n") + "\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Given the user has contact JohnDoe
|
|
|
|
|
// And the user types a message "Hello @JohnDoe!"
|
|
|
|
|
// Then the user can mention his contact as @JohnDoe
|
|
|
|
|
// And mentions suggestions openes when "@" is typed
|
|
|
|
|
// And the mentions suggestions will contain @JohnDoe as the mention is typed
|
|
|
|
|
// And the mentions suggestions will close once "!" is typed
|
|
|
|
|
// And the mention is sepparated by the next input once the mention is added to TextArea
|
|
|
|
|
// And the contact name from the mention can be replaced with "'0x0JohnDoe'" public key
|
|
|
|
|
function test_keyboard_mentions_input() {
|
|
|
|
|
testHelper.when_the_user_has_contact(controlUnderTest, "JohnDoe", (contact) => {
|
|
|
|
|
verify(controlUnderTest.usersStore.usersModel.count == 1, `Expected user to have 1 contact`)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
let expect_visible_suggestions_on_mention_typing = (typedText) => {
|
|
|
|
|
if(typedText.startsWith("Hello @") && !typedText.endsWith("!")) {
|
|
|
|
|
verify(controlUnderTest.suggestions.visible == true,
|
|
|
|
|
`Expected the mention suggestions to be visible`)
|
|
|
|
|
|
|
|
|
|
verify(controlUnderTest.suggestions.listView.currentItem.objectName === "JohnDoe",
|
|
|
|
|
`Expected the mention suggestions current item to be JohnDoe, received ${controlUnderTest.suggestions.listView.currentItem.objectName}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
testHelper.when_text_is_typed(statusChatInputKeyboardInputExpectedAsText,
|
|
|
|
|
"Hello @JohnDoe!", expect_visible_suggestions_on_mention_typing)
|
|
|
|
|
|
|
|
|
|
verify(controlUnderTest.suggestions.visible == false,
|
|
|
|
|
`Expected the mention suggestions to be closed`)
|
|
|
|
|
|
|
|
|
|
compare(controlUnderTest.getPlainText(),
|
|
|
|
|
"Hello \[\[mention\]\]@JohnDoe\[\[mention\]\] !",
|
|
|
|
|
"Expected the mention to be inserted")
|
|
|
|
|
|
|
|
|
|
var textWithPubKey = controlUnderTest.getTextWithPublicKeys()
|
|
|
|
|
verify(textWithPubKey.includes("@0x0JohnDoe"),
|
|
|
|
|
"Expected @pubKey to replace @contactName")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestCase {
|
|
|
|
|
id: statusChatInputStandardKeySequence
|
|
|
|
|
name: "StatusChatInputStandardKeySequence"
|
|
|
|
|
when: windowShown
|
|
|
|
|
|
|
|
|
|
property var controlUnderTest: null
|
|
|
|
|
property TextArea standardInput: null
|
|
|
|
|
|
|
|
|
|
Component {
|
|
|
|
|
id: standardTextAreaComponent
|
|
|
|
|
TextArea {
|
|
|
|
|
width: 400
|
|
|
|
|
height: 100
|
|
|
|
|
textFormat: Qt.RichText
|
|
|
|
|
selectByMouse: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initTestCase() {
|
|
|
|
|
Utils.globalUtilsInst = globalUtilsMock
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scenario: Default TextArea keyboard shortcuts are not altered by StatusChatInput
|
|
|
|
|
// Given a new StatusChatInput instance
|
|
|
|
|
// And a new standard Qt TextArea instance
|
|
|
|
|
// When the user is typing multiline text
|
|
|
|
|
// """
|
|
|
|
|
// Typing on first row
|
|
|
|
|
// Typing on the second row
|
|
|
|
|
// Typing on the third row
|
|
|
|
|
|
|
|
|
|
// """
|
|
|
|
|
// And is using standard key shortcutss
|
|
|
|
|
// Then the StatusChatInput will behave as standard TextArea
|
|
|
|
|
function init() {
|
|
|
|
|
controlUnderTest = createTemporaryObject(componentUnderTest, root)
|
|
|
|
|
standardInput = createTemporaryObject(standardTextAreaComponent, root)
|
|
|
|
|
|
|
|
|
|
standardInput.forceActiveFocus()
|
|
|
|
|
testHelper.given_multiline_text_is_typed(statusChatInputStandardKeySequence, testData.multiLineText)
|
|
|
|
|
|
|
|
|
|
controlUnderTest.textInput.forceActiveFocus()
|
|
|
|
|
testHelper.given_multiline_text_is_typed(statusChatInputStandardKeySequence, testData.multiLineText)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function test_standard_key_shortcuts_data() {
|
|
|
|
|
return [
|
|
|
|
|
{ tag: "Delete", key: StandardKey.Delete, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "Undo", key: StandardKey.Undo, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "Redo", key: StandardKey.Redo, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectAll", key: StandardKey.SelectAll, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0 , expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "Bold", key: StandardKey.Bold, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 3, expectIdenticalPlainText: false, expectIdenticalSelection: false },
|
|
|
|
|
{ tag: "Italic", key: StandardKey.Italic, initialCursorPosition: 0, initialSelectionStart: 8, initialSelectionEnd: 10, expectIdenticalPlainText: false, expectIdenticalSelection: false },
|
|
|
|
|
{ tag: "MoveToNextChar", key: StandardKey.MoveToNextChar, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "MoveToPreviousChar", key: StandardKey.MoveToPreviousChar, initialCursorPosition: 5, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "MoveToNextWord", key: StandardKey.MoveToNextWord, initialCursorPosition: 2, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true},
|
|
|
|
|
{ tag: "MoveToPreviousWord", key: StandardKey.MoveToPreviousWord, initialCursorPosition: 15, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true},
|
|
|
|
|
{ tag: "MoveToNextLine", key: StandardKey.MoveToNextLine, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true},
|
|
|
|
|
{ tag: "MoveToPreviousLine", key: StandardKey.MoveToPreviousLine, initialCursorPosition: 30, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true},
|
|
|
|
|
{ tag: "MoveToStartOfLine", key: StandardKey.MoveToStartOfLine, initialCursorPosition: 10, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "MoveToEndOfLine", key: StandardKey.MoveToEndOfLine, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "MoveToStartOfBlock", key: StandardKey.MoveToStartOfBlock, initialCursorPosition: 40, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "MoveToEndOfBlock", key: StandardKey.MoveToEndOfBlock, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "MoveToStartOfDocument", key: StandardKey.MoveToStartOfDocument, initialCursorPosition: 40, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "MoveToEndOfDocument", key: StandardKey.MoveToEndOfDocument, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectNextChar", key: StandardKey.SelectNextChar, initialCursorPosition: 5, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectPreviousChar", key: StandardKey.SelectPreviousChar, initialCursorPosition: 5, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectNextWord", key: StandardKey.SelectNextWord, initialCursorPosition: 2, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectPreviousWord", key: StandardKey.SelectPreviousWord, initialCursorPosition: 15, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectNextLine", key: StandardKey.SelectNextLine, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectPreviousLine", key: StandardKey.SelectPreviousLine, initialCursorPosition: 30, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectStartOfLine", key: StandardKey.SelectStartOfLine, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectEndOfLine", key: StandardKey.SelectEndOfLine, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectStartOfBlock", key: StandardKey.SelectStartOfBlock, initialCursorPosition: 40, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectEndOfBlock", key: StandardKey.SelectEndOfBlock, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectStartOfDocument", key: StandardKey.SelectStartOfDocument, initialCursorPosition: 40, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "SelectEndOfDocument", key: StandardKey.SelectEndOfDocument, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "DeleteStartOfWord", key: StandardKey.DeleteStartOfWord, initialCursorPosition: 4, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "DeleteEndOfWord", key: StandardKey.DeleteEndOfWord, initialCursorPosition: 4, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "DeleteEndOfLine", key: StandardKey.DeleteEndOfLine, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "InsertLineSeparator", key: StandardKey.InsertLineSeparator, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "Deselect", key: StandardKey.Deselect, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 10, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "DeleteCompleteLine", key: StandardKey.DeleteCompleteLine, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "Backspace", key: StandardKey.Backspace, initialCursorPosition: 5, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true },
|
|
|
|
|
{ tag: "AddTab", key: StandardKey.AddTab, initialCursorPosition: 0, initialSelectionStart: 0, initialSelectionEnd: 0, expectIdenticalPlainText: true }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function test_standard_key_shortcuts(data) {
|
|
|
|
|
|
|
|
|
|
standardInput.forceActiveFocus()
|
|
|
|
|
standardInput.cursorPosition = data.initialCursorPosition
|
|
|
|
|
standardInput.select(data.initialSelectionStart, data.initialSelectionEnd)
|
|
|
|
|
|
|
|
|
|
keySequence(data.key)
|
|
|
|
|
let standardControlSelection = standardInput.selectedText
|
|
|
|
|
let standardControlCursorPosition = standardInput.cursorPosition
|
|
|
|
|
|
|
|
|
|
controlUnderTest.textInput.forceActiveFocus()
|
|
|
|
|
controlUnderTest.textInput.cursorPosition = data.initialCursorPosition
|
|
|
|
|
controlUnderTest.textInput.select(data.initialSelectionStart, data.initialSelectionEnd)
|
|
|
|
|
|
|
|
|
|
keySequence(data.key)
|
|
|
|
|
let controlUnderTestSelection = controlUnderTest.textInput.selectedText
|
|
|
|
|
let controlUnderTestCursorPosition = controlUnderTest.textInput.cursorPosition
|
|
|
|
|
|
|
|
|
|
if(data.expectIdenticalPlainText) {
|
|
|
|
|
compare(controlUnderTest.textInput.length, standardInput.length, "Expected identical text length")
|
|
|
|
|
compare(controlUnderTest.textInput.getText(0, controlUnderTest.textInput.length),
|
|
|
|
|
standardInput.getText(0, standardInput.length),
|
|
|
|
|
"Expected identical text")
|
|
|
|
|
compare(controlUnderTestCursorPosition, standardControlCursorPosition, "Expected identical cursor position")
|
|
|
|
|
compare(controlUnderTestSelection, standardControlSelection, "Expected identical selected text")
|
|
|
|
|
} else {
|
|
|
|
|
//the text is expected to be different due to custom processor
|
|
|
|
|
//Ex: bold or italic where text is wrapped in specific symbols
|
|
|
|
|
verify(controlUnderTest.textInput.getText(0, controlUnderTest.textInput.length) !==
|
|
|
|
|
standardInput.getText(0, standardInput.length),
|
|
|
|
|
"Expected different text")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestCase {
|
|
|
|
|
id: statusChatInputEmojiAndMentions
|
|
|
|
|
name: "StatusChatInputEmojiAndMentions"
|
|
|
|
|
when: windowShown
|
|
|
|
|
|
|
|
|
|
property StatusChatInput controlUnderTest: null
|
|
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
|
Utils.globalUtilsInst = globalUtilsMock
|
|
|
|
|
controlUnderTest = createTemporaryObject(componentUnderTest, root)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scenario: The user can type text, mention and emoji
|
|
|
|
|
// Given a new instance of StatusChatInput
|
|
|
|
|
// And the user has <mention> as contact
|
|
|
|
|
// When the user is typing <text>
|
|
|
|
|
// Then the text is displayed in the input field as <expectedText>
|
|
|
|
|
// And the <mention> is inserted in the input field
|
|
|
|
|
// And the <emoji> is inserted in the input field
|
|
|
|
|
// And the <mention> can be replaced with contact public key
|
|
|
|
|
|
|
|
|
|
// Examples:
|
|
|
|
|
// text | mention | expectedPlainText
|
|
|
|
|
// “Hello John:D” | | “Hello John\uD83D\uDE04“
|
|
|
|
|
// “Hello @JohnDoe” | @JohnDoe | “Hello @JohnDoe ”
|
|
|
|
|
// “Hello:D@JohnDoe“ | @JohnDoe | “Hello\uD83D\uDE04 @JohnDoe “
|
|
|
|
|
// “:DHello@JohnDoe” | @JohnDoe | “\uD83D\uDE04 Hello @JohnDoe ”
|
|
|
|
|
// “:D:D:D:D:D:D@JohnDoe:D:D:D” | @JohnDoe | “\uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 @JohnDoe \uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 ”
|
|
|
|
|
// “Hello:@JohnDoe1:D@JohnDoe2:D | @JohnDoe1 @JohnDoe2 | “Hello:@JohnDoe1 \uD83D\uDE04 @JohnDoe2 \uD83D\uDE04 ”
|
|
|
|
|
// “Hello:@JohnDoe1:D@JohnDoe2:D | | “Hello:@JohnDoe1\uD83D\uDE04 @JohnDoe2\uD83D\uDE04 ”
|
|
|
|
|
function test_text_mention_emoji_input_data() {
|
|
|
|
|
return [
|
|
|
|
|
{ tag: "Hello John:D", text: "Hello John:D", mention: [], expectedText: "Hello John\uD83D\uDE04 " },
|
|
|
|
|
{ tag: "Hello @JohnDoe", text: "Hello @JohnDoe", mention: ["JohnDoe"], expectedText: "Hello @JohnDoe " },
|
|
|
|
|
{ tag: "Hello:D@JohnDoe", text: "Hello:D@JohnDoe", mention: ["JohnDoe"], expectedText: "Hello\uD83D\uDE04 @JohnDoe " },
|
|
|
|
|
{ tag: ":DHello@JohnDoe", text: ":DHello@JohnDoe", mention: ["JohnDoe"], expectedText: "\uD83D\uDE04 Hello@JohnDoe " },
|
|
|
|
|
{ tag: ":D:D:D:D:D:D@JohnDoe:D:D:D", text: ":D:D:D:D:D:D@JohnDoe:D:D:D", mention: ["JohnDoe"], expectedText: "\uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 @JohnDoe \uD83D\uDE04 \uD83D\uDE04 \uD83D\uDE04 " },
|
|
|
|
|
{ tag: "Hello:@JohnDoe1:D@JohnDoe2:D with contact", text: "Hello @JohnDoe1:D@JohnDoe2:D", mention: ["JohnDoe1", "JohnDoe2"], expectedText: "Hello @JohnDoe1 \uD83D\uDE04 @JohnDoe2 \uD83D\uDE04 " },
|
|
|
|
|
{ tag: "Hello:@JohnDoe1:D@JohnDoe2:D without contact", text: "Hello @JohnDoe1:D@JohnDoe2:D", mention: [], expectedText: "Hello @JohnDoe1\uD83D\uDE04 @JohnDoe2\uD83D\uDE04 " },
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function test_text_mention_emoji_input(data) {
|
|
|
|
|
data.mention.forEach(contact => testHelper.when_the_user_has_contact(controlUnderTest, contact, (addedContact) => {}))
|
|
|
|
|
testHelper.when_text_is_typed(statusChatInputEmojiAndMentions,
|
|
|
|
|
data.text, (typedText) => { keyClick(Qt.Key_Shift) /*mentions will be inserted only after a key input*/})
|
|
|
|
|
var plainText = controlUnderTest.removeMentions(controlUnderTest.getPlainText())
|
|
|
|
|
|
|
|
|
|
compare(plainText,
|
|
|
|
|
data.expectedText)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
|
id: testHelper
|
|
|
|
|
|
|
|
|
|
function get_all_ascii_characters() {
|
|
|
|
|
let result = '';
|
|
|
|
|
for( let i = 32; i <= 126; i++ )
|
|
|
|
|
{
|
|
|
|
|
result += String.fromCharCode( i );
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function when_the_user_has_contact(controlUnderTest: StatusChatInput, contact: string, expectationAfterContactAdded) {
|
|
|
|
|
controlUnderTest.usersStore.usersModel.append({
|
|
|
|
|
pubKey: `0x0${contact}`,
|
|
|
|
|
onlineStatus: 1,
|
|
|
|
|
isContact: true,
|
|
|
|
|
isVerified: true,
|
|
|
|
|
isAdmin: false,
|
|
|
|
|
isUntrustworthy: false,
|
|
|
|
|
displayName: contact,
|
|
|
|
|
alias: `${contact}-alias`,
|
|
|
|
|
localNickname: `${contact}-local-nickname`,
|
|
|
|
|
ensName: `${contact}.stateofus.eth`,
|
|
|
|
|
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
|
|
|
|
|
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC",
|
|
|
|
|
colorId: 7
|
|
|
|
|
})
|
|
|
|
|
expectationAfterContactAdded(contact)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function when_text_is_typed(testCase: TestCase, textInput: string, expectationAfterTextInput) {
|
|
|
|
|
for (let i = 0; i < textInput.length; i++) {
|
|
|
|
|
const typedText = textInput.slice(0,i+1)
|
|
|
|
|
const key = textInput[i];
|
|
|
|
|
testCase.keyClick(key)
|
|
|
|
|
expectationAfterTextInput(typedText)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function when_multiline_text_is_typed(testCase: TestCase, textLines: list<string>, expectationAfterNewLine, expectationAfterTextInput) {
|
|
|
|
|
for(let i = 0; i < textLines.length; i++) {
|
|
|
|
|
when_text_is_typed(testCase, textLines[i], expectationAfterTextInput)
|
|
|
|
|
testCase.keyClick(Qt.Key_Enter, Qt.ShiftModifier)
|
|
|
|
|
expectationAfterNewLine(i+1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function given_multiline_text_is_typed(testCase: TestCase, textLines: list<string>) {
|
|
|
|
|
when_multiline_text_is_typed(testCase, textLines, (lineNb) => {}, (typedText) => {})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
|
id: globalUtilsMock
|
|
|
|
|
|
|
|
|
|
property var plainText
|
|
|
|
|
property var isCompressedPubKey: function (publicKey) {
|
|
|
|
|
function plainText(htmlText) {
|
|
|
|
|
return TextUtils.htmlToPlainText(htmlText)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isCompressedPubKey(publicKey) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -26,7 +535,7 @@ Item {
|
|
|
|
|
property ListModel gifColumnA: ListModel {}
|
|
|
|
|
|
|
|
|
|
readonly property var formationChars: (["*", "`", "~"])
|
|
|
|
|
|
|
|
|
|
property bool isTenorWarningAccepted: true
|
|
|
|
|
function getSelectedTextWithFormationChars(messageInputField) {
|
|
|
|
|
let i = 1
|
|
|
|
|
let text = ""
|
|
|
|
@ -49,31 +558,11 @@ Item {
|
|
|
|
|
Component.onCompleted: {
|
|
|
|
|
RootStore.isGifWidgetEnabled = true
|
|
|
|
|
RootStore.isWalletEnabled = true
|
|
|
|
|
RootStore.isTenorWarningAccepted = true
|
|
|
|
|
RootStore.isTenorWarningAccepted = rootStoreMock.isTenorWarningAccepted
|
|
|
|
|
RootStore.getSelectedTextWithFormationChars = rootStoreMock.getSelectedTextWithFormationChars
|
|
|
|
|
RootStore.gifColumnA = rootStoreMock.gifColumnA
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StatusChatInput {
|
|
|
|
|
id: controlUnderTest
|
|
|
|
|
width: parent.width
|
|
|
|
|
property var globalUtils: globalUtilsMock
|
|
|
|
|
Component.onCompleted: {
|
|
|
|
|
Global.dragArea = root
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestCase {
|
|
|
|
|
name: "TestChatInputInitialization"
|
|
|
|
|
when: windowShown
|
|
|
|
|
|
|
|
|
|
function test_empty_chat_input() {
|
|
|
|
|
globalUtilsMock.plainText = (htmlText) => {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
verify(controlUnderTest.textInput.length == 0, `Expected 0 text length, received: ${controlUnderTest.textInput.length}`)
|
|
|
|
|
verify(controlUnderTest.getPlainText() == "", `Expected empty string, received: ${controlUnderTest.getPlainText()}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|