tests(StatusInput): add regression test to check for qml output
Also improves on the test structure
This commit is contained in:
parent
b2ac2794bb
commit
eae4e0f405
|
@ -16,3 +16,4 @@ sandbox/qmlcache_loader.cpp
|
||||||
doc/html
|
doc/html
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
.vscode
|
.vscode
|
||||||
|
build/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
project(TestStatusInputWithRegex LANGUAGES CXX)
|
project(TestControls LANGUAGES CXX)
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ list(APPEND QML_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../src/")
|
||||||
set(QML_IMPORT_PATH "${QML_DIRS}" CACHE STRING "Qt Creator extra qml import paths")
|
set(QML_IMPORT_PATH "${QML_DIRS}" CACHE STRING "Qt Creator extra qml import paths")
|
||||||
set(QML2_IMPORT_PATH "${QML_DIRS}" CACHE STRING "Qt Creator extra qml import paths")
|
set(QML2_IMPORT_PATH "${QML_DIRS}" CACHE STRING "Qt Creator extra qml import paths")
|
||||||
|
|
||||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS QuickTest Qml REQUIRED)
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS QuickTest Qml Quick REQUIRED)
|
||||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS QuickTest Qml REQUIRED)
|
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS QuickTest Qml Quick REQUIRED)
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
|
||||||
|
@ -24,10 +24,20 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
# no need to copy around qml test files for shadow builds - just set the respective define
|
# no need to copy around qml test files for shadow builds - just set the respective define
|
||||||
add_definitions(-DQUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
|
add_definitions(-DQUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
|
||||||
add_executable(TestStatusInputWithRegex main.cpp)
|
add_executable(${PROJECT_NAME} main.cpp)
|
||||||
add_test(NAME TestStatusInputWithRegex COMMAND TestStatusInputWithRegex)
|
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
|
||||||
|
|
||||||
target_link_libraries(TestStatusInputWithRegex PRIVATE
|
# TODO: move this to a test helpers library
|
||||||
|
target_include_directories(${PROJECT_NAME}
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(TestHelpers)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
Qt${QT_VERSION_MAJOR}::QuickTest
|
Qt${QT_VERSION_MAJOR}::QuickTest
|
||||||
Qt${QT_VERSION_MAJOR}::Qml)
|
Qt${QT_VERSION_MAJOR}::Qml
|
||||||
|
Qt${QT_VERSION_MAJOR}::Quick
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQml 2.14
|
||||||
|
import QtTest 1.0
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
|
||||||
|
//> Simulate key and wait for side effects
|
||||||
|
function pressKeyAndWait(test, item, key) {
|
||||||
|
test.keyClick(key)
|
||||||
|
|
||||||
|
test.waitForRendering(item)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module StatusQ.TestHelpers
|
||||||
|
|
||||||
|
singleton TestUtils 0.1 TestUtils.qml
|
|
@ -0,0 +1,12 @@
|
||||||
|
target_include_directories(${PROJECT_NAME}
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(${PROJECT_NAME}
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/MonitorQtOutput.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/MonitorQtOutput.cpp
|
||||||
|
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
|
||||||
|
)
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "MonitorQtOutput.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
std::weak_ptr<QString> MonitorQtOutput::m_qtMessageOutputForSharing;
|
||||||
|
std::mutex MonitorQtOutput::m_mutex;
|
||||||
|
|
||||||
|
|
||||||
|
MonitorQtOutput::MonitorQtOutput()
|
||||||
|
{
|
||||||
|
// Ensure only one instance registers a handler
|
||||||
|
// Warning: don't QT's call loger functions inside the critical section
|
||||||
|
std::unique_lock<std::mutex> localLock(m_mutex);
|
||||||
|
auto globalMsgOut = m_qtMessageOutputForSharing.lock();
|
||||||
|
if(!globalMsgOut) {
|
||||||
|
// Install message handler if not already done
|
||||||
|
m_thisMessageOutput = std::make_shared<QString>();
|
||||||
|
m_qtMessageOutputForSharing = m_thisMessageOutput;
|
||||||
|
qInstallMessageHandler(qtMessageOutput);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_thisMessageOutput = globalMsgOut;
|
||||||
|
m_start = m_thisMessageOutput->length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorQtOutput::~MonitorQtOutput()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> localLock(m_mutex);
|
||||||
|
if(m_thisMessageOutput.use_count() == 1) {
|
||||||
|
// Last instance, deregister the handler
|
||||||
|
qInstallMessageHandler(0);
|
||||||
|
m_thisMessageOutput.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MonitorQtOutput::qtMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> localLock(m_mutex);
|
||||||
|
auto globalMsgOut = m_qtMessageOutputForSharing.lock();
|
||||||
|
assert(globalMsgOut != nullptr);
|
||||||
|
globalMsgOut->append(msg + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
MonitorQtOutput::qtOuput()
|
||||||
|
{
|
||||||
|
assert(m_thisMessageOutput->length() >= m_start);
|
||||||
|
return m_thisMessageOutput->right(m_thisMessageOutput->length() - m_start);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Monitor output for tests and declarativelly control message handler availability
|
||||||
|
/// \todo Check that QML doesn't keep instance between test runs
|
||||||
|
///
|
||||||
|
class MonitorQtOutput : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
MonitorQtOutput();
|
||||||
|
~MonitorQtOutput();
|
||||||
|
|
||||||
|
Q_INVOKABLE QString qtOuput();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void qtMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||||
|
|
||||||
|
// Use it to keep track of qInstallMessageHandler call
|
||||||
|
static std::weak_ptr<QString> m_qtMessageOutputForSharing;
|
||||||
|
static std::mutex m_mutex;
|
||||||
|
std::shared_ptr<QString> m_thisMessageOutput;
|
||||||
|
int m_start = 0;
|
||||||
|
};
|
|
@ -1,6 +1,8 @@
|
||||||
#include <QtQuickTest/quicktest.h>
|
#include <QtQuickTest/quicktest.h>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
#include "TestHelpers/MonitorQtOutput.h"
|
||||||
|
|
||||||
class TestSetup : public QObject
|
class TestSetup : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -13,7 +15,12 @@ public slots:
|
||||||
{
|
{
|
||||||
// TODO: Workaround until we make StatusQ a CMake library
|
// TODO: Workaround until we make StatusQ a CMake library
|
||||||
engine->addImportPath("../../src/");
|
engine->addImportPath("../../src/");
|
||||||
|
engine->addImportPath(".");
|
||||||
|
// TODO: Alternative to not yet supported QML_ELEMENT
|
||||||
|
qmlRegisterType<MonitorQtOutput>("StatusQ.TestHelpers", 0, 1, "MonitorQtOutput");
|
||||||
}
|
}
|
||||||
|
private:
|
||||||
|
MonitorQtOutput _monitorOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
QUICK_TEST_MAIN_WITH_SETUP(TestControls, TestSetup)
|
QUICK_TEST_MAIN_WITH_SETUP(TestControls, TestSetup)
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtTest 1.0
|
||||||
|
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
|
import StatusQ.TestHelpers 0.1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 300
|
||||||
|
height: 100
|
||||||
|
|
||||||
|
StatusBaseInput {
|
||||||
|
id: statusInput
|
||||||
|
text: "Control under test"
|
||||||
|
placeholderText: "Placeholder"
|
||||||
|
focus: true
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase {
|
||||||
|
id: testCase
|
||||||
|
name: "CheckQmlWarnings"
|
||||||
|
|
||||||
|
when: windowShown
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test guards
|
||||||
|
|
||||||
|
function initTestCase() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
statusInput.text = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tests
|
||||||
|
|
||||||
|
function test_initial_empty_is_valid() {
|
||||||
|
mouseClick(statusInput)
|
||||||
|
// Do some editing
|
||||||
|
TestUtils.pressKeyAndWait(testCase, statusInput, Qt.Key_B)
|
||||||
|
TestUtils.pressKeyAndWait(testCase, statusInput, Qt.Key_Left)
|
||||||
|
TestUtils.pressKeyAndWait(testCase, statusInput, Qt.Key_A)
|
||||||
|
verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorQtOutput {
|
||||||
|
id: qtOuput
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ import QtTest 1.0
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
import StatusQ.Controls.Validators 0.1
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
|
import StatusQ.TestHelpers 0.1
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: 300
|
width: 300
|
||||||
height: 100
|
height: 100
|
||||||
|
@ -28,6 +30,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase {
|
TestCase {
|
||||||
|
id: regexTC
|
||||||
|
|
||||||
name: "RegexValidationTest"
|
name: "RegexValidationTest"
|
||||||
|
|
||||||
when: windowShown
|
when: windowShown
|
||||||
|
@ -50,9 +54,9 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_regex_validation() {
|
function test_regex_validation() {
|
||||||
keyClick(Qt.Key_1)
|
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_1)
|
||||||
verify(statusInput.valid, "Expected valid input")
|
verify(statusInput.valid, "Expected valid input")
|
||||||
keyClick(Qt.Key_Ampersand)
|
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_Ampersand)
|
||||||
verify(!statusInput.valid, "Expected invalid input")
|
verify(!statusInput.valid, "Expected invalid input")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,10 +65,10 @@ Item {
|
||||||
|
|
||||||
verify(statusInput.valid, "Expected valid input")
|
verify(statusInput.valid, "Expected valid input")
|
||||||
verify(statusInput.text.length === 0, "Expected no input")
|
verify(statusInput.text.length === 0, "Expected no input")
|
||||||
keyClick(Qt.Key_2)
|
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_2)
|
||||||
verify(statusInput.valid, "Expected valid input")
|
verify(statusInput.valid, "Expected valid input")
|
||||||
verify(statusInput.text === "2", "Expect one character")
|
verify(statusInput.text === "2", "Expect one character")
|
||||||
keyClick(Qt.Key_Ampersand)
|
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_Ampersand)
|
||||||
verify(statusInput.valid, "Expected invalid input")
|
verify(statusInput.valid, "Expected invalid input")
|
||||||
verify(statusInput.text === "2", "Expect the same input")
|
verify(statusInput.text === "2", "Expect the same input")
|
||||||
}
|
}
|
||||||
|
@ -74,12 +78,43 @@ Item {
|
||||||
const appendInvalidChars = "#@!*"
|
const appendInvalidChars = "#@!*"
|
||||||
|
|
||||||
statusInput.text = "invalid $" + appendInvalidChars
|
statusInput.text = "invalid $" + appendInvalidChars
|
||||||
keyClick(Qt.Key_End)
|
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_End)
|
||||||
verify(!statusInput.valid, "Expected invalid input due to characters not matching")
|
verify(!statusInput.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)
|
||||||
keyClick(Qt.Key_Backspace)
|
TestUtils.pressKeyAndWait(regexTC, statusInput, Qt.Key_Backspace)
|
||||||
verify(statusInput.valid, "Expected valid input")
|
verify(statusInput.valid, "Expected valid input")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase {
|
||||||
|
id: qmlWarnTC
|
||||||
|
|
||||||
|
name: "CheckQmlWarnings"
|
||||||
|
|
||||||
|
when: windowShown
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test guards
|
||||||
|
|
||||||
|
function initTestCase() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
statusInput.text = ""
|
||||||
|
statusInput.validationMode = _defaultValidationMode
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tests
|
||||||
|
|
||||||
|
function test_initial_empty_is_valid() {
|
||||||
|
mouseClick(statusInput)
|
||||||
|
verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorQtOutput {
|
||||||
|
id: qtOuput
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Readme
|
||||||
|
|
||||||
|
## Developer instructions
|
||||||
|
|
||||||
|
CMake
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd StatusQ/tests/TestControls
|
||||||
|
cmake -B ./build/ -S .
|
||||||
|
cmake --build ./build/
|
||||||
|
./build/TestControls
|
||||||
|
```
|
||||||
|
|
||||||
|
QtCreator
|
||||||
|
|
||||||
|
- Open the StatusQ/tests/CMakeLists.txt
|
||||||
|
- Choose a QT kit to run the tests
|
||||||
|
- In the `Test Results` panel choose Run All Tests
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- [ ] Consolidate and integrate with https://github.com/status-im/desktop-ui-tests
|
Loading…
Reference in New Issue