    /*

    Siesta 5.3.0
    Copyright(c) 2009-2019 Bryntum AB
    https://bryntum.com/contact
    https://bryntum.com/products/siesta/license

    */
    Singleton('Siesta.Test.ActionRegistry', {

        has : {
            actionClasses       : Joose.I.Object
        },

        methods : {

            registerAction : function (name, constructor) {
                this.actionClasses[ name.toLowerCase() ] = constructor
            },

            getActionClass : function (name) {
                return this.actionClasses[ name.toLowerCase() ]
            },

            create : function (obj, test, defaultArgs, initStep) {
                if (obj !== Object(obj)) throw "Action configuration should be an Object instance"

                obj     = Joose.O.copy(obj)

                if (!obj.action) {
                    var actionClasses       = this.actionClasses
                    var methods             = {}

                    if (test) {
                        methods             = test.getActionableMethods()
                    }

                    Joose.O.eachOwn(obj, function (value, key) {
                        var shortcut        = key.toLowerCase()

                        if (actionClasses[ shortcut ]) {
                            obj.action      = shortcut

                            switch (shortcut) {
                                case 'setvalue' :
                                case 'waitfor'  :
                                // do nothing
                                break

                                case 'type'     :
                                    obj.text        = value
                                break

                                default         :
                                    obj.target      = value
                            }

                            return false
                        } else if (methods[ shortcut ]) {
                            if (shortcut.match(/^waitFor/i)) {
                                obj.action      = 'wait'
                                obj.waitFor     = methods[ shortcut ]
                                obj.args        = value || []
                            } else {
                                obj.action      = 'methodCall'
                                obj.methodName  = methods[ shortcut ]
                                obj.args        = value || []
                            }

                            return false
                        }
                    })
                }

                if (!obj.action) throw "Need to include `action` property or shortcut property in the step config: " + Siesta.Util.Serializer.stringify(obj, 2)

                // Don't get the arguments from the previous step if it is a waitFor action,
                // it does not make sense and messes up the arguments
                if (obj.action != 'wait' && obj.action != 'waitfor' && obj.action != 'delay' && obj.action != 'methodCall') {
                    if (!obj.args && defaultArgs) obj.args = defaultArgs
                }

                var actionClass = this.getActionClass(obj.action)

                // if there's `initStep` function - overwrite the "next" function anyway
                if (!obj.next || initStep) obj.next     = initStep(actionClass.prototype.hasOwnAsyncFrame).next
                if (!obj.test || test) obj.test     = test

                return new actionClass(obj)
            }
        }
    });