Testing Dojo web applications with Selenium

I jot down here some findings about testing Dojo web applications (with a lot of Dijit Widget!) using Selenium, just in case someone will need them.

The first thing that you should never ever try to do is simulating events using JavaScript. Some libraries like jQuery provide convenient ways to click, focus, or type on HTML elements. But these “synthetic” events have two important limitations:

  • You have to find the right element to send an event. The DOM trees generated by Widgets are huge, with layers under layers. Sometimes it is necessary to read the code and decide which element is used by Dojo to receive which kind of event. Moreover, synthetic events are bad as it will not be able to consistently updating the focused elements the way human interactions do, while Widgets heavily rely on this behavior. Some Widgets even refuse to react upon faux clicks.
  • They can be sent to invisible elements, while human obviously cannot manipulate with such things. Dijit Widgets consist of many HTML elements that are carefully arranged in different positions. Any wrongly placed element can render the Widget unusable, while JavaScript does not care about (and understand) position. Therefore, synthetic events are not reliable at all, which would render your tests useless.

Without JavaScript, we have two more effective ways to trigger actions on Widget. They both simulate human interactions in the most native way.

The first one is to utilize native events implemented by all major web browsers. This approach should work well unless you encounter a bug in their implementation, or you are testing against an unpopular browser. It is quite straightforward to enable native events:

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“nativeEvents”, true);

The second one, which will work for edge cases, is to use a robot. A robot can simulate human action as Operating System level. This means if the robot is trying to move the mouse, you will see on your screen that the pointer is moving. The drawback is that the executing machine needs to have a display device regardless of being a physical machine or a virtual one (in another word, a headless machine will not work). Another disadvantage is that you have to convert coordinates from window-relative into device-relative. To overcome this limitation, just run the browser in full-screen mode. For example, with Chrome, we use “kiosk” command switch for full-screen mode:

ChromeOptions options = new ChromeOptions();
options.addArguments(“—kiosk”);

And we control the robot like this:

Point location = input.getLocation();
int x = location.getX();
int y = location.getY();
Robot r = new Robot();
r.mouseMove(x + 10, y + 10);

