Just a quick update on Browserling. We just launched IE 10s! IE 10s are available to all our paying customers. The free browserling version offers IE 9.

Internet Explorer 10.0.9200.16466

Next we're getting Mac OSX browsers and iPhone emulators. Stay tuned! Follow us on twitter for updates - @browserling, @testling, @substack, @pkrumins.

Try Browserling!

Browserling is an interactive cross-browser testing tool. It lets you test your website in older Internet Explorers such as IE 6, IE 7, IE 8 and IE 9, IE 10, Firefox, Chrome, Opera and Safari!

I thought I'd share this idea I've had for a while and that I want to build in the future. It's a 1:1 teaching network where people meet up and have 1:1 sessions teaching each other things they don't know. I've found that 1:1 teaching sessions are much more effective than a group teaching session or learning on your own. In a 1:1 teaching session you can keep asking the other person questions instantly and infinitely and you can learn really advanced topics really quickly.

Here's an example of how this meetup network would work. Let's say I'm an expert in Python. And let's say you're an expert in Ruby. And let's say you want to master Python and I want to master Ruby. So we schedule a meet up and I teach you Python for a few hours, and you teach me Ruby for a few hours. And we rescheduling meetups whenever we're both available.

The way I imagine it could be built is having a website where you indicate where you're at, what you're a master at, indicate what you want to master, and indicate the time that you've available. The system then matches you with someone and suggests a meetup.

I'd teach and learn and teach and learn forever this way!

There are so many other ideas that I want to build and I'll just keep sharing them in my "ideas for later" blog post series! See ya!

Writing testling-ci tests is super easy. The only thing your test runner needs to do is to produce TAP output on console.log or process.stdout.

Let's create a new node module called testling-ci-test-example and let's add cross-browser tests to it. Once the tests run, you get a badge like this that shows in which browsers your code works:

If you click the badge you go to the test status page.

First, let's create index.js that exports a few functions, some of which work in some browsers and fail in others:

