Posts tagged with “code”
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.
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.
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" />';
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.