Simulating Mouse and Touch Events.
Simulating Mouse and Touch events is likely one of the most useful tools that is offered by QML TestCase, and allows for more complex tests.
There are a couple of big gotchas for both Mouse and Touch Input that are not immediately obvious:
- In order to simulate either mouse or touch your test case MUST be inside of a component that has a defined size. If your root component does not have a defined size it will not be able to accept mouse or touch input.
- If you start a Press event (touch.press, or mousePress) ensure that it gets released by the end of your testFunction, or in your cleanup. Otherwise you will get angry error messages
- You must wait until your window is shown to start your tests.
Touch Events
Touch events start by defining a TouchEvent on a component.
Once a touchEvent has been initialized it can be passed a touch action such as press, move, or release. After each event commit()
must be called for the action to be
passed to the component.
let touch = touchEvent(component);
touch.press(0,component);
touch.commit();
touch.release(0);
touch.commit();
Touch Events are also able to be chained, and as long as each action is followed by a commit()
you can create complex flow. By default a touch action will take place
in the center of the component that a Touch Event is tied to. This can be controlled with optional arguements that define the x and y position of the touch.
Multitouch
Touch Events also support multitouch emulation. The first arguement in any touch action is the ID of the touch that you want to manipulate.
let touch = touchEvent(component);
touch.press(0,component);
touch.press(1,component,1,1);//press(ID,component,x,y);
touch.press(3,component,10,40);
touch.commit();
//test behavior on three touches
touch.release(0);
touch.release(1);
touch.release(2);
touch.commit();
Mouse Events
Unlike Touch Events, Mouse Events do not require defining anything. A mouse event can be affect any QML component. The simpiliest possible Mouse Event is a mouseClick
mouseClick(component);
By default this will click on the component at its center, with the left mouse button. It has additional optional arguements to control x,y, button, modifies, and delays. In addition to mouseClick there is also mousePress, mouseRelease, mouseDrag, and mouseMove, and a couple others. I am going to focus mainly on mouseClick to keep things simple, with the rest following a similar enough pattern.
Example Tests:
These two test sets show implementation of a touch test and a mouse test that test the pressed state, as well as a press and drag off of a touch and mouse.
Component Code:
import QtQuick 2.0
import QtTest 1.2
Rectangle{
width:800
height:800
Background{
id:bg
anchors.fill: parent
}
TestCase{
id:backgroundTests
anchors.fill: parent
name:"TestBackground"
when:windowShown
property color expectedBG:"green"
function test_touched_background(){
let clickRep = findChild(bg,"clickRep");
let mouseArea = findChild(bg,"clickable")
let touch = touchEvent(mouseArea);
touch.press(0,mouseArea)
touch.commit();
compare(clickRep.color,backgroundTests.expectedBG);
touch.release(0);
touch.commit();
}
}
TestCase{
id:backgroundDragTest
when:windowShown
property color expectedBG:"yellow"
function test_touch_release(){
let clickRep = findChild(bg,"clickRep");
let mouseArea = findChild(bg,"clickable")
let touch = touchEvent(mouseArea);
touch.press(0,mouseArea)
touch.commit();
touch.release(0);
touch.commit();
compare(clickRep.color,backgroundDragTest.expectedBG);
}
}
}
Note: It could be better to organize this into seperate test files for each test case.It mostly depends on personal prefernce in my opinion.
Mouse Test Code
import QtQuick 2.0
import QtTest 1.2
Rectangle{
width:800
height:800
Background{
id:bg
anchors.fill: parent
}
TestCase{
id:backgroundTests
anchors.fill: parent
name:"TestBackground"
when:windowShown
property color expectedBG:"yellow"
function test_clicked_background(){
let clickRep = findChild(bg,"clickRep");
let mouseArea = findChild(bg,"clickable")
mouseClick(mouseArea)
compare(clickRep.color,backgroundTests.expectedBG);
}
}
TestCase{
id:backgroundDragTest
when:windowShown
property color expectedBG:"green"
function test_mouse_press(){
let clickRep = findChild(bg,"clickRep");
let mouseArea = findChild(bg,"clickable")
mousePress(mouseArea);
compare(clickRep.color,backgroundDragTest.expectedBG);
mouseRelease(mouseArea);
}
}
}
Additional Notes: Signal Spy
In additional to simply directly comparing component properties, or properties of children it is also possible to tie into and listen for signals that your component emits based on actions. For example if you have a component that emits a clicked signal based on its internal state it would be possible to use a Signal Spy to listen for that signal as part of your tests.
Rectangle{
width:800;
height:800;
//Component that emits a signal
CustomComponent{
id:custom
anchors.fill:parent;
//signal customClicked();
}
SignalSpy{
id:spyder
target:custom
signalName:"customClick"
}
TestCase{
name:"testing";
function test_signal(){
compare(syder.count,0)
let touch = touchEvent(custom);
touch.press(0,custom);
touch.commit();
compare(spyder.count,1);
}
}
}