Finally, Dijit Widget has a bug with touch event (Dojo manipulate browser’s events a lot, it even managing it own focusing behavior so it is really prone to bugs and browser’s compatibility. Make sure you have latest fixes and if things still happen, disable touch events in Chrome.

  1. From Google Chrome, type chrome://flags in the address bar, then press Enter.
  2. Scroll down to the “Enable Touch Events”.
  3. Select the drop-down menu and select Disabled.
  4. Restart and enjoy Chrome!
Advertisements

Linh tinh về datagrid

Table, datagrid (không biết từ này có trong từ điển không, tránh nhầm với data grid), bảng dữ liệu, v.v là thứ không thể tránh khỏi khi làm mấy thứ hệ thống thông tin :(. Trên thị trường hiện nay có rất nhiều loại datagrid cho web. Hàng bán cả nghìn USD cũng có, hàng trôi nổi cũng không thiếu, mẫu mã đẹp, chức năng đa dạng, nhưng đã dùng là phải hối hận. Chúng ta sẽ xem xét một vài tiêu chí để chọn hoặc xây dựng datagrid.

Nhưng mà trước khi bắt đầu có lẽ tôi phải nói điều này. Nếu bạn định dùng DataGrid của Dojo, hay đang cố gắng chỉnh sửa nói, hay đang cố gắng khắc phục lỗi của nó, thì bạn nên cân nhắc việc bỏ hẳn nó để dùng một thứ khác, có thể là HTML table, có thể là bạn tự viết, SlickGrid, jqGrid, v.v. Nói rộng hơn, nếu bạn đang định dùng hay mới dùng Dojo thì hãy cố gắng tránh xa nó ra trước khi quá muộn.

Quay lại bài.

Quan hệ giữa dữ liệu và việc hiển thị

Các datagrid nói chung thường hay cố gắng quá mức trong việc quản lí data, như xây dựng các Store hay Model để lấy dữ liệu từ server, phân trang, tìm kiếm, cập nhật động, v.v mà trong khi đó xem nhẹ các method để giao tiếp trực tiếp với việc hiển thị. Hệ quả là việc tổ chức dữ liệu cho datagrid sẽ bị couple một cách cứng nhắc, gây khó khăn trong việc tùy chỉnh datagrid hay chuyển đổi sang một loại datagrid mới. Ở mức tối thiểu, datagrid phải cho chúng ta thực hiện những hành động như:

  • thay đổi giá trị được hiển thị (chứ không phải giá trị của dữ liệu) trong một ô
  • render lại một ô, một dòng, hay một khu vực nào đó (chứ không phải cập nhật dữ liệu của datagrid)
  • thay đổi thứ tự các dòng (chứ không phải sắp xếp bản thân dữ liệu của datagrid)
  • v.v

Trong trường hợp tự viết datagrid, tôi nghĩ chúng ta sẽ có ba thành phần:

  • Thành phần hiển thị, đây chính là datagrid. Thành phần này chỉ yêu cầu dữ liệu với cấu trúc tối thiểu, không cần hiểu những chức năng như sắp xếp, tìm kiếm, v.v. Đối với nó, dữ liệu chỉ là để  hiển thị.
  • Thành phần quản lí dữ liệu, như Store hay Model tùy theo framework hay cách bạn viết. Thành phần này ngược lại, không cần biết và không nên biết rằng dữ liệu sẽ được hiển thị với datagrid hay bất kì cái gì.
  • Một datagrid builder để xây dựng cầu nối giữa hai thành phần trên.

Không may là nhiều datagrid có sẵn lại được cung cấp ở dạng datagrid builder, với hai thành phần đầu tiên trộn lẫn rất chặt với nhau.

Markup được sinh ra

Tùy vào mức độ phức tạp của bảng dữ liệu, datagrid có thể dùng thẻ table của HTML, hay dùng thẻ table cho mỗi một dòng, hoặc dùng div và CSS để xếp thành bảng. Riêng table có một ưu điểm là cấu trúc bảng sẽ luôn được bảo đảm bởi ngữ nghĩa của markup, sẽ không bao giờ có chuyện ô trên rộng, ô dưới hẹp, mất ô, v.v. Nói chung, mỗi kiểu có ưu nhược điểm khác nhau, tuy nhiên tốt hơn cả là việc render có thể thay đổi được, dựa vào chẳng hạn những hàm callback. Datagrid sẽ không cần biết dữ liệu được hiển ở dạng bảng hay dạng gì, chúng chỉ cần biết là phải render ô, một nhóm các ô (ví dụ gọi là dòng), và một nhóm các dòng (ví dụ gọi là trang), v.v. Khi đó với dạng đơn giản chúng ta có thể render bảng dùng thẻ table, với dạng phức tạp chúng ta dùng div, v.v. Nếu không thì ít nhất việc thay đổi style như chiều rộng, chiều cao có thể được thực hiện dễ dàng bởi một người chỉ biết CSS. Đồng thời chiều cao của datagrid không được phụ thuộc nhiều vào sự tính toán của JavaScript (như DataGrid của Dojo), mà phải phụ thuộc vào việc reflow của trình duyệt.

Cơ chế plugin

Datagrid nào cũng cho bạn tự viết các hàm callback để tạo giá trị hiển thị trong một ô, hay tạo các thành phần giao diện để người dùng có thể nhập dữ liệu. Tuy nhiên, một datagrid tốt thì nên để cho người dùng có thể tùy ý thay đổi markup. Lí do là việc hiển thị dữ liệu hay thay đổi theo yêu cầu của phần mềm, như tô màu, ẩn, nhập ô, v.v và đều khó đoán trước. Trong khi đó datagrid là một thành phần giao diện phức tạp và việc đọc hiểu cũng như sửa đổi từ bên trong sẽ đòi hỏi nhiều thời gian và công sức. Tốt nhất là datagrid nên cung cấp những hàm callback với tham số là bản thân DOM node của các thành phần trên datagrid. Các callback này có thể được thể hiện như những filter method nhằm cho phép các module khác nhau có thể chỉnh sửa cùng một bộ phận trong markup.

Vài chú ý khi dùng Dojo

Nói chung, Dojo là một framework khá hoàn chỉnh, cung cấp một lớp các tính năng giúp bạn viết JavaScript hiệu quả hơn:

  • Các method tiện ích với DOM (dojo.create, dojo.place, v.v) và với JavaScript (dojo.isArray, dojo.hitch, v.v).
  • Một mô hình OOP kiểu class tương tự như các ngôn ngữ phổ biến Java, C#. Kết hợp với mô hình OOP kiểu prototype của JavaScript, việc lập trình OOP trên Dojo trở nên cực kì thoải mái và tiện lợi.
  • Các cơ chế để thao tác với dữ liệu, như lấy dữ liệu từ server, tìm, thêm, xóa dữ liệu. Có thể xem đây là một persistent library rất đơn giản cho JavaScript. Tính năng này cũng giúp tách biệt phần nội dung ra khỏi phần hiển thị của các widget, giúp cho việc thao tác với dữ liệu trên các widget dạng bảng, cây của Dojo khá mạch lạc.
  • Các widget thường dùng và cơ chế để viết thêm các widget khác. Việc viết thêm widget trong Dojo phải nói là khá đơn giản và được hướng dẫn rất chi tiết.
  • Cơ chế publish/subscribe các event, theo object và global. Event là một khái niệm quan trọng trong JavaScript, và nó giúp giảm coupling khi thiết kế các ứng dụng web nặng về Ajax. Với Dojo, tất cả các method đều là event.
  • Cơ chế để quản lí sự phụ thuộc giữa các file JavaScript (tương tự như import trong Java). Dojo giúp đơn giản hóa việc nạp các file JavaScript cần thiết từ server.
  • Trong thiết kế có tính đến việc hỗ trợ accessiblity. Tùy nhu cầu mà điều này có cần thiết hay không.

Tuy nhiên, Dojo cũng có nhiều hạn chế mà bạn phải cân nhắc.

  • Mức độ sử dụng thấp, nghĩa là cộng đồng hỗ trợ sẽ nhỏ hơn so với các library phổ biến hơn.
  • Widget của Dojo được tập hợp một cách có hệ thống, và mỗi widget luôn cố gắng đáp ứng mọi nhu cầu có thể. Điều này khiến cho một số widget như Tree, DataGrid trở nên cồng kềnh và phức tạp, cả về HTML sinh ra lẫn JavaScript cho hành vi của chúng. Việc chỉnh sửa chúng cho phù hợp với nhu cầu sẽ tiêu tốn nhiều công sức của bạn.
  • Chất lượng mã nguồn không đồng đều. Một số widget được xây dựng từ nhiều thành phần nhưng lại không thiết kế phù hợp với nguyên tắc Inversion of Control, khiến cho việc mở rộng, chỉnh sửa mỗi thành phần khó khăn hơn. Mặc dù điều này có thể được khắc phục phần nào trong một số trường hợp với prototype.
  • Việc layout các widget của Dojo phụ thuộc rất nhiều vào kích thước của các widget liên quan, trong khi đó hiện thực các tính toán này không hoàn toàn ổn định, rải rác khắp mọi chỗ trong mã nguồn, có thể ảnh hưởng tới hiệu năng và độ ổn định của ứng dụng.

Nói thêm, bởi vì Dojo là một framework hoàn chỉnh nhằm hỗ trợ mọi nhu cầu liên quan đến phát triển ứng dụng web với JavaScript, cho nên sẽ khó mà chọn được framework thay thế với chất lượng hoàn toàn tốt hơn. Tuy nhiên, bạn có thể thử gộp những library nhỏ để có được những tính năng tương tự như Dojo cung cấp. Vì các library nhỏ nhằm giải quyết những vấn đề cụ thể hơn, chúng có thể tiến hóa nhanh hơn và ổn định hơn một framework lớn. Chẳng hạn:

  • JQuery, Prototype.js, MooTools cho những mở rộng đơn giản trên JavaScript.
  • JQuery cho thao tác với DOM, và JQuery UI cho các widget thường dùng.
  • Prototype.js để sử dụng mô hình OOP kiểu class trong JavaScript.
  • v.v

Cuối cùng, nếu bạn đã quyết định dùng Dojo, thì có lẽ vài kinh nghiệm nhỏ dưới đây sẽ có ích cho bạn:

  • Học JavaScript. Nó đủ khác các ngôn ngữ phổ biến khác để bạn phải học một cách cẩn thận.
  • Đọc kĩ tài liệu Dojo cung cấp, và nếu có thể, xem qua mã nguồn của một số thành phần để hiểu rõ.
  • Mở rộng, chỉnh sửa các thành phần Dojo cung cấp theo ý của mình, thay vì cố gắng tìm một giải pháp “hack” dựa trên những gì Dojo đã có.
  • Tìm hiểu về những vấn đề liên quan nếu cần, chẳng hạn như memory leak trong JavaScript và Dojo, kiến trúc của một ứng dụng web nặng về JavaScript, v.v