Make Watir Support Jquery event

I have been in use watir(1.8.0) and ruby(1.8.7) for Regression test recently. I've got a problem yesterday. Look the demonstration page below:

<html>
	<head>
	<script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script>
	</head>
	<body>
		<select id ="s1">
			<option value="1">123</option>
			<option value="2">456</option>
			<option value="3">789</option>
		</select>
		<script>
			
			$('#s1').change(function(){
				alert('fired');
			});

			
		</script>
	</body>
</html>

when I use the code below to hope fire the change event of select element,but it can't work.

require "watir"
@browser = Watir::Browser.new
@browser.goto('a.html')
@browser.select_list(:id, 's1').set('456')

I deep in to the source code of watir(1.8.0),and found the code that:

def select_item_in_select_list(attribute, value) #:nodoc:
      assert_exists
      highlight(:set)
      found = false

      value = value.to_s unless [Regexp, String].any? { |e| value.kind_of? e }

      @container.log "Setting box #{@o.name} to #{attribute.inspect} => #{value.inspect}"
      @o.each do |option| # items in the list
        if value.matches(option.invoke(attribute.to_s))
          if option.selected
            found = true
            break
          else
            option.selected = true
            @o.fireEvent("onChange")
            @container.wait
            found = true
            break
          end
        end
      end

      unless found
        raise NoValueFoundException, "No option with #{attribute.inspect} of #{value.inspect} in this select element"
      end
      highlight(:clear)
    end

As the code show,Watir calling API of win32ole object,and raise the native event  "onChange".but this couldn't fire "change" event which jquery support. It's so tricky! 

After a research in google.i got this answer from StackOverFlow.

@browser = Watir::Browser.new
 @browser.goto("http://someurl")
 @browser.select_list(:id, element_id).select(item_to_select)
 @browser.ie.Document.parentWindow.execScript('$("##{element_id}").change();')

it's really cool method and seems resolved my problem(Let's say WATIR SUCKS loudly ). I couldn't use this directly because this is just a demonstration.In production environment,there are a lot of same invoke.To avoid duplication code,i have to extend watir to support our requirement.

......................
............ 
option.selected = true
@o.fireEvent("onChange")
script = %Q{$("##{self.id}").change();}
@o.Document.parentWindow.execScript(script)
@container.wait
found = true
break
.............
................

It works well now!

Posted by Leo 2011年9月28日 02:18


Let's refactor it-OpenSinaAPI

这两天一直在看Clean Code以及 重构,一直想找一些例子练练手。恰巧前两天在玩新浪微博应用的时候看到了陈着写的这个SDK。down下来代码后发现这里面还有很多可以重构的地方,于是花了一个周末实践了一下。

在简单了解了下OAuth的原理及使用后,先确定了重构的范围:

1、基础类oAuthBase.cs不动,因为这个类是由官方提供的。

2、有必要时外部调用接口也需要更改(事实上写到最后几乎对所有的接口都改了)。

开始下手,先看oAuthWebRequest这个函数

 

public string oAuthWebRequest(Method method, string url, string postData)
    {
        string outUrl = "";
        string querystring = "";
        string ret = "";
        postData += "&source=" + appKey;
        if (method == Method.POST)
        {
            if (postData.Length > 0)
            {
                NameValueCollection qs = HttpUtility.ParseQueryString(postData);
                postData = "";
                foreach (string key in qs.AllKeys)
                {
                    if (postData.Length > 0)
                    {
                        postData += "&";
                    }
                    qs[key] = HttpUtility.UrlEncode(qs[key]);
                    qs[key] = this.UrlEncode(qs[key]);
                    postData += (key + "=" + qs[key]);

                }
                if (url.IndexOf("?") > 0)
                {
                    url += "&";
                }
                else
                {
                    url += "?";
                }
                url += postData;
            }
        }

        Uri uri = new Uri(url);

        string nonce = this.GenerateNonce();
        string timeStamp = this.GenerateTimeStamp();

        //Generate Signature
        string sig = this.GenerateSignature(uri,
            this.appKey,
            this.appSecret,
            this.token,
            this.tokenSecret,
            method.ToString(),
            timeStamp,
            nonce,
            out outUrl,
            out querystring);


        querystring += "&oauth_signature=" + HttpUtility.UrlEncode(sig);

        if (method == Method.POST)
        {
            postData = querystring;
            querystring = "";
        }

        if (querystring.Length > 0)
        {
            outUrl += "?";
        }

        if (method == Method.POST || method == Method.GET)
            ret = _WebRequest(method, outUrl + querystring, postData);
        return ret;
    }

这个函数的目的是来创建请求,并且将请求结果返回。

让我们来识别一下这个函数都有哪些bad smell:

1、long method.这个方法足足有70行。

2、Temporary Field.光函数开头声明的临时变量就有3个,函数内部声明的临时变量更是不尽其数。

3、Single Responsibility Principle.函数中充斥着大量的if-else,以及多处判断POST与Get。

4、Higher Cyclomatic Complexity.函数中存在可以避免的嵌套 if-else

重构的方法:

首先根据不同的请求类型(POST/GET)来声明不同的处理方法,为了使客户类调用行为一致,我这里抽取出了一个IHttpRequestMethod接口

 

public interface IHttpRequestMethod
    {
        string Request(string uri, string postData);        
    }

其中Request方法相当于原库中oAuthWebRequest,分别用两个类HttpGet与HttpPost来实现。

然后是将这个方法拆解成为几个小方法。最后的代码如下:

 

 public class HttpPost : BaseHttpRequest
    {
        .....
        ......

        public override string Request(string uri, string postData)
        {
            var appendUrl = AppendPostDataToUrl(postData, uri);
            string outUrl;
            var querystring= AppendSignatureString(POST, appendUrl, out outUrl);
            return WebRequest(POST, outUrl, querystring);
        }
        ......
    }

 

 

 public class HttpGet : BaseHttpRequest
    {
       .......
        ......

        public override string Request(string uri, string postData)
        {
            string outUrl;
            var queryString = AppendSignatureString(GET, uri, out outUrl);
            if (queryString.Length > 0)
            {
                outUrl += "?";
            }
            return WebRequest(GET, outUrl + queryString);
        }
       ......
     }

全部代码参见:

http://code.google.com/p/opensinaapi/downloads/detail?name=OpenSinaApi04-18-2011.rar

欢迎各位看官一起动手来进一步重构:)

Posted by Leo 2011年4月17日 18:47