// doesn't work in IE<9
exports.timesTwo = function (list) {
    return list.map(function (x) { return x*2 });

// works everywhere
exports.timesThree = function (list) {
    var ret = [];
    for (var i = 0; i < list.length; i++) {
        ret[i] = list[i]*3;
    return ret;

Now let's write tests for this module. We'll put tests in tests/test.js:

var test = require('tape');
var exampleLib = require('../index.js');

test('timesTwo test', function (t) {
  t.deepEqual(exampleLib.timesTwo([1,2,3]), [2,4,6]);

test('timesThree test', function (t) {
  t.deepEqual(exampleLib.timesThree([1,2,3]), [3,6,9]);

The tests use tape module that console.log's TAP formatted output.

Now let's add testling field to package.json:

"testling" : {
    "files" : "test/*.js",
    "browsers" : {
        "ie" : [ 6, 7, 8, 9, 10 ],
        "ff" : [ 3.5, 10, 15.0, 16.0, 17.0 ],
        "chrome" : [ 10, 20, 21, 22, 23 ],
        "safari" : [ 5.1 ],
        "opera" : [ 10, 11, 12 ]

Finally, let's setup the github hook and point it to git.testling.com. First go to the settings of the repository,

Next, go to the service hooks,

Then, choose WebHook URLs,

Finally, type git.testling.com in the webhook URL field and press update settings,

And we're done. If we visit http://ci.testling.com/pkrumins/testling-ci-test-example we'll see the badge and which browsers the tests succeeded and in which browsers the code failed:

We can instantly see that the code fails in IE6, IE7, and IE8 because these versions of IE don't have Array.prototype.map, and that the code works everywhere else. So awesome!

For more information about testling-ci see ci.testling.com.


About Browserling

Our mission at Browserling Inc is to make the developers' life easier. Follow us on twitter for updates - @browserling, @testling, @substack, @pkrumins.

We at Browserling are proud to announce Testling-CI! Testling-CI lets you write continuous integration cross-browser tests that run on every git push!


There are a ton of modules on npm and github that aren't just for node.js but for browsers, too. However, figuring out which browsers these modules work with can be tricky. It's often that case that some module used to work in browsers but has accidentally stopped working because the developer hadn't checked that their code still worked recently enough. If you use npm for frontend and backend modules, this can be particularly frustrating.

Testling-CI (http://ci.testling.com) is a service for running your browser tests on every commit. Just add a "testling" field to your package.json like this:

"testling" : {
    "files" : "test/*.js",
    "browsers" : {
        "iexplore" : [ "6.0", "7.0", "8.0", "9.0" ],
        "chrome" : [ "20.0" ],
        "firefox" : [ "10.0", "15.0" ],
        "safari" : [ "5.1" ],
        "opera" : [ "12.0" ]

Then add a github webhook for http://git.testling.com and your browser tests will be run on every commit in exactly the browsers you've specified!

Once the tests run you get a badge you can put in your readme with the current browser test status. Here's how it looks:

If you click the badge you go to the test status page.

All your tests need to do is output TAP-formatted text output. Many test harnesses already support this format and the protocol is simple enough that you can just console.log() valid output yourself.

We recommend using tape to output TAP or if you use Mocha, we've a testling-ci example using Mocha. We'll support many more test harnesses later.

Your browser tests can even use node-style require()s bundled with browserify so that you can use exactly the same tests for node and the browser. If your browser tests aren't written the node way you can still run them with the "scripts" field. See testling-ci homepage for full documentation.

This service is free for open source modules published to github. If you want to run this service on your private repos, contact us and we can enroll you in our testling professional edition beta!

For full documentation and more examples visit ci.testling.com!

About Browserling

Our mission at Browserling Inc is to make the developers' life easier. Testling-CI is our third product. Take a look our first two products Browserling and Testling.

Browserling lets you do manual cross-browser testing in all major browsers, such as Internet Explorer 6, 7, 8, 9; Chrome; Safari; Firefox and Opera.

Testling lets you do automated cross-browser JavaScript testing. You write your tests in JavaScript and we run them on the browsers and tell you the results. Testling-CI is built on top of Testling.

We're also soon launching our fourth amazing product. Stay tuned! Follow us on twitter for updates - @browserling, @testling, @substack, @pkrumins.

Important Update:

We've changed goals are we're not launching a fourth product.

We've retired Testling as we built Testling-CI.

All questions about Testling-CI: feedback@browserling.com

This is going to be an article for beginners but with a spin at the end even for the most advanced users that involves analyzing source code of bash to determine how it actually works.

Let's say you want to run a command inside of chroot as another user all in a single command and be back at your shell. How do you do it? Turns out it can be easily done by combining several commands in a beautiful way:

sudo chroot /chroot su - user -c "cmd args"

This command combines four commands together - sudeo, chroot, su and cmd. Each command calls the next and when the last command finishes we return back to our shell (instead of being left inside of chroot.) It may seem like a pretty crazy combination of commands but that's the only way to get it working.

First, let's take a look at man chroot. It says that to run command inside chroot, you do this:

sudo chroot /chroot command

With this command we can run command inside of chroot as root, but how do we run it as another user? How about using su for command? Let's try that:

sudo chroot /chroot su - user

Nice! This runs su inside of chroot as user but we get an interactive shell and we don't return back to our shell. Let's take a closer look at man su. It says that we could try:

sudo su - user -c "cmd args"

Man page says that this makes su run cmd with arguments args as a shell command. Great! So let's use this together with chroot again:

sudo chroot /chroot su - user -c "cmd args"

Excellent! So are we done? Turns out not quite. There's still one problem. If the user has a custom environment (such as different PATHs to executables or some other customizations), it won't get initialized and we've to source the initialization file(s) ourselves:

sudo chroot /chroot su - user -c ". ~/.bash_profile; cmd args"

This does what we wanted and runs cmd args inside of chroot located at /chroot as user, initializes .bash_profile and after running cmd it quits, and we're back at our shell.

On Sourcing ~/.bash_profile

Some people couldn't understand why I was sourcing ~/.bash_profile. The reason is because I was unable to find another way to invoke the login shell and have the environment initialized.

I tried this:

sudo chroot /chroot su -l user -c "cmd args"

And it just wouldn't execute ~/.bash_profile. Su's man page says that -l invokes a login shell, however I don't see that happening. Here's the transcript that demonstrates it:

pkrumins$ pwd

pkrumins$ sudo cat ./home/testuser1/.bash_profile 
echo "moo"

pkrumins$ sudo chroot /chroot su -l testuser1 -c 'echo $PATH'

pkrumins$ sudo chroot /chroot su -l testuser1 

testuser1$ echo $PATH

testuser1$ ^D

pkrumins$ sudo chroot /chroot su -l testuser1 -c '. ~/.bash_profile; echo $PATH'       

pkrumins$ sudo chroot /chroot su -l testuser1

testuser1$ /bin/bash -l -c 'echo $PATH'

This definitely looks like a bug in su to me when -l and -c are used together. So the only way to get the environment loaded is by sourcing the initialization files yourself.


I went through su's source code (can be found in util-linux package) and it turns out it's not a bug in su!

Here are the arguments that su passes to execv when I run su user -c 'echo $PATH' (see run_shell function in su.c):

shell: /bin/bash
args[0]: -bash
args[1]: -c
args[2]: echo $PATH

As you can see, args[0][0] is -, which makes bash a login shell, so bash should be executing the startup files, but it does not!

To figure out what was happening, I built a custom version of bash and added a bunch of debugging statements. I found that there are two different login-shell states! Who'd have thought? One that you get when you've set args[0][0] to -, the other that you get when you've used the -l argument (to bash, not su!).

In case args[0][0] is -, and you're using -c to execute code, bash will not execute startup files because it's in this "non-interactive positive-login-shell" state (see run_startup_files function in bash's shell.c source file.)

However if you use -l and -c, it goes into "non-interactive negative-login-shell" state, and it will execute startup files. But we can't pass -l argument to bash, we can only pass it to su, which is not the same as passing it to bash!

That was one hell of an adventure debugging this. So if you want bash to execute startup files through su, use the . ~/.bash_profile sourcing trick! (Or alternatively define NON_INTERACTIVE_LOGIN_SHELLS when building bash.)

Until next time!