[Freeipa-devel] QUnit and unit testing

Adam Young ayoung at redhat.com
Mon Sep 13 19:44:42 UTC 2010


On 09/13/2010 02:44 PM, Endi Sukma Dewata wrote:
> ----- "Adam Young"<ayoung at redhat.com>  wrote:
>    
>>> IPA class:
>>> - IPA(path, type): Construct IPA object given a path to server's JSON-RPC or
>>>                     static files.
>>> - success(): Default success handler.
>>> - error(): Default error handler.
>>> - failure(): Default failure handler.
>>> - execute(methodName, params, options, [success], [error], [failure]):
>>>     Execute a method with optional handlers. The methodName should include
>>>     the object name.
>>>        
>    
>>> For the Web UI we could create a global IPA object that points to JSON-RPC
>>> or sample data based on the current URL. The IPA object can be reused, each
>>> entity only needs to invoke the execute() method and provides necessary
>>> handlers.
>>>        
> This class could still be used in addition to your class below to provide
> global settings for the entire application or test suite. For example we can
> implement a default handler for insufficient access error.
>
>    
>> execute has a lot of parameters.  I think I would prefer a simple
>>       execute(args, options,caller)
>> It could then be called
>>       cmd.execute(args, options,this)
>>
>> The constructor can take a spec like this
>>       var cmd =  command({entity: e, method:m, succes:s , error: e,
>> failure f});
>>
>> and then we can check for the presence of each of the parameters.
>>
>> The inline  case would be
>>       command({entity: e, method:m, succes:s , error: e, failure
>> f}).execute(args, options);
>>      
> I guess this class probably can be called IPACommand or something like
> that. This class could be instantiated by invoking createCommand()
> in the above IPA class. I'd ask though that the entity&  method be
> combined into a single parameter. This is just in case in the future
> we will have a command that doesn't have an associated entity.
>    
We'll keep the current logic which is that entity is only appended to 
the method if it is set, otherwise, use method as is.


There are no classes in JavaScript, just objects.  Either we create it 
through
    new Command(){...}

or using the functional approach     var addUserComand = command(){  
that = {};  ...; return that ;}

There is a lot to recommend the second approach.


>    
>> caller would be an object that respects the contract
>> {
>>       on _error = function(){...}
>>       on_success = function(){...}
>>       on_failure= function(){...}
>> }
>>
>> With each of these functions being optional.  If set, they get called
>> in place of the Command members.
>>      
> I'm not sure about this, there's a potential of conflict if the caller
> wants to use several different IPA commands.
>    
Should not be too hard in the case where one caller wants to have 
multiple commands to write a simple adapter that calls the appropriate 
methods on the caller, or to split the caller along appropriate lines.

>    
>> The prototype for command should have a default implementation for
>> error, success, and failure.  New instance should be created using a
>> clone, with the default as the prototype.
>>      
> I'd rather override the default function with a custom one as needed.
>
> So in the application initialization we could do this:
> var ipa;
> if (window.location.protocol == "file:") {
>    ipa = new IPA("sampledata", IPA_JSON_FOLDER);
> } else {
>    ipa = new IPA("/ipa/json", IPA_JSON_RPC);
> }
> ipa.error = function() {
>    if (insufficient access) {
>      do something
>    }
> }
>
> In the entity initialization we could do this:
> var addCmd = ipa.createCommand("user-add");
> addCmd.success = function() {...};
> addCmd.error = function() {...};
>    

There is a slightly better way to do this.

A trick from Javascript: the good parts:

Object.beget = function(o){
     ar F = function(){
     F.prototype = o;
     return new F();
};


Bascially a clone function, which set up the prototype, and thus the 
default behavior.  Then, we have a Default command object:


IPA.command = function(){
     var that = {};

     execute(args, params){
        //pretty much the same logic we have now;
     }

     function set_error(on_error){
          this.error = error;
          return this;
     }

     function set_success(on_success){
          this.success = on_success;
          return this;
     }

     function set_failure(on_fail){
          this.success = on_fail;
          return this;
     }

     function clone(){

          return Object.beget(that);
     }

     //THis is the public interface
     that.execute = execute;
     that.set_failure = set_error;
     that.set_success = set_error;
     that.set_error= set_error;

     return that;
}

TO make new command that over rides just the success method:

IPA.command.clone().
     set_success(funcion(){...});

Over ride sucess and failure:

IPA.command.clone().
     set_success(function(){...}).
     set_failure(function(){...});





> In the button handler we could do this:
> addCmd.execute(params, options);
>
> Just my $.02. Thanks.
>
> --
> Endi S. Dewata
>    




More information about the Freeipa-devel mailing list