Posts tagged with “code”

1 Jul
11 Jun

Pet project: build web version of thinlet

I recently forked an open source project called FrontlineSMS.

FrontlineSMS uses Thinlet to build the GUI. Thinlet is a XUL framework with a very small footprint. Unfortunately, Thinlet only targets thick clients (desktops) and thin clients (applets).

The goal of my project is to port the application from a desktop-based solution to a web-based solution without changing their code base.To achieve it, I'm thinking of writing a brand new implementation of Thinlet that will target webapps instead of desktops and applets.

24 Feb
428ca41b64ab74d527885656ffe2b0c9.png

My first comic strip! Hope you'll enjoy :)

· Tags: , ,
12 Jan
design-pattern-selenium.png

I found the Decorator Pattern to be very practical when using Selenium. It allows you to dynamically add new behavior on the selenium object and still use the same syntax (the Selenium methods), which makes the test script clean and easy to read.

11 Jan

Lessons learned when using Selenium

Favor elements ids rather than complex XPath queries

  • In general, XPath engines are much faster when searching for ids
  • Also, if your HTML changes, your XPath will break. Unfortunately, HTML always change in an iterative process, hence it will always break.

Encapsulate all your elements ids inside class constants, then build your test scenarios

  • Because the HTML code often changes, if 5 scripts have to login and logout all the time, you will have to update those 5 scripts.
  • However, if you only use constants to specify the login and logout links, you will only have to change the constant value in one place.

Only automate the happy path!

  • Writing automated web tests takes time in terms of development and maintenance, but they are great time savers when regression testing before pushing to QA.
  • Furthermore, if normal path is already covered by automated tests, it allows QA to cover more areas of the product and increase quality.
22 Jun

Selenium RC code to generate C# unit test for Microsoft.VisualStudio.TestTools.UnitTesting

/*
 * Format for Selenium Remote Control .NET (C#) client.
 * This code goes to Selenium IDE
 */
 
load('remoteControl.js');
 
this.name = "cs-rc";
 
function testMethodName(testName) {
	return "The" + capitalize(testName) + "Test";
}
 
function assertTrue(expression) {
	return "Assert.IsTrue(" + expression.toString() + ");";
}
 
function assertFalse(expression) {
	return "Assert.IsFalse(" + expression.toString() + ");";
}
 
function verify(statement) {
	return "try\n" +
	  "{\n" +
	  indents(1) + statement + "\n" +
	  "}\n" +
	  "catch (AssertionException e)\n" +
	  "{\n" +
	  indents(1) + "verificationErrors.Append(e.Message);\n" +
	  "}";
}
 
function verifyTrue(expression) {
	return verify(assertTrue(expression));
}
 
function verifyFalse(expression) {
	return verify(assertFalse(expression));
}
 
function assignToVariable(type, variable, expression) {
	return capitalize(type) + " " + variable + " = " + expression.toString();
}
 
function waitFor(expression) {
	return "for (int second = 0;; second++) {\n" +
	  indents(1) + 'if (second >= 60) Assert.Fail("timeout");\n' +
	  indents(1) + "try\n" +
	  indents(1) + "{\n" +
	  (expression.setup ? indents(2) + expression.setup() + "\n" : "") +
	  indents(2) + "if (" + expression.toString() + ") break;\n" +
	  indents(1) + "}\n" +
	  indents(1) + "catch (Exception)\n" +
	  indents(1) + "{}\n" +
	  indents(1) + "Thread.Sleep(1000);\n" +
	  "}";
}
 
function assertOrVerifyFailure(line, isAssert) {
	var message = '"expected failure"';
	var failStatement = isAssert ? "Assert.Fail(" + message + ");" : 
		"verificationErrors.Append(" + message + ");";
	return "try\n" +
	  "{\n" +
		line + "\n" +
		failStatement + "\n" +
	  "}\n" +
	  "catch (Exception) {}\n";
}
 
Equals.prototype.toString = function() {
	return this.e1.toString() + " == " + this.e2.toString();
}
 
NotEquals.prototype.toString = function() {
	return this.e1.toString() + " != " + this.e2.toString();
}
 
Equals.prototype.assert = function() {
	return "Assert.AreEqual(" + this.e1.toString() + ", " + this.e2.toString() + ");";
}
 
Equals.prototype.verify = function() {
	return verify(this.assert());
}
 
NotEquals.prototype.assert = function() {
	return "Assert.AreNotEqual(" + this.e1.toString() + ", " + this.e2.toString() + ");";
}
 
NotEquals.prototype.verify = function() {
	return verify(this.assert());
}
 
RegexpMatch.prototype.toString = function() {
	return "Regex.IsMatch(" + this.expression + ", " + string(this.pattern) + ")";
}
 
EqualsArray.prototype.length = function() {
	return this.variableName + ".Length";
}
 
EqualsArray.prototype.item = function(index) {
	return this.variableName + "[" + index + "]";
}
 
function pause(milliseconds) {
	return "Thread.Sleep(" + parseInt(milliseconds) + ");";
}
 
function echo(message) {
	return "Console.WriteLine(" + xlateArgument(message) + ");";
}
 
function statement(expression) {
	return expression.toString() + ';';
}
 
function array(value) {
	var str = 'new String[] {';
	for (var i = 0; i < value.length; i++) {
		str += string(value[i]);
		if (i < value.length - 1) str += ", ";
	}
	str += '}';
	return str;
}
 
