Add qml tests for StatusChatInput

Test parts of the working functionality
This commit is contained in:
Alex Jbanca 2022-12-13 18:18:02 +02:00 committed by Alex Jbanca
parent 513fa1f71d
commit 3059631226
6 changed files with 562 additions and 26 deletions

View File

@ -72,6 +72,7 @@ add_test(NAME FigmaModelTest COMMAND FigmaModelTest)
add_executable(QmlTests add_executable(QmlTests
qmlTests/main.cpp qmlTests/main.cpp
qmlTests/src/TextUtils.cpp qmlTests/src/TextUtils.h
${TEST_QML_FILES}) ${TEST_QML_FILES})
target_compile_definitions(QmlTests target_compile_definitions(QmlTests
PRIVATE QML_IMPORT_ROOT="${CMAKE_CURRENT_LIST_DIR}" PRIVATE QML_IMPORT_ROOT="${CMAKE_CURRENT_LIST_DIR}"

View File

@ -1,4 +1,5 @@
import QtQuick 2.14 import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQml 2.14 import QtQml 2.14
import QtTest 1.0 import QtTest 1.0
@ -6,16 +7,524 @@ import utils 1.0
import shared.status 1.0 import shared.status 1.0
import shared.stores 1.0 import shared.stores 1.0
import TextUtils 1.0
Item { Item {
id: root id: root
width: 600 width: 600
height: 400 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 { QtObject {
id: globalUtilsMock id: globalUtilsMock
property var plainText function plainText(htmlText) {
property var isCompressedPubKey: function (publicKey) { return TextUtils.htmlToPlainText(htmlText)
}
function isCompressedPubKey(publicKey) {
return false return false
} }
} }
@ -26,7 +535,7 @@ Item {
property ListModel gifColumnA: ListModel {} property ListModel gifColumnA: ListModel {}
readonly property var formationChars: (["*", "`", "~"]) readonly property var formationChars: (["*", "`", "~"])
property bool isTenorWarningAccepted: true
function getSelectedTextWithFormationChars(messageInputField) { function getSelectedTextWithFormationChars(messageInputField) {
let i = 1 let i = 1
let text = "" let text = ""
@ -49,31 +558,11 @@ Item {
Component.onCompleted: { Component.onCompleted: {
RootStore.isGifWidgetEnabled = true RootStore.isGifWidgetEnabled = true
RootStore.isWalletEnabled = true RootStore.isWalletEnabled = true
RootStore.isTenorWarningAccepted = true RootStore.isTenorWarningAccepted = rootStoreMock.isTenorWarningAccepted
RootStore.getSelectedTextWithFormationChars = rootStoreMock.getSelectedTextWithFormationChars RootStore.getSelectedTextWithFormationChars = rootStoreMock.getSelectedTextWithFormationChars
RootStore.gifColumnA = rootStoreMock.gifColumnA RootStore.gifColumnA = rootStoreMock.gifColumnA
}
}
StatusChatInput {
id: controlUnderTest
width: parent.width
property var globalUtils: globalUtilsMock
Component.onCompleted: {
Global.dragArea = root 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()}`)
}
}
} }

View File

@ -1,5 +1,6 @@
#include <QtQuickTest/quicktest.h> #include <QtQuickTest/quicktest.h>
#include <QQmlEngine> #include <QQmlEngine>
#include "src/TextUtils.h"
class Setup : public QObject class Setup : public QObject
{ {
@ -18,6 +19,8 @@ public slots:
for (const auto& path : additionalImportPaths) for (const auto& path : additionalImportPaths)
engine->addImportPath(path); engine->addImportPath(path);
qmlRegisterSingletonType<TextUtils>("TextUtils", 1, 0, "TextUtils", &TextUtils::qmlInstance);
} }
}; };

View File

@ -0,0 +1,25 @@
#include "TextUtils.h"
#include <QQmlEngine>
#include <QTextDocumentFragment>
TextUtils::TextUtils(QObject *parent) :
QObject(parent)
{
}
QObject *TextUtils::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
static TextUtils instance;
QQmlEngine::setObjectOwnership(&instance, QQmlEngine::CppOwnership);
return &instance;
}
QString TextUtils::htmlToPlainText(const QString &html) {
return QTextDocumentFragment::fromHtml( html ).toPlainText();
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <QObject>
class QQmlEngine;
class QJSEngine;
class TextUtils : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(TextUtils)
public:
static QObject *qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine);
Q_INVOKABLE QString htmlToPlainText(const QString& html);
private:
TextUtils(QObject *parent = nullptr);
};

View File

@ -4,9 +4,9 @@ import QtQuick 2.14
QtObject { QtObject {
property var userProfileInst property var userProfileInst
property bool isWalletEnabled
property bool isTenorWarningAccepted property bool isTenorWarningAccepted
property bool isGifWidgetEnabled
property bool isWalletEnabled
property var getSelectedTextWithFormationChars property var getSelectedTextWithFormationChars
property var isGifWidgetEnabled
property var gifColumnA property var gifColumnA
} }