2019-03-21 09:57:27 +01:00
/* globals Power0 */
/* eslint no-console: ["error", { allow: ["log", "info", "warn", "error"] }] */
/ * *
* Imports
* /
/ * *
* A class that can be used to perform automated user interface tests .
*
* @ example
* // Create the UITest object
* const test = new UITest ( {
* timeScale : 2
* } )
*
* // Add an action to the test case
* test . tap ( button , { eventType : 'click' } )
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* // Start the test case
* test . start ( )
*
* @ class
* @ see { @ link https : //www.iwm-tuebingen.de/iwmbrowser/lib/pixi/uitest.html|DocTest}
* /
export default class UITest {
/ * *
* Creates an instance of an UITest .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* In the background , the class UITest uses the Greensock TimelineMax class . The opts object is passed directly to the TimelineMax class , so it can use any key that uses the TimelineMax class .
*
* @ constructor
* @ param { object } [ opts ] - An options object to specify the behaviour of the test case .
* @ param { number } [ opts . timeScale = 1 ] - The speed at which the test should run , see https : //greensock.com/docs/TimelineMax/timeScale().
* @ param { string } [ opts . eventType = auto ] - The type of events which should be used . Possible values : pointer , touch , mouse , auto . If set to auto , the eventType is set depending on the support of the browser used .
* @ param { boolean } [ opts . debug = false ] - If set to true , multiple informations will be print to the console .
* @ param { number } [ opts . defaultInterval ] - The interval used when no action is specified for an action .
* /
constructor ( opts = { } ) {
2019-07-18 12:26:39 +02:00
this . opts = Object . assign (
{ } ,
{
timeScale : 1 ,
eventType : 'auto' ,
debug : false ,
2022-10-04 10:51:35 +02:00
defaultInterval : null ,
2019-07-18 12:26:39 +02:00
} ,
opts
)
2019-03-21 09:57:27 +01:00
// timeline
//--------------------
2019-07-18 12:26:39 +02:00
this . _timeline = new TimelineMax (
Object . assign (
{ } ,
{
2022-10-04 10:51:35 +02:00
paused : true ,
2019-07-18 12:26:39 +02:00
} ,
this . opts
)
)
2019-03-21 09:57:27 +01:00
this . _timeline . timeScale ( this . opts . timeScale )
// eventType
//--------------------
if ( this . opts . eventType === 'auto' ) {
if ( window . PointerEvent ) {
this . opts . eventType = 'pointer'
} else if ( 'ontouchstart' in window ) {
this . opts . eventType = 'touch'
} else {
this . opts . eventType = 'mouse'
}
}
if ( this . opts . debug ) {
console . log ( ` Event type: ${ this . opts . eventType } ` )
}
this . _timelinePositions = [ 0 ]
this . _actions = 0
// setup
//-----------------
this . setup ( )
}
/ * *
* Generates the required structure .
*
* @ private
* @ return { UITest } A reference to the UITest for chaining .
* /
setup ( ) {
return this
}
/ * *
* Gets the Greensock TimelineMax object , used in the background of UITest .
*
* @ member { TimelineMax }
* /
get timeline ( ) {
return this . _timeline
}
/ * *
* Starts the test case and executes the corresponding statements in the specified order .
*
* @ return { UITest } A reference to the UITest for chaining .
* /
start ( ) {
this . _timeline . play ( )
return this
}
/ * *
* Stops the test case and stops executing any further instructions .
*
* @ return { UITest } A reference to the UITest for chaining .
* /
stop ( ) {
this . _timeline . pause ( )
return this
}
/ * *
* Clears all instructions of the test case .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ return { UITest } A reference to the UITest for chaining .
* /
clear ( ) {
this . _timeline . clear ( )
return this
}
/ * *
* Restarts the test case .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ return { UITest } A reference to the UITest for chaining .
* /
restart ( ) {
this . _timeline . restart ( )
return this
}
/ * *
* Executes a tap event ( pointerdown , pointerup ) on a specific element .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ param { HTMLElement | string } element - The HTML element on which the event is to be executed , e . g . button , document , h2 , canvas , etc . or an selector string . If a selector has been specified , it is evaluated immediately before the event is called using the querySelector method .
* @ param { number [ ] | object | PIXI . DisplayObject } [ position = The center of the element . ] - The local position of the event in the context of the specified HTML element . If no position is specified , the center of the HTML element is used . The position can be specified as an array of numbers , as an object with the two properties x and y , or as a PIXI . Display object .
* @ param { number } [ timelinePosition = One second after the last action . ] - The position in seconds when the event should be triggered , see shttps : //greensock.com/docs/TimelineMax/addCallback().
* @ param { object } [ opts ] - An options object to specify the behaviour of the action .
* @ param { function } [ opts . onStart ] - A function that runs after the first event is fired . Will not be fired if only one event is running ( for example , a click event ) . Receives the fired event object as the first parameter . The test case ( UITest ) is bound to this .
* @ param { function } [ opts . onComplete ] - A function that runs after the second event is fired . Always fired , even if only one event is running ( for example , a click event ) . Receives the fired event object as the first parameter . The test case ( UITest ) is bound to this .
* @ param { string [ ] } [ opts . eventTypes = [ 'pointerdown' , 'pointerup' ] ] - The event types to use . If no types are specified , the event types specified in the UITest constructor are used ( or auto if not specified ) .
* @ param { string } [ opts . eventType ] - If you want the tap method to fire only one event ( for example , a click event ) , you can specify the opts . eventType parameter . If eventType is not null , the parameter opts . eventTypes is ignored .
* @ param { Window | Frame } [ opts . context = window ] - The context within which the optionally specified element selector should be executed .
* @ param { boolean } [ opts . bubbles = true ] - The Event property bubbles indicates whether the event bubbles up through the DOM or not .
* @ param { boolean } [ opts . cancelable = true ] - Events ' cancelable property indicates if the event can be canceled , and therefore prevented as if the event never happened . If the event is not cancelable , then its cancelable property will be false and the event listener cannot stop the event from occurring .
* /
tap ( element , position , timelinePosition , opts = { } ) {
// arguments
//--------------------
2019-07-18 12:26:39 +02:00
; [ position , timelinePosition , opts ] = this . reorderArguments ( arguments )
2019-03-21 09:57:27 +01:00
this . _timelinePositions . push ( timelinePosition )
// debug
//--------------------
2019-07-18 12:26:39 +02:00
if ( this . opts . debug )
console . log ( 'tap params' , {
element ,
position ,
timelinePosition ,
2022-10-04 10:51:35 +02:00
opts ,
2019-07-18 12:26:39 +02:00
} )
2019-03-21 09:57:27 +01:00
// opts
//--------------------
2019-07-18 12:26:39 +02:00
opts = Object . assign (
{ } ,
{
onStart : null ,
onComplete : null ,
eventTypes : this . resolveEvents ( [ 'down' , 'up' ] ) ,
eventType : null ,
context : window ,
bubbles : true ,
2022-10-04 10:51:35 +02:00
cancelable : true ,
2019-07-18 12:26:39 +02:00
} ,
opts
)
2019-03-21 09:57:27 +01:00
if ( opts . eventType ) {
opts . eventTypes = opts . eventType
}
2019-07-30 16:56:29 +02:00
opts . eventTypes = Array . isArray ( opts . eventTypes ) ? opts . eventTypes : [ opts . eventTypes ]
2019-03-21 09:57:27 +01:00
// timeline
//--------------------
2019-07-18 12:26:39 +02:00
this . _timeline . addCallback (
2022-10-04 10:51:35 +02:00
( position ) => {
2019-07-18 12:26:39 +02:00
// element
//--------------------
const elem = Util . extractElement ( opts . context , element )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// position
//--------------------
if ( position === null ) {
const rect = elem . getBoundingClientRect ( )
position = [ rect . width / 2 , rect . height / 2 ]
}
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// coords
//--------------------
const coords = Util . extractPosition ( position )
if ( this . opts . debug ) console . log ( 'local coords' , coords )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// eventTypes
//--------------------
if ( opts . eventTypes . length === 1 ) {
opts . eventTypes . unshift ( null )
}
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// event opts
//--------------------
const eventOpts = {
bubbles : opts . bubbles ,
2022-10-04 10:51:35 +02:00
cancelable : opts . cancelable ,
2019-07-18 12:26:39 +02:00
}
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
if ( opts . eventTypes [ 0 ] ) {
// create and dispatch event
//--------------------
2019-07-30 16:56:29 +02:00
const eventStart = Event . create ( elem , coords , opts . eventTypes [ 0 ] , eventOpts )
if ( this . opts . debug ) console . log ( 'dispatch event' , eventStart )
2019-07-18 12:26:39 +02:00
elem . dispatchEvent ( eventStart )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// onStart
//--------------------
if ( opts . onStart ) {
opts . onStart . call ( this , eventStart )
}
}
2019-03-21 09:57:27 +01:00
// create and dispatch event
//--------------------
2019-07-30 16:56:29 +02:00
const eventComplete = Event . create ( elem , coords , opts . eventTypes [ 1 ] , eventOpts )
if ( this . opts . debug ) console . log ( 'dispatch event' , eventComplete )
2019-07-18 12:26:39 +02:00
elem . dispatchEvent ( eventComplete )
// onComplete
2019-03-21 09:57:27 +01:00
//--------------------
2019-07-18 12:26:39 +02:00
if ( opts . onComplete ) {
opts . onComplete . call ( this , eventComplete )
2019-03-21 09:57:27 +01:00
}
2019-07-18 12:26:39 +02:00
} ,
timelinePosition ,
[ position ]
)
2019-03-21 09:57:27 +01:00
this . _actions ++
return this
}
/ * *
* Executes a pan event ( pointerdown , pointermove , pointerup ) on a specific element .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ param { HTMLElement | string } element - The HTML element on which the event is to be executed , e . g . button , document , h2 , canvas , etc . or an selector string . If a selector has been specified , it is evaluated immediately before the event is called using the querySelector method .
* @ param { number [ ] | object | PIXI . DisplayObject } [ position = The center of the element . ] - The local position of the event in the context of the specified HTML element . If no position is specified , the center of the HTML element is used . The position can be specified as an array of numbers , as an object with the two properties x and y , or as a PIXI . Display object .
* @ param { number } [ timelinePosition = One second after the last action . ] - The position in seconds when the event should be triggered , see shttps : //greensock.com/docs/TimelineMax/addCallback().
* @ param { object } [ opts ] - An options object to specify the behaviour of the action .
* @ param { function } [ opts . onStart ] - A function that runs after the first event is fired . Receives the fired event object as the first parameter . The test case ( UITest ) is bound to this .
* @ param { function } [ opts . onUpdate ] - A function that runs after each execution of the second event . Receives the fired event object as the first parameter . The test case ( UITest ) is bound to this .
* @ param { function } [ opts . onComplete ] - A function that runs after the third event is fired . Receives the fired event object as the first parameter . The test case ( UITest ) is bound to this .
* @ param { number [ ] | object | PIXI . DisplayObject } [ opts . to = { x : 0 , y : 0 } ] - The target of the pan process . The position can be specified as an array of numbers , as an object with the two properties x and y , or as a PIXI . Display object .
* @ param { number } [ opts . duration = 1 ] - The duration of the pan animation in seconds , see https : //greensock.com/docs/TweenLite/duration().
* @ param { Ease } [ opts . ease = Power0 . easeNone ] - The easing of the pan animation , see https : //greensock.com/docs/Easing.
* @ param { string [ ] } [ opts . eventTypes = [ 'pointerdown' , 'pointermove' , 'pointerup' ] ] - The event types to use . If no types are specified , the event types specified in the UITest constructor are used ( or auto if not specified ) .
* @ param { Window | Frame } [ opts . context = window ] - The context within which the optionally specified element selector should be executed .
* @ param { boolean } [ opts . bubbles = true ] - The Event property bubbles indicates whether the event bubbles up through the DOM or not .
* @ param { boolean } [ opts . cancelable = true ] - Events ' cancelable property indicates if the event can be canceled , and therefore prevented as if the event never happened . If the event is not cancelable , then its cancelable property will be false and the event listener cannot stop the event from occurring .
* /
pan ( element , position , timelinePosition , opts = { } ) {
// arguments
//--------------------
2019-07-18 12:26:39 +02:00
; [ position , timelinePosition , opts ] = this . reorderArguments ( arguments )
2019-03-21 09:57:27 +01:00
this . _timelinePositions . push ( timelinePosition )
// debug
//--------------------
2019-07-18 12:26:39 +02:00
if ( this . opts . debug )
console . log ( 'tap params' , {
element ,
position ,
timelinePosition ,
2022-10-04 10:51:35 +02:00
opts ,
2019-07-18 12:26:39 +02:00
} )
2019-03-21 09:57:27 +01:00
// opts
//--------------------
2019-07-18 12:26:39 +02:00
opts = Object . assign (
{ } ,
{
onStart : null ,
onUpdate : null ,
onComplete : null ,
to : { x : 0 , y : 0 } ,
duration : 1 ,
ease : Power0 . easeNone ,
eventTypes : this . resolveEvents ( [ 'down' , 'move' , 'up' ] ) ,
context : window ,
bubbles : true ,
2022-10-04 10:51:35 +02:00
cancelable : true ,
2019-07-18 12:26:39 +02:00
} ,
opts
)
2019-03-21 09:57:27 +01:00
// timeline
//--------------------
2019-07-18 12:26:39 +02:00
this . _timeline . addCallback (
2022-10-04 10:51:35 +02:00
( position ) => {
2019-07-18 12:26:39 +02:00
// element
//--------------------
const elem = Util . extractElement ( opts . context , element )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// coords
//--------------------
const from = Util . extractPosition ( position )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// event opts
//--------------------
const eventOpts = {
bubbles : opts . bubbles ,
2022-10-04 10:51:35 +02:00
cancelable : opts . cancelable ,
2019-07-18 12:26:39 +02:00
}
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
const gsOpts = {
ease : opts . ease ,
onStart : ( ) => {
// create and dispatch event
//--------------------
2019-07-30 16:56:29 +02:00
const event = Event . create ( elem , from , opts . eventTypes [ 0 ] , eventOpts )
if ( this . opts . debug ) console . log ( 'dispatch event' , event )
2019-07-18 12:26:39 +02:00
elem . dispatchEvent ( event )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// onStart
//--------------------
if ( opts . onStart ) {
opts . onStart . call ( this , event )
}
} ,
onUpdate : ( ) => {
// create and dispatch event
//--------------------
2019-07-30 16:56:29 +02:00
const event = Event . create ( elem , from , opts . eventTypes [ 1 ] , eventOpts )
if ( this . opts . debug ) console . log ( 'dispatch event' , event )
2019-07-18 12:26:39 +02:00
elem . dispatchEvent ( event )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// onUpdate
//--------------------
if ( opts . onUpdate ) {
opts . onUpdate . call ( this , event )
}
} ,
onComplete : ( ) => {
// create and dispatch event
//--------------------
2019-07-30 16:56:29 +02:00
const event = Event . create ( elem , from , opts . eventTypes [ 2 ] , eventOpts )
if ( this . opts . debug ) console . log ( 'dispatch event' , event )
2019-07-18 12:26:39 +02:00
elem . dispatchEvent ( event )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// onComplete
//--------------------
if ( opts . onComplete ) {
opts . onComplete . call ( this , event )
}
2022-10-04 10:51:35 +02:00
} ,
2019-03-21 09:57:27 +01:00
}
2019-07-18 12:26:39 +02:00
// to
//--------------------
const object = Util . extractTo ( opts )
Object . assign ( gsOpts , object )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// drag animation
//--------------------
TweenLite . to ( from , opts . duration , gsOpts )
} ,
timelinePosition ,
[ position ]
)
2019-03-21 09:57:27 +01:00
this . _actions ++
return this
}
/ * *
* Executes a pinch event ( pointerdown , pointermove , pointerup ) on a specific element with two "fingers" simultaneously .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ param { HTMLElement | string } element - The HTML element on which the event is to be executed , e . g . button , document , h2 , canvas , etc . or an selector string . If a selector has been specified , it is evaluated immediately before the event is called using the querySelector method .
* @ param { number [ ] | object | PIXI . DisplayObject } [ position = The center of the element . ] - The local position of the event in the context of the specified HTML element . If no position is specified , the center of the HTML element is used . The position can be specified as an array of numbers , as an object with the two properties x and y , or as a PIXI . Display object .
* @ param { number } [ timelinePosition = One second after the last action . ] - The position in seconds when the event should be triggered , see shttps : //greensock.com/docs/TimelineMax/addCallback().
* @ param { object } [ opts ] - An options object to specify the behaviour of the action .
* @ param { function } [ opts . onStart ] - A function that runs after the first events are fired . Receives the fired event object as the first parameter . The test case ( UITest ) is bound to this .
* @ param { function } [ opts . onUpdate ] - A function that runs after each execution of the second events . Receives the fired event object as the first parameter . The test case ( UITest ) is bound to this .
* @ param { function } [ opts . onComplete ] - A function that runs after the third events are fired . Receives the fired event object as the first parameter . The test case ( UITest ) is bound to this .
* @ param { boolean } [ opts . doubleCallbacks = false ] - The callbacks onStart , onUpdate and onComplete will be fired only for one finger . If set to true , the events will be fired for both fingers .
* @ param { number } [ opts . distance = 100 ] - The distance in pixels , how far the two "fingers" should move apart . If to or bezier specified , distance is ignored .
* @ param { number [ ] [ ] | object [ ] | PIXI . DisplayObject [ ] } [ opts . to ] - The targets of the pinch process . The position must be an array with two entries . An entry can be specified as an array of numbers , as an object with the two properties x and y , or as a PIXI . Display object . If bezier is specified , to is ignored .
* @ param { number [ ] [ ] | object [ ] | PIXI . DisplayObject [ ] } [ opts . bezier ] - The targets of the pinch process . The position must be an array with two entries . An entry may be an array of positions or a bezier object ( https : //greensock.com/docs/Plugins/BezierPlugin). A position in the array or the values array of the bezier object can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. If bezier is specified, to is ignored.
* @ param { number } [ opts . duration = 1 ] - The duration of the pan animation in seconds , see https : //greensock.com/docs/TweenLite/duration().
* @ param { Ease } [ opts . ease = Power0 . easeNone ] - The easing of the pan animation , see https : //greensock.com/docs/Easing.
* @ param { string [ ] } [ opts . eventTypes = [ 'pointerdown' , 'pointermove' , 'pointerup' ] ] - The event types to use . If no types are specified , the event types specified in the UITest constructor are used ( or auto if not specified ) .
* @ param { Window | Frame } [ opts . context = window ] - The context within which the optionally specified element selector should be executed .
* @ param { boolean } [ opts . bubbles = true ] - The Event property bubbles indicates whether the event bubbles up through the DOM or not .
* @ param { boolean } [ opts . cancelable = true ] - Events ' cancelable property indicates if the event can be canceled , and therefore prevented as if the event never happened . If the event is not cancelable , then its cancelable property will be false and the event listener cannot stop the event from occurring .
* /
pinch ( element , position , timelinePosition , opts = { } ) {
// arguments
//--------------------
2019-07-18 12:26:39 +02:00
; [ position , timelinePosition , opts ] = this . reorderArguments ( arguments )
2019-03-21 09:57:27 +01:00
this . _timelinePositions . push ( timelinePosition )
// debug
//--------------------
2019-07-18 12:26:39 +02:00
if ( this . opts . debug )
console . log ( 'tap params' , {
element ,
position ,
timelinePosition ,
2022-10-04 10:51:35 +02:00
opts ,
2019-07-18 12:26:39 +02:00
} )
2019-03-21 09:57:27 +01:00
// opts
//--------------------
2019-07-18 12:26:39 +02:00
opts = Object . assign (
{ } ,
{
onStart : null ,
onUpdate : null ,
onComplete : null ,
doubleCallbacks : false ,
duration : 1 ,
distance : 100 ,
to : null ,
bezier : null ,
ease : Power0 . easeNone ,
eventTypes : this . resolveEvents ( [ 'down' , 'move' , 'up' ] ) ,
context : window ,
bubbles : true ,
2022-10-04 10:51:35 +02:00
cancelable : true ,
2019-07-18 12:26:39 +02:00
} ,
opts
)
2019-03-21 09:57:27 +01:00
// timeline
//--------------------
2019-07-18 12:26:39 +02:00
this . _timeline . addCallback (
2022-10-04 10:51:35 +02:00
( position ) => {
2019-07-18 12:26:39 +02:00
// element
2019-03-21 09:57:27 +01:00
//--------------------
2019-07-18 12:26:39 +02:00
const elem = Util . extractElement ( opts . context , element )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// from
2019-03-21 09:57:27 +01:00
//--------------------
2019-07-18 12:26:39 +02:00
let from1 = null
let from2 = null
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
if ( Array . isArray ( position ) && ! Util . isNumber ( position [ 0 ] ) ) {
from1 = Util . extractPosition ( position [ 0 ] )
from2 = Util . extractPosition ( position [ 1 ] )
} else {
from1 = Util . extractPosition ( position )
from2 = { x : from1 . x , y : from1 . y }
}
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// to
//--------------------
let gsOpts1 = { }
let gsOpts2 = { }
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
if ( opts . to || opts . bezier ) {
; [ gsOpts1 , gsOpts2 ] = Util . extractMultiTo ( opts )
} else {
const distance = opts . distance != null ? opts . distance : 100
gsOpts1 . x = from1 . x - distance / 2
gsOpts1 . y = from1 . y
gsOpts2 . x = from2 . x + distance / 2
gsOpts2 . y = from2 . y
}
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// pointers
//--------------------
const pointers = new Map ( )
pointers . set ( 0 , { element : from1 , gsOpts : gsOpts1 } )
pointers . set ( 1 , { element : from2 , gsOpts : gsOpts2 } )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// loop
//--------------------
pointers . forEach ( ( value , key ) => {
// from
//--------------------
const from = value . element
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// event opts
//--------------------
const eventOpts = {
bubbles : opts . bubbles ,
cancelable : opts . cancelable ,
p ointerId : key ,
2022-10-04 10:51:35 +02:00
isPrimary : key === 0 ,
2019-07-18 12:26:39 +02:00
}
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
const gsOpts = {
ease : opts . ease ,
onStart : ( ) => {
// create and dispatch event
//--------------------
2019-07-30 16:56:29 +02:00
const event = Event . create ( elem , from , opts . eventTypes [ 0 ] , eventOpts )
if ( this . opts . debug ) console . log ( 'dispatch event' , event )
2019-07-18 12:26:39 +02:00
elem . dispatchEvent ( event )
// onStart
//--------------------
2019-07-30 16:56:29 +02:00
if ( opts . onStart && ( opts . doubleCallbacks || key === 0 ) ) {
2019-07-18 12:26:39 +02:00
opts . onStart . call ( this , event )
}
} ,
onUpdate : ( ) => {
// create and dispatch event
//--------------------
2019-07-30 16:56:29 +02:00
const event = Event . create ( elem , from , opts . eventTypes [ 1 ] , eventOpts )
if ( this . opts . debug ) console . log ( 'dispatch event' , event )
2019-07-18 12:26:39 +02:00
elem . dispatchEvent ( event )
// onUpdate
//--------------------
2019-07-30 16:56:29 +02:00
if ( opts . onUpdate && ( opts . doubleCallbacks || key === 0 ) ) {
2019-07-18 12:26:39 +02:00
opts . onUpdate . call ( this , event )
}
} ,
onComplete : ( ) => {
// create and dispatch event
//--------------------
2019-07-30 16:56:29 +02:00
const event = Event . create ( elem , from , opts . eventTypes [ 2 ] , eventOpts )
if ( this . opts . debug ) console . log ( 'dispatch event' , event )
2019-07-18 12:26:39 +02:00
elem . dispatchEvent ( event )
// onComplete
//--------------------
2019-07-30 16:56:29 +02:00
if ( opts . onComplete && ( opts . doubleCallbacks || key === 0 ) ) {
2019-07-18 12:26:39 +02:00
opts . onComplete . call ( this , event )
}
2022-10-04 10:51:35 +02:00
} ,
2019-03-21 09:57:27 +01:00
}
2019-07-18 12:26:39 +02:00
// to
//--------------------
Object . assign ( gsOpts , value . gsOpts )
2019-03-21 09:57:27 +01:00
2019-07-18 12:26:39 +02:00
// drag animation
//--------------------
TweenLite . to ( from , opts . duration , gsOpts )
} )
} ,
timelinePosition ,
[ position ]
)
2019-03-21 09:57:27 +01:00
this . _actions ++
return this
}
// /**
// * Adds a tap event to the timeline.
// *
// * @return {UITest} A reference to the uitest for chaining.
// */
// rotate() {
// return this
// }
// /**
// * Adds a tap event to the timeline.
// *
// * @return {UITest} A reference to the uitest for chaining.
// */
// swipe() {
// return this
// }
// /**
// * Adds a tap event to the timeline.
// *
// * @return {UITest} A reference to the uitest for chaining.
// */
// press() {
// return this
// }
// /**
// * Adds a tap event to the timeline.
// *
// * @return {UITest} A reference to the uitest for chaining.
// */
// event() {
// return this
// }
/ * *
* Sorts the parameters so that the second , third , and fourth parameters can be optional ( and possibly slip forward ) .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ private
* @ param { arguments } params - The arguments which were passed to the function .
* @ returns { array } - Returns an array of the position , the timelinePosition and the opts object .
* /
reorderArguments ( params ) {
// first parameter
//--------------------
const element = params [ 0 ]
// other parameter
//--------------------
let position = null
let timelinePosition = null
let opts = null
2019-07-18 12:26:39 +02:00
2019-03-21 09:57:27 +01:00
// second parameter
//--------------------
if ( Util . isNumber ( params [ 1 ] ) ) {
timelinePosition = params [ 1 ]
2019-07-18 12:26:39 +02:00
} else if (
Util . isObject ( params [ 1 ] ) &&
! Util . isPixiDisplayObject ( params [ 1 ] ) &&
( params [ 1 ] . x == null || params [ 1 ] . y == null )
) {
2019-03-21 09:57:27 +01:00
opts = params [ 1 ]
} else if ( params [ 1 ] != null ) {
position = params [ 1 ]
}
// third parameter
//--------------------
if ( Util . isNumber ( params [ 2 ] ) ) {
timelinePosition = params [ 2 ]
} else if ( Util . isObject ( params [ 2 ] ) ) {
opts = params [ 2 ]
}
// fourth parameter
//--------------------
if ( Util . isObject ( params [ 3 ] ) ) {
opts = params [ 3 ]
}
// defaults
//--------------------
if ( position === null ) {
// will later be filled...
}
if ( timelinePosition === null ) {
if ( this . opts . defaultInterval === null && this . _actions > 1 ) {
2019-07-18 12:26:39 +02:00
throw new Error (
'No execution time was specified for this action, and a default interval was not set in the class constructor!'
)
2019-03-21 09:57:27 +01:00
}
2019-07-30 16:56:29 +02:00
timelinePosition = Math . max ( ... this . _timelinePositions ) + ( this . opts . defaultInterval || 1 )
2019-03-21 09:57:27 +01:00
}
if ( opts === null ) {
opts = { }
}
return [ position , timelinePosition , opts ]
}
/ * *
* Converts event type shortcuts to real event names .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ private
* @ param { string [ ] } events - An array of event types .
* /
resolveEvents ( events ) {
const data = [ ]
if ( this . opts . eventType === 'pointer' ) {
2022-10-04 10:51:35 +02:00
events . forEach ( ( it ) => {
2019-03-21 09:57:27 +01:00
if ( it === 'down' ) {
data . push ( 'pointerdown' )
} else if ( it === 'move' ) {
data . push ( 'pointermove' )
} else if ( it === 'up' ) {
data . push ( 'pointerup' )
}
} )
} else if ( this . opts . eventType === 'touch' ) {
2022-10-04 10:51:35 +02:00
events . forEach ( ( it ) => {
2019-03-21 09:57:27 +01:00
if ( it === 'down' ) {
data . push ( 'touchstart' )
} else if ( it === 'move' ) {
data . push ( 'touchmove' )
} else if ( it === 'up' ) {
data . push ( 'touchend' )
}
} )
} else {
2022-10-04 10:51:35 +02:00
events . forEach ( ( it ) => {
2019-03-21 09:57:27 +01:00
if ( it === 'down' ) {
data . push ( 'mousedown' )
} else if ( it === 'move' ) {
data . push ( 'mousemove' )
} else if ( it === 'up' ) {
data . push ( 'mouseup' )
}
} )
}
return data
}
}
/ * *
* Helper class .
*
* @ example
* // Checks if a thing is a number.
* const num = Util . isNumber ( 20 )
*
* @ private
* @ ignore
* @ class
* /
class Util {
/ * *
* Resolves the element from a specific context .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { Window | Frame } context - The context within which the optionally specified element selector should be executed .
* @ return { HTMLElement | string } element - The HTML element on which the event is to be executed , e . g . button , document , h2 , canvas , etc . or an selector string . If a selector has been specified , it is evaluated immediately before the event is called using the querySelector method .
* /
static extractElement ( context , element ) {
2019-07-30 16:56:29 +02:00
const cont = Util . isFrame ( context ) ? context . contentDocument : context . document
const elem = Util . isString ( element ) ? cont . querySelector ( element ) : element
2019-03-21 09:57:27 +01:00
return elem
}
/ * *
* Extracts the position of the second parameter .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { object } object - Something were the coords should be extracted .
* @ return { object } - Returns an object with the keys x and y .
* /
static extractPosition ( object ) {
// event coords
//--------------------
2019-07-18 12:26:39 +02:00
const position = { x : 0 , y : 0 }
2019-03-21 09:57:27 +01:00
// get the position
//--------------------
if ( ! object ) {
position . x = 0
position . y = 0
} else if ( typeof object . getBounds === 'function' ) {
const bounds = object . getBounds ( )
position . x = bounds . x + bounds . width / 2
position . y = bounds . y + bounds . height / 2
} else if ( Array . isArray ( object ) ) {
position . x = object [ 0 ]
position . y = object [ 1 ]
} else if ( object . x != null && object . y != null ) {
position . x = object . x
position . y = object . y
}
return position
}
/ * *
* Extracts the to or bezier key .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { object } opts - An options object where to or bezier should be extracted .
* @ return { object } - Returns an object with the to or bezier keys .
* /
static extractTo ( opts ) {
const object = { }
if ( opts . bezier ) {
let bezier = null
if ( Array . isArray ( opts . bezier ) ) {
bezier = {
2022-10-04 10:51:35 +02:00
values : opts . bezier . map ( ( it ) => Util . extractPosition ( it ) ) ,
type : 'thru' ,
2019-03-21 09:57:27 +01:00
}
} else {
2022-10-04 10:51:35 +02:00
opts . bezier . values = opts . bezier . values . map ( ( it ) => Util . extractPosition ( it ) )
2019-03-21 09:57:27 +01:00
bezier = opts . bezier
}
object . bezier = bezier
} else {
const to = Util . extractPosition ( opts . to )
object . x = to . x
object . y = to . y
}
return object
}
/ * *
* Extracts multiple to or bezier keys .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { object } opts - An options object where to or bezier should be extracted .
* @ return { object [ ] } - Returns an array of objects with the keys x and y .
* /
static extractMultiTo ( opts ) {
const objects = [ ]
if ( opts . bezier ) {
2022-10-04 10:51:35 +02:00
opts . bezier . forEach ( ( it ) => {
2019-03-21 09:57:27 +01:00
let bezier = null
if ( Array . isArray ( it ) ) {
bezier = {
2022-10-04 10:51:35 +02:00
values : it . map ( ( it ) => Util . extractPosition ( it ) ) ,
type : 'thru' ,
2019-03-21 09:57:27 +01:00
}
} else {
2022-10-04 10:51:35 +02:00
it . values = it . values . map ( ( it ) => Util . extractPosition ( it ) )
2019-03-21 09:57:27 +01:00
bezier = it
}
objects . push ( {
2022-10-04 10:51:35 +02:00
bezier ,
2019-03-21 09:57:27 +01:00
} )
} )
} else {
2022-10-04 10:51:35 +02:00
opts . to . forEach ( ( it ) => {
2019-03-21 09:57:27 +01:00
const to = Util . extractPosition ( it )
objects . push ( {
x : to . x ,
2022-10-04 10:51:35 +02:00
y : to . y ,
2019-03-21 09:57:27 +01:00
} )
} )
}
return objects
}
/ * *
* Checks if a thing is a string .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { object } object - The object to test for .
* @ return { boolean } - true if the thing is a string , otherwise false .
* /
static isString ( object ) {
return typeof object === 'string'
}
/ * *
* Checks if a thing is a number .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { object } object - The object to test for .
* @ return { boolean } - true if the thing is a number , otherwise false .
* /
static isNumber ( object ) {
return typeof object === 'number'
}
/ * *
* Checks if a thing is an object .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { object } object - The object to test for .
* @ return { boolean } - true if the thing is an object , otherwise false .
* /
static isObject ( object ) {
return typeof object === 'object' && ! Array . isArray ( object )
}
/ * *
* Checks if a thing is an PIXI . DisplayObject .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { object } object - The object to test for .
* @ return { boolean } - true if the thing is a PIXI . DisplayObject , otherwise false .
* /
static isPixiDisplayObject ( object ) {
2019-07-18 12:26:39 +02:00
return (
typeof object . getBounds === 'function' &&
typeof object . renderWebGL === 'function' &&
typeof object . setTransform === 'function'
)
2019-03-21 09:57:27 +01:00
}
/ * *
* Checks if a thing is a frame .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { object } object - The object to test for .
* @ return { boolean } - true if the thing is a frame , otherwise false .
* /
static isFrame ( object ) {
return object . contentDocument != null
}
}
/ * *
* Event helper class .
*
* @ example
* // Creates an event object.
* const event = Event . create ( h2 , { x : 5 , y : 10 } , 'touchstart' )
*
* @ private
* @ ignore
* @ class
* /
class Event {
/ * *
* Creates an event object .
2019-07-18 12:26:39 +02:00
*
2019-03-21 09:57:27 +01:00
* @ static
* @ param { HTMLElement } target - The element on which the event should be executed .
* @ param { object } position - The local position of the event in relation to the target . The object must have the keys x and y .
* @ param { string } type - The type of the event , see https : //developer.mozilla.org/de/docs/Web/Events
* @ param { object } opts - An options object . Every paramter of the event object can be overridden , see e . g . https : //developer.mozilla.org/de/docs/Web/API/MouseEvent for all the properties.
* /
2019-07-30 16:56:29 +02:00
static create ( target , position = { x : 0 , y : 0 } , type = 'pointerup' , opts = { } ) {
2019-07-18 12:26:39 +02:00
const rect =
2019-07-30 16:56:29 +02:00
typeof target . getBoundingClientRect === 'function' ? target . getBoundingClientRect ( ) : { x : 0 , y : 0 }
2019-03-21 09:57:27 +01:00
// EventInit
const eventOpts = {
bubbles : true ,
cancelable : true ,
2022-10-04 10:51:35 +02:00
composed : false ,
2019-03-21 09:57:27 +01:00
}
// UIEventInit
const uiEventOpts = {
detail : 0 ,
2022-10-04 10:51:35 +02:00
view : window ,
2019-03-21 09:57:27 +01:00
}
// MouseEvent
const mouseEventOpts = {
2019-07-08 15:44:06 +02:00
screenX : window . screenX + ( target . offsetLeft || 0 ) + position . x ,
screenY : window . screenY + ( target . offsetTop || 0 ) + position . y ,
2019-03-21 09:57:27 +01:00
clientX : rect . x + position . x ,
clientY : rect . y + position . y ,
ctrlKey : false ,
shiftKey : false ,
altKey : false ,
metaKey : false ,
button : 0 ,
buttons : 1 ,
relatedTarget : null ,
2022-10-04 10:51:35 +02:00
region : null ,
2019-03-21 09:57:27 +01:00
}
// TouchEvent
const touchEventOpts = {
touches : [ ] ,
targetTouches : [ ] ,
changedTouches : [ ] ,
ctrlKey : false ,
shiftKey : false ,
altKey : false ,
2022-10-04 10:51:35 +02:00
metaKey : false ,
2019-03-21 09:57:27 +01:00
}
// PointerEvent
const pointerEventOpts = {
pointerId : 0 ,
width : 1 ,
height : 1 ,
pressure : 0 ,
tangentialPressure : 0 ,
tiltX : 0 ,
tiltY : 0 ,
twist : 0 ,
pointerType : 'touch' ,
2022-10-04 10:51:35 +02:00
isPrimary : true ,
2019-03-21 09:57:27 +01:00
}
if ( type . startsWith ( 'pointer' ) ) {
2019-07-18 12:26:39 +02:00
return new PointerEvent (
type ,
2019-07-30 16:56:29 +02:00
Object . assign ( { } , eventOpts , uiEventOpts , mouseEventOpts , pointerEventOpts , opts )
2019-07-18 12:26:39 +02:00
)
2019-03-21 09:57:27 +01:00
} else if ( type . startsWith ( 'touch' ) ) {
2019-07-30 16:56:29 +02:00
return new TouchEvent ( type , Object . assign ( { } , eventOpts , uiEventOpts , touchEventOpts , opts ) )
2019-03-21 09:57:27 +01:00
} else {
2019-07-30 16:56:29 +02:00
return new MouseEvent ( type , Object . assign ( { } , eventOpts , uiEventOpts , mouseEventOpts , opts ) )
2019-03-21 09:57:27 +01:00
}
}
}