CallSelenium.prototype.toString = function() {
	var result = '';
	if (this.negative) {
		result += '!';
	}
	if (options.receiver) {
		result += options.receiver + '.';
	}
	result += capitalize(this.message);
	result += '(';
	for (var i = 0; i < this.args.length; i++) {
		result += this.args[i];
		if (i < this.args.length - 1) {
			result += ', ';
		}
	}
	result += ')';
	return result;
}
 
function formatComment(comment) {
	return comment.comment.replace(/.+/mg, function(str) {
			return "// " + str;
		});
}
 
this.options = {
    receiver: "selenium",
	indent:	'tab',
    initialIndents:	'3',
	namespace: "SeleniumTests"
};
 
options.header =
	'using System;\n' +
	'using System.Text;\n' +
	'using System.Text.RegularExpressions;\n' +
	'using System.Collections.Generic;\n' +
	'using System.Threading;\n' +
	'using System.Linq;\n' +
	'using Microsoft.VisualStudio.TestTools.UnitTesting;\n' +
	'using Selenium;\n' +
	'\n' +
	'namespace ${namespace}\n' +
	'{\n' +
 
	indents(1) + '[TestClass]\n' +
	indents(1) + 'public class ${className}\n' +
	indents(1) + '{\n' +
	indents(2) + 'private static ISelenium selenium;\n' +
	indents(2) + 'private static StringBuilder verificationErrors;\n' +
	indents(2) + '\n' +
 
	indents(2) +  'public ${className}()\n' +
	indents(2) +  '{\n' +
	indents(3) +  '//\n' +
	indents(3) +  '// TODO: Add constructor logic here\n' +
	indents(3) +  '//\n' +
	indents(2) +  '}\n' +
 
	indents(2) +  'private TestContext testContextInstance;\n' +
 
	indents(2) +  '/// <summary>\n' +
	indents(2) +  '///Gets or sets the test context which provides\n' +
	indents(2) +  '///information about and functionality for the current test run.\n' +
	indents(2) +  '///</summary>\n' +
	indents(2) +  'public TestContext TestContext\n' +
	indents(2) +  '{\n' +
	indents(3) +  'get\n' +
	indents(3) +  '{\n' +
	indents(4) +  'return testContextInstance;\n' +
	indents(3) +  '}\n' +
	indents(3) +  'set\n' +
	indents(3) +  '{' +
	indents(4) +  'testContextInstance = value;\n' +
	indents(3) +  '}\n' +
	indents(2) +  '}\n' +
 
 
	indents(2) +  '//\n' +
	indents(2) +  '// You can use the following additional attributes as you write your tests:\n' +
	indents(2) +  '//\n' +
	indents(2) +  '// Use ClassInitialize to run code before running the first test in the class\n' +
	indents(2) +  ' [ClassInitialize()]\n' +
	indents(2) +  ' public static void MyClassInitialize(TestContext testContext)\n' +
	indents(2) +  '{\n' +
	indents(3) +  'selenium = new DefaultSelenium("localhost", 4444, "*chrome", "${baseURL}");\n' +
	indents(3) +  'selenium.Start();\n' +
	indents(3) +  'verificationErrors = new StringBuilder();\n' +
	indents(2) +  '}\n' +
	indents(2) +  '\n' +
	indents(2) +  '//' +
	indents(2) +    '// Use ClassCleanup to run code after all tests in a class have run\n' +
	indents(2) +    ' [ClassCleanup()]\n' +
	indents(2) +    'public static void MyClassCleanup()\n' +
	indents(2) + '{\n' +
	indents(3) + 'try\n' +
	indents(3) + '{\n' +
	indents(4) + 'selenium.Stop();\n' +
	indents(3) + '}\n' +
	indents(3) + 'catch (Exception)\n' +
	indents(3) + '{\n' +
	indents(4) + '// Ignore errors if unable to close the browser\n' +
	indents(3) + '}\n' +
	indents(3) + 'Assert.AreEqual("", verificationErrors.ToString());\n' +
	indents(2) + '}\n' +
	indents(2) + '\n' +
	indents(2) +    '//\n' +
	indents(2) +    '// Use TestInitialize to run code before running each test \n'+
	indents(2) +    '// [TestInitialize()]\n' +
	indents(2) +    '// public void MyTestInitialize() { }\n' +
	indents(2) +    '//\n' +
	indents(2) +    '// Use TestCleanup to run code after each test has run\n' +
	indents(2) +    '// [TestCleanup()]\n' +
	indents(2) +    '// public void MyTestCleanup() { }\n' +
	indents(2) +    '//\n' +
 
	indents(2) + '\n' +
	indents(2) + '[TestMethod]\n' +
	indents(2) + 'public void ${methodName}()\n' +
	indents(2) + '{\n';
 
options.footer =
	indents(2) + '}\n' +
	indents(1) + '}\n' +
	'}\n';
 
this.configForm = 
	'<description>Variable for Selenium instance</description>' +
	'<textbox id="options_receiver" />';
 
 
· Tags:
9 Jun

Calling Card Dialer

Website - http://dev.hamidou.net
Card Dialer automate the calls when using calling cards for long distances calls or recharging your prepaid phone: It takes in charge all the tedious steps once you’re connected to the call:
selecting the language, entering your PIN, entering your destination number, etc. This application is good for you if:
  • You use prepaid service.
  • Just update the PIN and let the application take care of doing all the work to recharge your account.
  • You often make long distance calls with a calling card: just enter your phone number and place the call: the application takes care of the rest.
  • You often forget or loose your calling cards: store all your favorite calling cards inside the application and select the one you wish to make a call.
Compatible phones: All Sony Ericsson phones, all Nokia phones, all Motorola phones.
· Tags: ,
10 Nov
Learning Objective C is tougher than I expected!
· Tags:
4 Aug