Surfez sur le web quelques heures, et vous trouverez sûrement un site ou une application qui contient un bogue dans son interface graphique. Que ce soit un bouton qui ne fonctionne pas, un rectangle partiellement caché ou mal positionné, il semble que les problèmes d’interface soient notoirement difficiles à traquer! Des technologies intelligentes pourraient-elles aider les concepteurs et les développeurs à mieux tester leurs interfaces? Au Laboratoire d’informatique formelle de l’Université du Québec à Chicoutimi, nous le croyons. Nous avons développé Cornipickle, un logiciel permettant à un développeur d’énoncer, dans un langage simple et lisible, une foule de contraintes sur le positionnement et le contenu des éléments d’une interface web. Nous verrons comment Cornipickle peut s’intégrer à une application existante, détecter des problèmes, identifier les éléments qui sont fautifs et même suggérer des correctifs. Ceci permet donc à un développeur de gagner beaucoup de temps lors de la recherche des problèmes.
Runtime monitoring de propriétés temporelles par (streaming) XML
Technologies intelligentes d'aide au développement d'applications web (WAQ 2018)
1. 12 avril 2018
Technologies intelligentes
d'aide au développement
technique d'applications web
Sylvain Hallé
Université du Québec à Chicoutimi
CRSNG
NSERC
2. 12 avril 2018
Qui suis-je?
Professeur-chercheur au
Département d'informatique et
de mathématique, Université du
Québec à Chicoutimi
Titulaire de la Chaire de recherche
du Canada en spécification, test
et vérification de systèmes
informatiques ($$$)
La personne dans
cette photo ;-)
3. 12 avril 2018
$$
60 étudiants depuis 2010
prix
Best Paper
Award
en subventions de recherche (2010-2020)
4 3professeurs-
chercheurs
100+
publications
scientifiques
1 500 000
$
6. 12 avril 2018
+
?
Quelques projets du LIF...
Librairie de event
stream processing
https://liflab.github.io/beepbeep-3
Utile pour tests en temps
réel, analyse de logs,
détection d'intrusions, ...
7. 12 avril 2018
Quelques projets du LIF...
Détection de bugs dans les
jeux vidéo
Monitoring d'appareils
électriques
8. 12 avril 2018
Quelques projets du LIF...
Librairie d'automatisation
d'expériences informatiques
https://liflab.github.io/labpal
Fonctions avancées de traçabilité
LabLabPalPal
À paraître dans IEEE Computer!
9. 12 avril 2018
Quelques projets du LIF...
LabLabPalPal
Cell (0,2) in Table #3
The sum of column 2 in Table #2
Cell (0,2) in Table #2
The value of
Cell (1,1) in Table #1
Value of time in Experiment #2
Cell (1,2) in Table #1
Value of name in Experiment #2
Cell (1,2) in Table #2
The value of
Cell (5,1) in Table #1
Value of time in Experiment #6
Cell (5,2) in Table #1
Value of name in Experiment #6
Cell (2,2) in Table #2
The value of
Cell (9,1) in Table #1
Value of time in Experiment #10
Cell (9,2) in Table #1
Value of name in Experiment #10
10. 12 avril 2018
Quelques projets du LIF...
Système de traçabilité
sécurisé
https://github.com/artichoke-x
Basé sur une variante des
blockchains
ARTICHOKE
$
DOCTOR
PATIENT
INSURANCE
COMPANY
PHARMACIST
NURSE
DOCUMENT
Best Paper Award en 2016
11. 12 avril 2018
Quelques projets du LIF...
https://liflab.github.io/sealtest
3
4
1
a [n<2]
/ n=n+1
c
a [n>1] 5
6 7
a n=0
b
2
c c
d [n=0] d [n>0]
d d
8
G (a → (X (b ∨ c)))
Générateur de
séquences de test
Notre plus récent projet
(en développement)
28. 12 avril 2018
Mojibake est un emprunt lexical du
japonais qui signifie que les caractères
affichés à l'écran d'un logiciel informatique
ne s'affichent pas correctement, à cause
d'un problème de codage. (Wikipedia)
«
»
Pièce à conviction #4
Mojibake!
69. 12 avril 2018
Une partie de l'explication
Les navigateurs ne font pas le même rendu du même
code HTML/CSS (cross-browser layout problems)
(C'est la faute aux navigateurs!)
70. 12 avril 2018
ACID2 Test: la même page dans deux (vieux) navigateurs
Safari (succès)
Internet Explorer (échec)
71. 12 avril 2018
Une minute...
Presque tous les bugs que nous
avons vus sont présents dans tous
les navigateurs...
...alors est-ce uniquement
de leur faute?
72. 12 avril 2018
Une autre partie de l'explication
CSS est complexe!
Difficile de savoir si les propriétés réelles d'un élément
correspondent à une règle CSS ("suggestion")
73. 12 avril 2018
Une autre partie de l'explication
CSS est complexe!
Difficile de savoir si les propriétés réelles d'un élément
correspondent à une règle CSS ("suggestion")
#ext {
width: 200px;
}
.foo {
width: 150px;
}
<div id="ext">
<div class="foo">A</div>
<div class="foo">B</div>
</div>
74. 12 avril 2018
Une autre partie de l'explication
CSS est complexe!
Difficile de savoir si les propriétés réelles d'un élément
correspondent à une règle CSS ("suggestion")
#ext {
width: 200px;
}
.foo {
width: 150px;
}
<div id="ext">
<div class="foo">A</div>
<div class="foo">B</div>
</div>
#ext
.foo .foo
300px
77. 12 avril 2018
Une autre partie
de l'autre partie de l'explication
Pourcentage des applications web qui n'ont
pas de suite de tests (source: ICST 2015)
86%
78. 12 avril 2018
Les bugs liés à l'affichage peuvent révéler des problèmes
de design, mais aussi de fonctionnalité d'une application
Ils sont fréquents
Ils ne sont pas forcément causés par le navigateur
CSS peut donner des "suggestions",
mais pas imposer des contraintes
Conclusion: la détection de ces bugs
doit se faire par un moyen externe
à HTML/CSS
79. 12 avril 2018
Solution #1
Observation "à l'oeil nu" par un testeur
M. TEST
Beaucoup de choses à vérifier
(lent)
Basé sur la compréhension
du testeur
C'est bizarre
ici!
80. 12 avril 2018
Solution #2
Programmer la vérification des contraintes sur
l'interface avec un langage "traditionnel"
82. 12 avril 2018
...et plusieurs autres!
Si jQuery ne fait pas notre affaire, il existe d'autres
solutions:
83. 12 avril 2018
evaluate = function() {
if (!evaluate.positions) {
evaluate.positions = [];
evaluate.positionindex = 0;
$("li").each(function() {
evaluate.positions.push({
"top" : $(this).offsetTop,
"left" : $(this).offsetLeft
});
$(this).uniqueid = evaluate.positionindex;
evaluate.positionindex++;
});
}
$("li").each(function(index) {
var uniqueid = $(this).uniqueid;
if (uniqueid && (
evaluate.positions[uniqueid].top
!= $(this).offsetTop ||
evaluate.positions[uniqueid].left
!= $(this).left)
)
console.log("Error");
});
};
Quelques problèmes...
Expliquez ce que
fait ce bout de code
(en 5 secondes!)
84. 12 avril 2018
evaluate = function() {
if (!evaluate.positions) {
evaluate.positions = [];
evaluate.positionindex = 0;
$("li").each(function() {
evaluate.positions.push({
"top" : $(this).offsetTop,
"left" : $(this).offsetLeft
});
$(this).uniqueid = evaluate.positionindex;
evaluate.positionindex++;
});
}
$("li").each(function(index) {
var uniqueid = $(this).uniqueid;
if (uniqueid && (
evaluate.positions[uniqueid].top
!= $(this).offsetTop ||
evaluate.positions[uniqueid].left
!= $(this).left)
)
console.log("Error");
});
};
Quelques problèmes...
Expliquez ce que
fait ce bout de code
(en 5 secondes!)
Retourne juste
"Error" ?!?
Si on veut plus d'info,
on doit écrire plus de
code....
85. 12 avril 2018
Solution #3
Approche intelligente!
On écrit des énoncés
lisibles qui expriment des
contraintes que l'interface
doit suivre
La machine s'occupe de
trouver comment les vérifier
≠ IA
94. 12 avril 2018
Un langage pour exprimer des contraintes de haut
niveau sur des interfaces web (ou autres)
- DOM + attributs CSS
- Quantification du premier ordre
- Opérateurs temporels
1.
Notre solution:
95. 12 avril 2018
Un architecture pour intstrumenter une application
web et rapporter les attributs pertinents à un
interpréteur
2.
Notre solution:
96. 12 avril 2018
Un mécanisme pour vérifier automatiquement les
propriétés et donner une explication si une
contrainte est violée
3.
Notre solution:
97. 12 avril 2018
BEST
TOOL
Hallé, Bergeron, Guérin, Le Breton. Testing
Web Applications Through Layout
Constraints. Proc ICST 2015.
https://github.com/liflab/cornipickle
Notre solution:
102. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
103. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
104. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
105. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
106. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
107. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
108. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
Probe
interaction
Probe status report
9
109. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
Probe
interaction
Probe status report
9
{{tagname:"div",cornipickleid:1,props:
{left:0,right:10,color:red,width:40},children:
[{tagname:"span",cornipickleid:21,props:
{left:20,right:145,color:black,width:120},children:[]},
{tagname:"p",cornipickleid:14,props:
{left:0,right:10,color:red,width:40},children:[]}]}}
...
110. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
Probe
interaction
Probe status report
9
{{tagname:"div",cornipickleid:1,props:
{left:0,right:10,color:red,width:40},children:
[{tagname:"span",cornipickleid:21,props:
{left:20,right:145,color:black,width:120},children:[]},
{tagname:"p",cornipickleid:14,props:
{left:0,right:10,color:red,width:40},children:[]}]}}
...
Probe log
10
111. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
Probe
interaction
Probe status report
9
{{tagname:"div",cornipickleid:1,props:
{left:0,right:10,color:red,width:40},children:
[{tagname:"span",cornipickleid:21,props:
{left:20,right:145,color:black,width:120},children:[]},
{tagname:"p",cornipickleid:14,props:
{left:0,right:10,color:red,width:40},children:[]}]}}
...
Probe log
10
11
112. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
Probe
interaction
Probe status report
9
{{tagname:"div",cornipickleid:1,props:
{left:0,right:10,color:red,width:40},children:
[{tagname:"span",cornipickleid:21,props:
{left:20,right:145,color:black,width:120},children:[]},
{tagname:"p",cornipickleid:14,props:
{left:0,right:10,color:red,width:40},children:[]}]}}
...
Probe log
10
11
Probe instructions (optional) 12
113. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
Probe
interaction
Probe status report
9
{{tagname:"div",cornipickleid:1,props:
{left:0,right:10,color:red,width:40},children:
[{tagname:"span",cornipickleid:21,props:
{left:20,right:145,color:black,width:120},children:[]},
{tagname:"p",cornipickleid:14,props:
{left:0,right:10,color:red,width:40},children:[]}]}}
...
Probe log
10
11
Probe instructions (optional) 12
Dashboard
(analytics)
Developer
13
114. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
Probe
interaction
Probe status report
9
{{tagname:"div",cornipickleid:1,props:
{left:0,right:10,color:red,width:40},children:
[{tagname:"span",cornipickleid:21,props:
{left:20,right:145,color:black,width:120},children:[]},
{tagname:"p",cornipickleid:14,props:
{left:0,right:10,color:red,width:40},children:[]}]}}
...
Probe log
10
11
Probe instructions (optional) 12
Dashboard
(analytics)
Developer
13
14
115. 12 avril 2018
Application under test
Server
Probe
creation
Developer
1
Probe repository
2
Probe ID
3
<script ...
"getprobe?uid=39f2ac8">
4
Request for probe code
5
6
7
Probe code
Probe (JS object)
Probe
retrieval
8
{if("INPUT"===e.tagName||"BUTTON"===e.tagName)
return"range"===e.type||"number"==e.type?
e.valueAsNumber:e.value},Cornipickle.CornipickleProbe.re
freshDelay=500,Cornipickle.CornipickleProbe.elementCount
er=0,Cornipickle.CornipickleProbe.clickThrough
=!1,Cornipickle.CornipickleProbe.toggleClickThrough=
function(){return
Cornipickle.CornipickleProbe.clickThrough
!=Cornipickle.CornipickleProbe.clickThrough,
...
~12 ko
Probe
interaction
Probe status report
9
{{tagname:"div",cornipickleid:1,props:
{left:0,right:10,color:red,width:40},children:
[{tagname:"span",cornipickleid:21,props:
{left:20,right:145,color:black,width:120},children:[]},
{tagname:"p",cornipickleid:14,props:
{left:0,right:10,color:red,width:40},children:[]}]}}
...
Probe log
10
11
Probe instructions (optional) 12
Dashboard
(analytics)
Developer
13
14
16
15
116. 12 avril 2018
$ java -jar cornipickle.jar --port 12345 spec.cp
Pour un usage local:
Fichier texte contenant les
contraintes à monitorer
Port sur lequel écoute
l'interpréteur Cornipickle
Dans l'en-tête de l'application
à tester, ajouter:
<script src="http://localhost:12345/probe" />
C'est tout!
120. 12 avril 2018
For each $x in $(css-selector)
something about $x.
There exists $x in $(css-selector)
such that
something about $x.
121. 12 avril 2018
$x's prop is greater than
$y's prop
$x's height
$x's text
$x's color
$x's prop matches "a string"
...et la plupart des attributs
CSS et DOM
...et la plupart des
fonctions usuelles
123. 12 avril 2018
For each $x in $(.item)
$x's color equals "red".
<ul>
<li class="item">foo</li>
<li class="item">bar</li>
</ul>
.item {
color: red;
}
124. 12 avril 2018
VRAIFor each $x in $(.item)
$x's color equals "red".
<ul>
<li class="item">foo</li>
<li class="item">bar</li>
</ul>
.item {
color: red;
}
125. 12 avril 2018
For each $x in $(.item)
$x's color equals "red".
<ul>
<li class="item">foo</li>
<li class="item">bar</li>
</ul>
.item {
color: red;
}
.item:first-child {
color: yellow;
}
126. 12 avril 2018
FAUXFor each $x in $(.item)
$x's color equals "red".
<ul>
<li class="item">foo</li>
<li class="item">bar</li>
</ul>
.item {
color: red;
}
.item:first-child {
color: yellow;
}
127. 12 avril 2018
For each $x in $(.item)
$x's color equals "red".
<ul>
<li class="item">foo</li>
<li class="item"
style="color:blue">bar</li>
</ul>
.item {
color: red;
}
128. 12 avril 2018
FAUXFor each $x in $(.item)
$x's color equals "red".
<ul>
<li class="item">foo</li>
<li class="item"
style="color:blue">bar</li>
</ul>
.item {
color: red;
}
129. 12 avril 2018
There exists $x in $(li)
such that
$x's text equals "foo".
<ul>
<li class="item">foo</li>
<li class="item">bar</li>
</ul>
.item {
color: red;
}
130. 12 avril 2018
VRAIThere exists $x in $(li)
such that
$x's text equals "foo".
<ul>
<li class="item">foo</li>
<li class="item">bar</li>
</ul>
.item {
color: red;
}
131. 12 avril 2018
There exists $x in $(li)
such that
$x's text equals "foo".
<ul>
<li class="item">foo</li>
<li class="item">bar</li>
</ul>
$().ready(function() {
$(".item").text("");
}
132. 12 avril 2018
FAUXThere exists $x in $(li)
such that
$x's text equals "foo".
<ul>
<li class="item">foo</li>
<li class="item">bar</li>
</ul>
$().ready(function() {
$(".item").text("");
}
133. 12 avril 2018
For each $x in $(#menu li) (
For each $y in $(#menu li) (
$x’s left equals $y’s left)).
A list item
Another list item
A third list item
The last list item
134. 12 avril 2018
FAUXFor each $x in $(#menu li) (
For each $y in $(#menu li) (
$x’s left equals $y’s left)).
A list item
Another list item
A third list item
The last list item
135. 12 avril 2018
We say that pattern
When something.
Exemple:
We say that $x is wider than $y
When
$x's width is greater
than $y's width.
On peut définir soi-même des prédicats (genres de
"macros") pour simplifier l'écriture.
136. 12 avril 2018
We say that $x and $y are left-
aligned when
$x’s left equals $y’s left.
For each $x in $(#menu li) (
For each $y in $(#menu li) (
$x and $y are left-aligned)).
A list item
Another list item
A third list item
The last list item
137. 12 avril 2018
... $(ul li) ...
... 's width ...
... $(p#abc) ...
... 's color ...
La sonde JavaScript est sélective:
elle ne renvoie que les attributs mentionnés dans
une contrainte
seulement pour les éléments qui correspondent à
un des sélecteurs
138. 12 avril 2018
{
cornipickleid: 3,
x: 300,
y: 250,
event: "click"
...
Si un clic se produit dans la page, l'élément concerné
possède un attribut spécial event dans le message
renvoyé par la sonde:
On peut mentionner cet attribut dans une expression
comme toute autre propriété CSS.
144. 12 avril 2018
Voyons le logiciel en action sur quelques exemples
simples...
https://www.youtu.be/90YitGRRx2w
145. 12 avril 2018
Identifier les éléments responsables d'une violation ne
nécessite aucun code
Ce n'est qu'un sous-produit de l'évaluation des
expressions
146. 12 avril 2018
Il est possible de parler de l'état de l'interface à
différents moments dans le temps au moyen
d'opérateurs temporels.
147. 12 avril 2018
Always something
L'expression something doit toujours être
vraie à partir de maintenant.
maintenant
something
Il est possible de parler de l'état de l'interface à
différents moments dans le temps au moyen
d'opérateurs temporels.
148. 12 avril 2018
Il est possible de parler de l'état de l'interface à
différents moments dans le temps au moyen
d'opérateurs temporels.
Eventually something
Il est possible de parler de l'état de l'interface à
différents moments dans le temps au moyen
d'opérateurs temporels.
L'expression something doit être vraie
au moins une fois dans le futur.
maintenant
something
149. 12 avril 2018
Il est possible de parler de l'état de l'interface à
différents moments dans le temps au moyen
d'opérateurs temporels.
Next something
Il est possible de parler de l'état de l'interface à
différents moments dans le temps au moyen
d'opérateurs temporels.
L'expression something doit être vraie
dans le prochain instantané.
maintenant
something
151. 12 avril 2018
Les opérateurs temporels permettent de décrire une
séquence de pages et leur contenu
Ouvre la porte à tester le comportement d'une
application
Faire la même chose avec du code
procédural est plus compliqué (variables
temporaires, conditions supplémentaires)
Bonus: le mécanisme de feedback
intelligent fonctionne toujours!
152. 12 avril 2018
corni
pickle
We say that $x is immobile when (
Always (
When $x is now $y (
($x's left equals $y's left)
And
($x's top equals $y's top)
)
)
).
For each $item in $(li) (
$item is immobile
).
evaluate = function() {
if (!evaluate.positions) {
evaluate.positions = [];
evaluate.positionindex = 0;
$("li").each(function() {
evaluate.positions.push({
"top" : $(this).offsetTop,
"left" : $(this).offsetLeft
});
$(this).uniqueid = evaluate.positionindex;
evaluate.positionindex++;
});
}
$("li").each(function(index) {
var uniqueid = $(this).uniqueid;
if (uniqueid && (
evaluate.positions[uniqueid].top
!= $(this).offsetTop ||
evaluate.positions[uniqueid].left
!= $(this).left)
)
console.log("Error");
});
};
153. 12 avril 2018
corni
pickle
We say that I click on Go when (
There exists $b in $(button) such that (
($b's text equals "Go")
And
($b's event equals "mouseup")
)
).
Always (
If (I click on Go)
Then (
There exists $x in $(.value) such that (
The next time (I click on Go)
Then (
There exists $y in $(.value) such that (
$x's text equals $y's text
)
)
)
)
).
click_on_go = function(event) {
return event.type === 'mouseup' &&
$(event.target).text() === "Go";
}
evaluate = function(event) {
if (click_on_go(event)) {
var current_values = [];
$(".value").each(
evaluate.lastValues.push($(this).text());
);
if (evaluate.lastValues !== undefined) {
var found = false;
for (var v in current_values) {
if ($.inArray(v, evaluate.lastValues))
found = true;
break;
}
}
}
if (!found)
console.log("Error");
evaluate.lastValues = current_values;
}
};
154. 12 avril 2018
"La plupart des navigateurs"
{ {Part de marché
(desktop)
Part de marché
(non-desktop)
75%
25%
155. 12 avril 2018
Dans les bugs d'interface, le navigateur est parfois
important
On ne devrait pas prendre des décisions de design qui
nous limitent dès le début (Selenium, plugin spécifique)
Si votre app/site tourne dans le navigateur X, votre outil
de test le devrait lui aussi!
156. 12 avril 2018
For each $x in $(selector)
blabla...
There exists $y in $(selector)
blabla...
Les sélecteurs CSS sont fragiles. Des changements
simples à la mise en page peuvent "endommager" vos
contraintes.
157. 12 avril 2018
Amélioration du feedback
intelligent: suggestions de
corrections
Extension à WebDriver
Passerelle avec outils
d'intégration continue (Jenkins,
Travis)
161. 12 avril 2018
✓ ✘
The Bottom Navigation can accommodate between 3 and 5
destinations.
162. 12 avril 2018
✓ ✘
The floatting button should not be hidden by other components.
163. 12 avril 2018
✓ ✘
The application should report by a color change if the number of
characters allowed is exceeded.
164. 12 avril 2018
L'interpréteur local de Cornipickle est open source et
gratuit
Nous pouvons personnaliser et étendre Cornipickle via
des collaborations
Projet d'intégration avec Eckinox Média (Alma) en cours
(janvier-juillet 2017) sous financement de MITACS