Practical AngularJS
Practical AngularJS
Buy on Leanpub
Practical AngularJS

Table of Contents

Leanpub book, originally based on Blog posts

This book started with the AngularJS related blog posts published on my blog and eventually evolved into the book you have in your hands (or eReader).

The idea to use my blog posts was to:

  • kickstart the creation of this book (and provide a number of chapters that could be further improved)
  • capture the multiple experiments and learning curves that I experienced (while learning how to use AngularJS)
  • provide a ‘real world’ point of view on how to learn and use AngularJS

The theme of this book is ‘Practical’ (hence the name ‘Practical AngularJS’), and what I am trying to do here is to show how to use these technologies in practical situations, specially how to apply them to solve real problems. This means that sometimes the end result is a bit messy, with lots of rabbit-holes/tangents followed (and un-followed). But that is how it works. When learning a new technology, what is really important is to experiment a lot, and see how it works for the problem we have at hand.

An open book, freely available at GitHub

My objective with this book is to share knowledge and to help others (as I have been helped by numerous other online resources)

If you are reading this on your eReader, please visit https://github.com/DinisCruz/Book_Practical_AngularJS for a full hyperlinked copy of the content AND (more importantly) the code samples used in the book.

This repository contains the full source materials that are used to create the version available at Leanpub which can be downloaded for free, or paid for using the dynamic ‘how much do you want to pay’ slider. These fees help to cover the costs of creating this book, so if you bought the book, THANK YOU, if you haven’t, then it would be great if you could help a bit (assuming you liked and learned from this book).

I’m also using the GitHub’s Issues (see here) to manage the work items, ideas and bugs/problems with the content/code-samples.

Please use it if you want to provide any ideas, fixes or contributions to this book.

Finally, note that this book is still in a ‘draft release status’ with lots of content and fixes to be added.

I can be contacted directly at dinis.cruz@owasp.org (note that sometimes I’m not very good with my email, so don’t be shy to resend your email/question multiple times :) )

Notes about current structure

The first version of this book had the chapter order created by the original ‘import from blogger’ (i.e. by publish order).

Next, the posts were split into the following areas: “Using AngularJS”, “KarmaJS”, “Firebase”, “Misc Tricks”, “IDEs”, “Troubleshooting”, “Appendices”, which were a filter based on technology.

At the moment I’m working/thinking about the best way to structure this content, and how to present it in a easy to read/consume format.

It would be great if you (the reader) could provide some feedback on the book’s structure, for example:

  • if you think the book’s order should be different (or chapters renamed)
  • there is content missing (really important to cover in an AngularJS book)
  • a particular chapter is not very clear, easy-to-understand or relevant
  • etc…

About the Author

Dinis Cruz is a Developer and Application Security Engineer focused on how to develop secure applications. A key drive is on ‘Automating Application Security Knowledge and Workflows’ which is the main concept behind the OWASP O2 Platform and the FluentSharp APIs.

After many years (and multiple roles) Dinis is still very active at OWASP, currently leading the OWASP O2 Platform project and helping out other projects and initiatives.

After failing to scale his own security knowledge, learned Git, created security vulnerabilities in code published to production servers, delivered training to developers, and building multiple CI (Continuous Integration) environments; Dinis had the epiphany that the key to application security is “Secure Continuous Delivery: Developer’s Immediate Connection to What They’re Creating”. This ‘Immediate Connection/Feedback’ concept is deep rooted in the development of the O2 Platform, and is something that will keep Dinis busy for many years.

Change log:

Here are the changes made (per version):

  • v0.40 (Jan 2016)
    • Added new local editor ui (from o2platform/leanpub-book-site repo) which provides
    • a local site (using express) to preview content
    • generation of Leanpub manuscript folder from a better formatted folder structure
    • Fixed tons of content formatting issues and broken images
    • Fixed issues:
    • This version doesn’t add or remove content (the main focus was on the refactoring and fixing of bad images)
  • v0.30 (Aug 2014)
    • Re-wrote the Introduction
    • Added ‘AngularJS Books’ chapter (to Introduction)
  • v0.20 (29 March 2014)
    • updated Readme.md file to point to existing articles.
    • renamed all content files to *.md (which make sense since they are Markdown files, and they look much better at GitHub) and gave them a name that is related to its current chapter
    • new files: C0-Introduction/Table_of_contents.md
    • created a GitHub-only ‘table of contents’ which links to all available chapters. Each chapter intro page also has the links for its child articles. Added some navigation links to the end of each article (to allow easy navigation when browsing the content in GitHub)
  • v0.13 (16th March 2014)
    • added new cover to eBook version
    • lots of formatting changes to most chapters/posts (specialy on current source code samples)
    • added leanpub attributes for: frontmatter, mainmatter, backmatter
    • major rename of markdown file names to help to quickly see which file belongs to what chapter (see github repo)
  • v0.12 (11th March 2014)
    • fixed spellings
    • fixed nested list in this change log (needs 4 spaces on line start)
    • fixed use of sections in ‘Appendix Posts Details’
    • created github repository for this book: https://github.com/DinisCruz/Book_Practical_AngularJS
  • v0.11 (11th March 2014)
    • Create the following main section: “Using AngularJS”, “KarmaJS”, “Firebase”, “Misc Tricks”, “IDEs”, “Troubleshooting”, “Appendices”
    • Changed the order of the chapters (using the new sections created, instead of the original import structure (by month))
    • Book configuration change to add page break for every section (i.e. blog post)
    • Added this change log
    • Created Git repo on local dropbox sync folder
    • Added Appendix “Post’s Details”
  • v0.10 (10th March 2014)
    • First release of book with raw import from blogger posts (no formatting or editing done)

Post’s dates

This is the chronological order of the posts and when they were posted (on [my blog])(http://blog.diniscruz.com):

March 2013

  • Using Chrome inside a native VisualStudio pane (using Window Handle Hijacking)

April 2013

  • Submitting TM users to HubSpot via TBOT interface (using Angular JS)
  • Hubspot current.js code includes JQuery on it

June 2013

  • AngularJS code editor using UI-Bootstrap and CodeMirror (done without using jQuery)
  • Running KarmaJS’s AngularJS example test/e2e/angular-scenario (on Chrome)
  • Debugging a weird case of missing module in AngularJS and KarmaJS
  • If AngularJS doesn’t work on your O2 Platform IE scripts (the fix is to change browser compatibility mode)
  • KarmaJS AngularJS Scenario Test Runner execution variations in IE 7,8,9 and 10 when using AngularJS
  • A small AngularJS Jasmine test executed by KarmaJS
  • Adding KarmaJS support to WebStorm to automagically run tests on file changes (and test UI with SublimeText, Chrome and Cmd.exe)
  • When the best way to automate Chrome is to use … Chrome (with examples on Google search, direct AngularJS scope manipulation and ChromeDriver javascript access)Â
  • Using WebStorm with Chrome and ChromeDriver (to view KarmaJS execution results)

February 2014

  • Creating an Eclipse UI to run AngularJS e2e tests using Karma
  • Trying out Firebase (Beta) hosting solution and good example of Firebase Security rules
  • First PoC of sending TeamMentor’s server-side request URLS to Firebase (and seeing it in realtime in an AngularJS page)
  • Eclipse Groovy REPL script to sync a Browser with file changes (with recursive folder search via Java’s WatchService)
  • A really SIMPLE and clean AngularJS+Firebase example
  • Using AngularJS in Eclipse, Part 1) The Basics
  • Using AngularJS in Eclipse, Part 2) Add Some Control
  • Using AngularJS in Eclipse, Part 3) Wire up a Backend
  • Using AngularJS in Eclipse, Part 4) Create Components

March 2014

  • Eclipse Groovy script to remove the ‘busy’ image from the WebBrowser Editor
  • Programatically changing an AngularJS scope variable and adding Firebug Lite to an AngularJs app

1. Using AngularJS

This section has the following chapters:

  • Really SIMPLE and clean AngularJS + Firebase example
  • Using AngularJS in Eclipse, Part 1) The Basics
  • Using AngularJS in Eclipse, Part 2) Add Some Control
  • Using AngularJS in Eclipse, Part 3) Wire up a Backend
  • Using AngularJS in Eclipse, Part 4) Create Components
  • AngularJS code editor using UI-Bootstrap and CodeMirror (done without using jQuery)

1.1 Really SIMPLE and clean AngularJS + Firebase example

As seen on the First PoC of sending TeamMentor’s server-side request URLS to Firebase (and seeing it in realtime in an AngularJS page) I created a Simple AngularJS website which I’m very happy with (and I mean Simple with a capital S).

The main reason I really like the solution shown below, is because it represents a number of really nice, clean and Simple solutions for common (complex) problems that exist while developing in Javascript.

The created application is an:

  • AngularJS real-time viewer for HTTP requests,
  • … made to an ASP.NET web application (TeamMentor),
  • … captured by an custom C# HttpHandler filter,
  • … submitted to Firebase using its REST API and
  • … pushed back to the AngularJS app using open HTML 5 WebSockets.

The image below shows what the AngularJS+Firebase application looks like, with the urls shown in the background browser being the ones requested when the TeamMentor website is loaded or navigated (note that the latency between ‘request made’ and ‘request listed’ is really small (about ~ 50 milliseconds)):

What I also like about the AngularJS structure that I ended up with, is that it represents a great way to learn AngularJS’ architecture and capabilities (and yes I know and agree that for bigger AngularJS apps it is better to organize by feature and group the multiple files under a dedicated folder (for example the login controller, service, factory, view and tests should all go under the same logical folder))

This post contains a tour of the multiple files created (gist here) which where developed/refactored while in Eclipse using the Eclipse Groovy REPL script to sync a Browser with file changes (with recursive folder search via Java’s WatchService)

Folder/File Structure:

Inside an Eclipse Web Static project, I created a file structure with:

  • _index.html _as the main AngularJS file (i.e. this is the single-page application file)
  • all javascript files were placed in the _js _folder (with the file names providing a clue on what they are doing/providing)
  • 1 directive created in the directives folder
  • 3 views placed on the views folder

1) Index.html

This is the file loaded directly by the browser, which is made of:

  • External/Framework javascript includes: angular.js, angular-route.js, firebase.js, angularfire.js, bootsrap.min.css
  • AngularJS javascript includes (for the current application): app.js, factories.js, controllers.js, directives.js, routes.js
  • CSS link to bootstrap.min.css
  • Html body (containing the AngularJS _ng-app _ directive) and:
    • div tag with container css class
    • h3 tag with alert-success css class (which creates that nice green top banner)
    • the custom top-menu directive (using attribute instead of element)
    • the AngularJS ng-view directive

Since I’m using the AngularJS Eclipse plugin, hovering the mouse op top of an AngularJS directive provides a nice description of they do.

Here is the ng-app directive

… and here is the ng-view directive:

2) app.js

This is where the project module is created (with two dependencies ngRoute and firebase).

Since I moved the controllers, factories, directives and routes into their own separate js file, there wasn’t much to do here, apart from creating global values for the firebase URL and auth token (which will be dependency injected into the controllers and factories)

3) controllers.js

This file contains 3 controllers: DebugCtrl, MessagesCtrl and RequestsUrlCtrl (each will be used on a specific view)

Note that each controller has services injected into them (the AngularJS $scope and the custom fbDebugMsg, fbRequestUrl, fbaDebugUrl)

The DebugCtrl _is currently just adding the injected _fbDebugMsg and fbRequestUrl services into the $scope so that we can see them in the view (this is a nice trick to get an inside view of AngularJS objects)

The MessagesCtrl is using the Firebase AngularFire API, which is makes it really easy to create the firebase-real-time update view (my only problem with this was that there didn’t seem to be an easy way to re-order the new elements (which in the current AngularFire implementation are added at the end of the provided array)

The RequestsUrlsCtrl uses the default Firebase Javascript API (i.e not the AngularFire one) which gives us more control on how to handle the data received by Firebase. The $scope.urls array is used to store the data received from the Firebase child_added event (one note here to say that the Firebase child_added will also provide the entire data-set on first load, which is okish, but I would prefer that the child_added only fired for new events)

4) directives.js

This is a simple directive used by index.html, that will display a top menu, created by the content of the topMenu.html file (directives are AngularJS way to creating/defining new HTML tags/attributes)

5) factories.js

These factories create the Firebase mappings, namely they define the ‘area’ (or namespace/object) that the data will be read from.

The first two (fbDebugMsg and fbRequestUrl) use the Firebase Javascript API. I needed to do them this way so that I could add the Firebase auth token (that said, I’m sure there is a better way to do this in Angular, since ideally I would have an Angular service that took the two variables that need to be set: the target Firebase area and auth code)

The fbaDebugMsg is just a simple service/factory to return an AngularFire API object based on the (dependency injected) fbDebugMsg service

6) routes.js

The routes configuration is basically defining the 3 available views (each with a different controller mapped to it)

7) requestUrls.html (view)

Since the RequestsUrlsCtrl is responsible for updating the $scope.urls array, all we need to do here is to use Angular’s ng-repeat directive to create a list with all items (the list-unstyled class hides the bullet usually shown in HTML <li> tags).

Note that since the RequestsUrlsCtrl controller is using the Firebase Javascript API child_added event, we will see new entries shown in real time (ie. no browser refresh needed), but any changes made to existing items will not be reflected on the UI (unless the entire page is refreshed and the data is reloaded)

8) messages.html (view)

In this view the $scope.messages (used in the ng-repeat) is populated by the MessagesCtrl controller which is using the AngularFire API. This means that data will be updated in real time (on both add and change events)

… which look like this:

9) debug.html (view)

This view just shows a json representation of the fbDebugMsg and fbRequestUrl

… which looks like this:

10) topMenu.html (directive templateUrl)

Finally this is the html that creates the top menu (this could be improved/refactored by having the url and titles being provided as a ng-model object)

All code:

For reference here is the entire source code (gist here) of the source code files shown above.

1.2 Using AngularJS in Eclipse, Part 1) The Basics

This covers the The Basics example from AngularJS’s home page:

I’m doing this on an OSX laptop and the first step was to download and unzip (eclipse-standard-kepler-SR1-macosx-cocoa.tar.gz (32bit version of Eclipse’s Kerpler) into the ~/_Dev/_AngularJS folder.

I fired up eclipse, chose the ~/_Dev/_AngularJS/workspace as the workspace root and installed the Eclipse Grovy REPL Scripting Environment 1.6.0 (update site) and Angular-JS Eclipse Tooling (update site) plugins.

1) Creating an Angular JS Project

After restarting eclipse, I right-clicked on the Project Explorer view and chose the New -> Static Web Project menu item

… set AngularJS_Tests as the project name and clicked Finish

… switched to the Web Perspective

… with the Project Explorer view now looking like this:

With the final setup being the conversion into an AngularJS Project

2) Creating the The_Basics.html file

To create the first test file, I right-clicked on the Web Content folder, and chose the New -> Html File menu option:

… set the file name to The_Basics.html and click on Finish

NOTE: The reason this first file is called The_Basics.html is because I’m going to be using the examples from AngularJS’ home page http://angularjs.org/

Once the The_Basics.html file opens up in eclipse

… I change its contents to the code sample from http://angularjs.org/

Note how the AngularJS Eclipse plugin successfully detects the Angular attributes and showed relevant information about it.

Here is ng-app:

Here is ng-model:

3) Fixing the undefined Attribute name issue

Since I used Eclipse’s Static Web Project, when I saved the The_Basics.html file, the following error occurred (if the Eclipse’s Html validation settings are the default ones):

… which basically means that Eclipse is not recognising the AngularJS Html attributes:

To fix this, I went to the AngularJS_Test project’s Properties, opened the HTML Syntax page (from the Validation section) and set to false the Undefined attribute name **setting (in the **Attributes options , not the Elements one)

With that config change, there are no problems in this page, and hovering on top of one the AngularJS directives will show the correct tooltip:

4) Viewing and previewing the The_Basics.html page

Since at the moment we only have one page, we can view it directly without needing a Web Server.

To do that, I clicked on the html file and chose the Web Browser option from the Open With menu:

This will open the default Eclipse Browser

… with the AngularJS test working as expected (in this case any text typed in the Name TextBox will automatically be shown in the page:

We can also preview some of the changes in real time, by choosing the Web Page Editor:

… which will look like this (note the non-processed HTML at the top and the HTML code at the bottom):

Opening up the Preview tab (from the default Design tab) will allow us to test the page currently being edited (note how Angular JS is working):

This means that (when in the Design tab) we can edit the AngularJS HTML page and see the changes immediately:

NOTE: This version of the Web Page Editor **doesn’t render the CSS while in that **Design mode, which means that if we add bootstrap to this project:

… the CSS will only be visible when in the Preview tab:

4) Creating a Git Repository for the files created

The best way for me to share these files is via a Git Repository, so the final step of this post is to create one on the files we have already created.

Since we are in eclipse I tried to create an Git Repo for the current project:

But the Git Repository wizard:

… didn’t work, because it expects the target folder to not exist:

This was easily solved via the command line (by executing $ git init on the AngularJS_Tests folder)

Now that we have a git repository, I was time to open it:

… using the_ Git Repositories_ view

… where we can use the Add an existing local Git repository link:

… to open the repository just created:

In order to create a git commit, I also opened the Git Staging view:

This is what these two git views look like (note that there are no commits/references and the list of new files in the Unstaged Changes list)

To commit the files drag-n-drop them from the Unstaged Changes to the Staged Changes, and write a commit message:

After clicking the Commit button the Git Repositories view will give a visual representation of the location current HEAD (which is the commit just done)

1.3 Using AngularJS in Eclipse, Part 2) Add Some Control

This covers the Add Some Control example from AngularJS’s home page:

1) Creating the Html, CSS and JS Files

In order to keep the AngularJS_Tests repository better organized, lets put each sample in its own folder.

After moving the The_Basics.html file into its own folder, I created a new folder:

… called Add Some Control to the WebContent folder of the AngularJS_Tests project

And inside it, I created the index.html, todo.js and todo.css files (each using the Eclipse default template for the respective file type)

Here is what these 3 files look with the default content:

Here is what they look like with the content from the AngularJs.org Add Some Control sample:

2) Opening up in Web Browser

To see the example in action, right-click on the /Add Some Control/index.html file, and chose the Web Browser option form the Open With menu

… which will look like this:

The objective of this sample is to add new Todos to the list shown, using the TextBox provided and the add button

So in the example shown above, after entering ‘This is a new ToDo’ on the TextBox and clicking add a new item will be added to the list (see below)

We can also remove Todos by using the checkboxes (see above) and clicking on the archive link (see below for the result)

2) Making some changes to the code to see AngularJS in $scope in action

To have a better understanding of what is going on, let’s make some changes to the code sample.

Here is the original code

… which I’m going to modify by adding a new paragraph containing the value of the todoText variable (note that the todoText variable is used on the input HTML element, auto-wired to Angular by using the ng-model attribute).

After refreshing the browser, we can see this in action by typing some text on the TextBox and seeing it show in real time after the ‘New Todo text:’ text (one of the features of AngularJS is to keep all these variables in sync):

3) Changing default Todo(s)

As another test, let’s add a new default todo to the original version of the $scope.todos array (see below).

What is happening is that the $scope.todos variable is populated when the TodoCtrl is executed (part of the page build), which is then inserted into the webpage using the <li ng-repeat="todo in todos"\> HTML code (see screenshot of index.html above).

Here is the new $scope.todos array entry added:

… which can be seen in action by refreshing the browser (note that it is already checked by default, since I set the done variable to true)

4) Changing default value of TextBox (after adding new Todo)

To show that we can control the $scope variables from anywhere in the TodoCtrl controller, here is an example where I’m changing the $scope.todoText to have a specific value after the add button is clicked (which is wired to the $scope.addTodo using the <form ng-submit=”addTodo()”> HTML code)

After refreshing the web browser, we can see that if we add a new todo:

… the TextBox (and paragraph below) will have the value ‘Default value’ (vs the original behavior of being empty)

4) Creating an ‘impossible to archive’ Todo

An interesting variation is to change the default value of the $scope.todos on the $scode.archive method, which, instead of being empty:

… it contains one entry:

… which means that (after refresh) and clicking on the archive link

… the WILL be added on Archive todo will now exist:

… and even if we try to archive all:

… the WILL be added on Archive todo will still be there:

5) Adding a Message label

As a final example, lets modify change the variable shown in the extra paragraph added to index.html to be message (which will be wired to $scope.message):

… and lets use that variable to provide visual feedback to the user when the TodoCtrl has executed:

Now, we should see the message ‘AngularJS controller is all set’ just after the page finishes reloading:

To make it more relevant, lets add a $scope.message to the $scope.addTodo method:

… and the $scope.archive method:

Now we will get this message when the page reloads:

… this message after clicking on the add button

… this message after clicking on the archive link

Finally let’s refactor the $scope.addTodo method to so that it doesn’t add a new Todo when no value is provided:

After reloading the page, we can confirm that trying to click on the add button without entering first any text on the TextBox will show this message:

… and if we add a new one (in this case ‘not an empty todo’) we will see this message:

1.4 Using AngularJS in Eclipse, Part 3) Wire up a Backend

This covers the Wire up a Backend example from AngularJS’s home page:

1) Creating the test files

First step is to create a folder:

… called Wire a Backend

… to hold the 4 files required for this example: index.html, detail.html, list.html and project.js

Here is what they look like (with content from http://angularjs.org)

index.html :

project.js :

list.html :

detail.html :

2) Running in Browser and noticing CSS problems

To see this example in action, lets open it in Eclipse’s WebBrowser:

… which looks like this:

… with a search bar at the top that can be used to filter the loaded data:

Only problem is that it doesn’t look at all like it does on the http://angularjs.org page (see below)

Note how not only the css styles are different, the add and edit links (i.e. icons) are missing:

Here is what the edit page should look like:

Here is what the new page should look like:

3) Running example on JSFiddle

On the http://angularjs.org page there is a button called Edit Me (top right) which when clicked will do a cross-site POST submission to http://jsfiddle.net/api/post/library/pure/

… which looks like this (notice the extra Bootstrap css included that is not present on the code sample provided in the http://angularjs.org page )

For reference here is what the Edit Me form looks like in the http://angularjs.org page (with the values passed as hidden parameters to jsfiddle)

And here is the jsfiddle API description for the method used (http://doc.jsfiddle.net/api/post.html)

4) Fixing CSS issue

Since I wanted to match the CSS of my local test page to the style used in the example embedded in the http://angularjs.org page, the best place to look is on the source code code of that page :)

Using Chrome Browser Inspector, on the Styles tab, I was able to see that they were using two css libraries: Bootstrap and Font Awesome:

Back in Eclipse, I added the bootstrap css reference

And on refresh, the page looked better (but the edit and add icons/links where still missing)

So I went back to the angularjs.org source code to see what they were using:

First I tried adding the latest Font Awesome from their CDN:

But since that didn’t work, I decided to add both font-awesome.css an docs.css references:

… with much better results (note the icons on the right)

But there was still something wrong, since the new page looked like this:

… and the edit page looked like this:

Since it was all good on the angular.org site, my next try was to use the bootstrap.min.css file from it (vs the CDN)

With the local page:

… and the edit page now looking just like the real thing (note the Search TextBox (above) and the fields (below))

That said, the layout where still a little bit different (namely the background of the local file which was white).

So I had a look at the style of the example in angular.org and noticed that there was a div element with the bootstrap classes well and ng-scope:

Back in the local file, I edited the index.html to also have a similar div:

And finally the local form looked just like the original:

5) Running local sample on multiple browsers

At this stage I was curious on what this form would look like on multiple browsers, and found out something interesting.

Here is the page on Safari (left) and Firefox (right), which are loaded ok:

But on Chrome, it doesn’t work at all, and the culprit is security!

Basically chrome does not allow Cross origin requests from html files loaded from the local disk (which does make sense from a security point of view).

As a final little comment, in case you noticed that on the Firefox screenshot the Angular content was correctly displayed but the icons didn’t show up, that much be a similar problem with loading Bootstrap or Font Awesome from the local disk

Here is the same example running on Firefox browser, so it works when loaded from http:

1.5 Using AngularJS in Eclipse, Part 4) Create Components

This covers the Create Components: example from AngularJS’s home page:

1) Creating the test files

First step is to create a folder called 4. Create Components:

… with the 4 files provided by this example:

index.html

components.js

app.js

and bootstrap.css

2) Running in browser (and failing to change locale)

Here is what it looks like when loaded locally (in Eclipse):

This example shows how we can create components (i.e directives ) that are able to handle multiple languages or currencies.

The Localization tab (shown above) is currently set for english (USA) and the Pluralization tab (shown below) is currently set to English.

This demo also has support for SK, but there didn’t seem to be an easy way to change the browser’s locale (so that we can see the SK values in action)

But on the angular.org page they were able to show both US and SK side by side:

so there had to be a way to do it :)

3) Programmatically change the locale

Looking at the code from the angular.org the code changes made below seem to be the ones required.

Basically we need to:

  • load up the angular-locale_sk.js _file followed by immediately creating a module called _ngLocal.sk
  • load up the angular-locale_en-us.js _file followed by immediately creating a module called _ngLocal.us
  • create a module called app-us that depends/consumes ngLocal.us
  • create a module called app-sk that depends/consumes ngLocal.sk

Then we can change the ng-app to be app-sk:

… and now the locale is SK (i.e. Slovak):

… with the text showing in Slovak (well I hope it does, since I don’t speak Slovakian :) )

If we wanted to have the site back in english (US), we can change the ng-app to be app-us

… and we’re back in USA currency and English language:

4) viewing Git Diffs in Eclipse

At this stage I wanted to see git diffs inside eclipse (for example ‘current changes vs last committed code’), but the option I was looking for (‘Compare With -> HEAD Revision’) was not there.

The problem was that I needed to ‘Share current project’, which can be done via the Team menu:

After choosing the Team -> Share Project … menu option, I was asked to chose the Git repository type (which is a bit weird since this was already a Git repository)

… and chose to use the repository in the parent folder of the selected project:

Once that completed, I was able to see the Compare With -> HEAD Revision option:

… which provides this really nice Git diff UI:

1.6 AngularJS code editor using UI-Bootstrap and CodeMirror (done without using jQuery)

I’m adding a number of AngularJS views to TeamMentor, and here is a simple HTML based source code editor inspired on the How to Integrate Codemirror With Angular UI post and ui-codemirror repository.

In the end, these are the APIs I used:

And this is what it looks like:

The source code editor is showing the contents of the current page (dynamically fetched using Angular $http.get) and the bottom yellow div is showing (in real-time) the contents of the source code editor:

What is nice about this example is that I didn’t use jQuery at all!

The great posts http://stackoverflow.com/a/15012542 and AngularJS for jQuery Developers explain why learning to code in Angular without JQuery is so important.

Basically, it’s better not have jQuery available, since them, the only way to solve a problem, is the ‘AngularJS way’ :)

How it works:

Here is a brief explanation of the code behind this PoC (included in full at the end of this page):

First we load the Javascript and CSS:

… them set up AngularJS module and codeMirror value

… use a controller to get the code to show (using $http.get) and assign it to the the_ $scope.code_ variable

… configure angularJS in the HTML by setting the textarea element to be a codemirror (linked to the $scope.code model)

… finally show the current value of $scope.code in side an bootstrap alert element

Complete Source code:

 1 <!DOCTYPE html>
 2 
 3 <html>  
 4     <head>  
 5         <title>CodeMirror with AngularJS</title>  
 6         <link href="/Content/bootstrap.min.css" rel="stylesheet">  
 7         <script src="/Scripts/angular.min.js" type="text/javascript"></script>  
 8         <script src="/Scripts/angular-ui.js" type="text/javascript"></script>  
 9         <script src="/Scripts/ui-bootstrap-tpls-0.3.0.min.js" type="text/javascript"></script>
10 
11         <link href="/Content/codemirror-3.01/codemirror.css" rel="stylesheet"/>  
12         <link href="/Content/codemirror-3.01/theme/rubyblue.css" rel="stylesheet"/>  
13         <script src="/Scripts/codemirror-3.01/codemirror.js" type="text/javascript"></script>\
14   
15         <script src="/Scripts/codemirror-3.01/mode/javascript.js" type="text/javascript"></sc\
16 ript>
17 
18 
19         <script type="text/javascript">  
20             var myApp = angular.module('myApp', ['ui', 'ui.bootstrap']);
21 
22             myApp.value('ui.config',  
23                 {  
24                     codemirror:  
25                         {  
26                             mode: 'javascript',  
27                             lineNumbers: true,  
28                             matchBrackets: true,  
29                             theme: 'rubyblue'  
30                         }  
31                 });
32 
33             function codeCtrl($scope,$http)  
34             {  
35                 $scope.docLocation = document.location.href;  
36                 $http.get($scope.docLocation)  
37                      .success(function (data)  
38                         {  
39                             $scope.code = data;  
40                         });  
41                 //$scope.code = "var a = 'somecode'; \n//which also shows above</h1>";  
42             }  
43         </script>  
44     </head>  
45     <body ng-app="myApp">  
46         <div class="well well-large">  
47             <div class="container">  
48                 <h2>CodeMirror working with AngularJS and Bootstrap</h2></div>  
49             </div>  
50             <div ng-controller="codeCtrl">  
51                 <div class="container">
52 
53                 <h4>Code Editor:</h4>  
54                 <p>With the the contents of this page (i.e.: {{docLocation}} )</p>
55 
56                 <textarea ui-codemirror ng-model="code"></textarea>
57 
58                 <br/><hr/><br/>
59 
60                 <h4>Bootstrap alert style</h4>  
61                 <p>  
62                     Showing in real time the contents of the code shown above (make a change \
63 to try it)  
64                 </p>  
65                 <alert type="success">{{code}}</alert>  
66             </div>  
67         </div>  
68     </body>  
69 </html>  

2. KarmaJS

This section has the following chapters:

  • A small AngularJS Jasmine test executed by KarmaJS
  • Creating an Eclipse UI to run AngularJS e2e tests using Karma
  • Running KarmaJS’s AngularJS example test/e2e/angular-scenario (on Chrome)

2.1 A small AngularJS Jasmine test executed by KarmaJS

When I try to understand how a particular technology works I always like to create a simple test case with a small number of moving parts.

This post shows such example for an AngularJS page, a Jasmine test, a NodeJS web server and a KarmaJS execution cycle.

The files used/customised were based on the KarmaJS test/e2e/angular-scenario example:

Let’s look at each file’s contents and what they do

1) angular.min.js is the latest version of AngularJS

2) index.html is a simple AngularJS example:

 1 <!DOCTYPE html>  
 2 <html xmlns:ng="http://angularjs.org" id="ng-app" ng-app>  
 3 <head>  
 4   <meta charset="utf-8">    
 5   <title>Sample Angular App</title>  
 6   <script src="angular.min.js"></script>  
 7 </head>  
 8 <body>  
 9   <div>  
10     <label>Name:</label>  
11     <input type="text" ng-model="yourName" placeholder="Enter a name here">  
12     <hr>  
13     <h1>Hello {{yourName}}!</h1>  
14   </div>  
15 </body>  
16 </html>

3) karma.conf.js is a reduced to the normal KarmaJS config file

 1 module.exports = function(karma)   
 2     {  
 3         karma.configure(  
 4               {  
 5                 // generic  
 6                 frameworks    : ['ng-scenario'],                          
 7                 urlRoot        : '/__karma/',          
 8                 autoWatch    : true,                                  
 9                 plugins     : ['karma-ng-scenario'],
10 
11                 //project specific  
12                 proxies : { '/': 'http://localhost:8000/'},   
13                 files : ['singleTest.js'],
14 
15                 //run specific   
16                 singleRun : true,   
17               });  
18     };  

4) singleTest.js checks if AngularJS is working as expected

 1 /** A Sample Angular E2E test */
 2 
 3 describe('SimpleTest', function()
 4 {
 5 
 6     it('A small Angular Test', function()
 7         {  
 8             browser().navigateTo('/index.html');  
 9             input('yourName').enter('A Pirate!');  
10             expect(element('.ng-binding').text()).toEqual('Hello A Pirate!!');  
11         });   
12 });  

5) server.js is a working Web NodeJS server (also reduced for easier reading):

  1 var sys = require('sys'),  
  2     http = require('http'),  
  3     fs = require('fs'),  
  4     url = require('url'),  
  5     events = require('events');
  6 
  7 var DEFAULT_PORT = 8000;
  8 
  9 function main(argv)
 10 {  
 11     new HttpServer({  
 12                         'GET': createServlet(StaticServlet),  
 13                         'HEAD': createServlet(StaticServlet)  
 14                     }).start(Number(argv[2]) || DEFAULT_PORT);  
 15 }
 16 
 17 function escapeHtml(value)
 18 {  
 19     return value.toString().  
 20     replace('<', '&lt;').  
 21     replace('>', '&gt').  
 22     replace('"', '&quot;');  
 23 }
 24 
 25 function createServlet(Class)
 26 {  
 27     var servlet = new Class();  
 28     return servlet.handleRequest.bind(servlet);  
 29 }
 30 
 31 /**  
 32 * An Http server implementation that uses a map of methods to decide  
 33 * action routing.  
 34 *  
 35 * @param {Object} Map of method => Handler function  
 36 */  
 37 function HttpServer(handlers)
 38 {  
 39     this.handlers = handlers;  
 40     this.server = http.createServer(this.handleRequest_.bind(this));  
 41 }
 42 
 43 HttpServer.prototype.start = function(port) {  
 44     this.port = port;  
 45     this.server.listen(port);  
 46     sys.puts('Http Server running at http://127.0.0.1:' + port + '/');  
 47 };
 48 
 49 HttpServer.prototype.parseUrl_ = function(urlString) {  
 50     var parsed = url.parse(urlString);  
 51     parsed.pathname = url.resolve('/', parsed.pathname);  
 52     return url.parse(url.format(parsed), true);  
 53 };
 54 
 55 HttpServer.prototype.handleRequest_ = function(req, res)
 56 {  
 57     var logEntry = req.method + ' ' + req.url;  
 58     if (req.headers['user-agent'])
 59     {  
 60         logEntry += ' ' + req.headers['user-agent'];  
 61     }  
 62     sys.puts(logEntry);  
 63     req.url = this.parseUrl_(req.url);  
 64     var handler = this.handlers[req.method];  
 65     if (!handler)
 66     {  
 67         res.writeHead(501);  
 68         res.end();  
 69     } else
 70     {  
 71     handler.call(this, req, res);  
 72     }  
 73 };
 74 
 75 /**  
 76 * Handles static content.  
 77 */  
 78 function StaticServlet() {}
 79 
 80 StaticServlet.MimeMap =
 81     {  
 82         'txt': 'text/plain',  
 83         'html': 'text/html',  
 84         'css': 'text/css',  
 85         'xml': 'application/xml',  
 86         'json': 'application/json',  
 87         'js': 'application/javascript',  
 88         'jpg': 'image/jpeg',  
 89         'jpeg': 'image/jpeg',  
 90         'gif': 'image/gif',  
 91         'png': 'image/png',  
 92         'manifest': 'text/cache-manifest'  
 93     };
 94 
 95 StaticServlet.prototype.handleRequest = function(req, res)
 96     {  
 97         var self = this;  
 98         var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(mat\
 99 ch, hex)
100             {  
101                 return String.fromCharCode(parseInt(hex, 16));  
102             });     
103         var parts = path.split('/');
104 
105         fs.stat(path, function(err, stat)
106             {  
107                 if (err)  
108                     return self.sendMissing_(req, res, path);  
109                 return self.sendFile_(req, res, path);  
110             });  
111     };
112 
113 StaticServlet.prototype.sendFile_ = function(req, res, path)
114     {  
115         var self = this;  
116         var file = fs.createReadStream(path);  
117         res.writeHead(200,
118         {  
119             // CSP headers, uncomment to enable CSP  
120             //"X-WebKit-CSP": "default-src 'self';",  
121             //"X-Content-Security-Policy": "default-src 'self'",  
122             'Content-Type': StaticServlet.  
123             MimeMap[path.split('.').pop()] || 'text/plain'  
124         });  
125         if (req.method === 'HEAD')
126         {  
127             res.end();  
128         } else
129         {  
130             file.on('data', res.write.bind(res));  
131             file.on('close', function()
132             {  
133                 res.end();  
134             });  
135             file.on('error', function(error)
136             {  
137                 self.sendError_(req, res, error);  
138             });  
139         }  
140     };
141 
142 StaticServlet.prototype.sendError_ = function(req, res, error)
143     {  
144         res.writeHead(500, {  
145                                 'Content-Type': 'text/html'  
146                            });  
147         res.write('<!doctype html>\n');  
148         res.write('<title>Internal Server Error</title>\n');  
149         res.write('<h1>Internal Server Error</h1>');  
150         res.write('<pre>' + escapeHtml(sys.inspect(error)) + '</pre>');  
151         sys.puts('500 Internal Server Error');  
152         sys.puts(sys.inspect(error));  
153     };
154 
155 StaticServlet.prototype.sendMissing_ = function(req, res, path)
156     {  
157         path = path.substring(1);  
158         res.writeHead(404, {  
159                                 'Content-Type': 'text/html'  
160                             });  
161         res.write('<!doctype html>\n');  
162         res.write('<title>404 Not Found</title>\n');  
163         res.write('<h1>Not Found</h1>');  
164         res.write('<p>The requested URL ' + escapeHtml(path) +  
165                   ' was not found on this server.</p>');  
166         res.end();  
167         sys.puts('404 Not Found: ' + path);  
168     };
169 
170 // Must be last,  
171 main(process.argv);  

To see this in action:

1) start the web server using: start node server.js

2) start karma with: karma start karma.confi.js

3) on a local browser open: http://localhost:9876/__karma

… which will execute the test

Since we have singleRun set to true (in karma.conf.js), the karma process ends after each execution, which means that the captured browsers will go into a ‘wait state’ (i.e. waiting for another karma server to come back to life)

4) notice that back in karma, the tests executed ok:

So here it is, a petty small example of a really powerful combination of technologies (and UnitTests workflows)

Helper Tool

While writing this post, I wrote an O2 Platform C# script which looked liked this:

This UI allowed me (in the same window) to make code changes and see its impact.

Here is the C# code of this script:

 1 var topPanel = "PoC - Karma and Angular run".popupWindow(1200,700);  
 2 //var topPanel = panel.clear().add_Panel();  
 3 //var textBox = topPanel.clear().add_RichTextBox();  
 4 var codeDir  = @"E:\_Code_Tests\AngularJS\angular-scenario";  
 5 var karma      = @"C:\Users\o2\AppData\Roaming\npm\node_modules\karma\bin\karma";
 6 
 7 var testPage = "http://localhost:8000/index.html";   
 8 var testRunner = "http://localhost:9876/__karma/";
 9 
10 var karmaConfig = codeDir.pathCombine("karma.conf.js");  
11 var serverConfig = codeDir.pathCombine("server.js");  
12 var unitTestFile = codeDir.pathCombine("e2eSpec.js");
13 
14 Process karmaProcess = null;
15 
16 Action startWebServer =   
17     ()=>{  
18             Processes.startProcessAndRedirectIO("node", serverConfig,codeDir,(line)=>line.inf\
19 o());   
20         };
21 
22 Action runKarma =   
23     ()=>{  
24             Action<string> consoleOut =   
25                 (consoleLine)=> consoleLine.info(); //textBox.append_Line(consoleLine);
26 
27             var command = "start \"{0}\" ".format(karmaConfig);  
28             karmaProcess = "node".startProcess("\"" + karma + "\" " + command, consoleOut);   
29         };
30 
31 if (testPage.GET().notValid())  
32 {  
33     "Staring WebServer".info();  
34     startWebServer();  
35     1000.wait();  
36 }  
37 
38 var toolStrip = topPanel.insert_Above_ToolStrip();  
39 var codeEditor_Test = topPanel.add_SourceCodeEditor();  
40 var ie_UnitTestRunner = codeEditor_Test.insert_Right().add_WebBrowser_with_NavigationBar();  
41 var ie_Site = ie_UnitTestRunner.insert_Below().add_WebBrowser_with_NavigationBar();  
42 var codeEditor_Config = codeEditor_Test.insert_Below().add_SourceCodeEditor();
43 
44 codeEditor_Test.open(unitTestFile);  
45 codeEditor_Config.open(karmaConfig);  
46 ie_Site.open(testPage);  
47 ie_UnitTestRunner.open(testRunner);  
48 toolStrip.add_Button("Run","btExecuteSelectedMethod_Image".formImage(),()=>runKarma());
49 
50 
51 runKarma();
52 
53 //using System.Diagnostics

2.2 Creating an Eclipse UI to run AngularJS e2e tests using Karma

This post shows how I created a nice set of views in Eclipse to quickly see the execution result of AngularJS e2e (end-to-end) tests, without leaving the Eclipse UI.

The image below shows this UI in action, where:

  • The source code of the test is shown in the Eclipse Java editor
  • Just below is the console out of the Karma runner (which is detecting files changes)
  • On the top-right is the hooked browser (i.e. the one that will run the tests)
  • On the middle-right is the simple AngularJS Hello World page
  • On the bottom-right is the debug view of the hooked browser (which is what you get if you click on the Debug Button included on the top-right page)

Here are the tools and Eclipse Plugins used to create this UI:

Although the AngularJS and NodeJs Eclipse plugins provide nice helpers and views, they didn’t provide (yet) the kind of UI that I was looking for. Namely they didn’t support the use of KarmaJS to run AngularJS tests.

But since I now have the ability to quickly manipulate and create new Eclipse views without restarting Eclipse (using the Groovy REPL script environment ) it was not very hard to create the UI that I wanted (see Eclipse Plugin that allows the execution of REPL Groovy Scripts (in the current Eclipse Instance) and Fluent API for Eclipse+SWT).

Basically the brief was to:

  • Create new (Eclipse) view with a browser showing http://localhost:9879/__karma/ (the KarmaJs browser runner/hook)
  • Create new view with a browser showing http://localhost:9879/index.html (the AngularJS page)
  • Create new view with a browser showing http://localhost:9879/__karma/debug.html (debug view of Karma runner), with an toolbar button to refresh it.

Here is the Groovy code* with the code that creates this 3 Eclipse views:

 1 eclipse.views.create("Karma Runner")
 2          .clear()
 3          .add.browser()
 4          .open("http://localhost:9879/__karma/")
 5 
 6 eclipse.views.create("Website")
 7          .clear()
 8          .add.browser()
 9          .open("http://localhost:9879/index.html")
10 
11 def view      = eclipse.views.create("Karma Runner - debug ").clear()
12                                                                                              \
13    .set.layout.grid();
14 def runButton = view.add.toolBar()
15                 .add_Button("run", images.get("IMG_TOOL_REDO_HOVER"));
16 
17 def browser   = view.add.panel()
18              .set.layout.grid_Grab().add.browser();
19 
20 def openKarmaDebug = new Runnable() { public void run()
21     {
22         Thread.start {
23                 browser.open("http://localhost:9879/__karma/debug.html");
24                 view.refresh();
25                  };
26     }};
27 
28 runButton.onClick(openKarmaDebug);
29 
30 view.refresh();
31 
32 openKarmaDebug.run();
33 
34 
35 //Config:UIThread_False

Hopefully this script is easier to read (the idea of the Fluent API that I added to the Groovy REPL was allow the easy reading and understanding of scripts like this).

1) Setting up the environment

Now lets look at how this environment was setup:

We start with a simple AngularJS project test page that will just show the model-binding capabilities of AngularJS (yes I know that all those JS files should be in separate folders :) )

Then I opened up Groovy REPL script environment and wrote the Groovy script shown above:

Clicking on _Execute _will create 3 views:

a) the Karma Runner view:

b) the Website view:

c) the Karma Runner - debug view:

… which (as seen in the first screenshot of this post) looks like this:

Part of this UI (but not created using the Groovy script) is an Eclipse Console with the ‘Karma Runner process’ console out

The AngularJS tests were executed when the Karma Runner view was opened, because the Karma process (configured via Karma.config.js) is set to wait for new browser’s hooks/runners and (on connection or code changes) execute the configured tests.

2) Making changes to existing tests

To see Karma in action, let’s make a test fail :)

The current AngularJS page is basically just echoing/binding the contents of the Name TextBox into the H1 tag:

Here is the test that is currently being executed, which is an e2e test that runs on the browser (just like Selenium or Watin).

Hopefully this type of Fluent API is easy to read:

  • browser navigates to /index.html
  • value is inserted into the yourName input field (the TextBox)
  • the value of the binded element (.ng-binding) is checked to see if it matches

To make this test fail, let’s change just the yourName value:

Immediately after saving the test with the changes shown above, Karma refreshes the hooked browsers in order to trigger the execution of the tests.

And as the image below will show, there is now one failed test:

In cases like this, the Karma Runner - debug is very useful since it will show more details on what happened, and where the test failed:

Just to confirm that the page is actually being loaded and the tests are happening in a real browser instance, if we add an Javascript alert to the current test:

… we will get an alert box in the Karma Runner - debug (note that on the screenshot below we are seeing the image of the final state of execution of the previous test (in this case the ‘A small Angular Test’)

3) Hooking multiple browsers and only failing on Firefox

In order to run the tests in multiple browsers all we need to do is to open the http://localhost:9879/__karma/ page (in the target browser), and a test execution will be triggered.

Note how on the image below:

  • Eclipse is in the background (containing the views previously created and showing the console out of the Karma test runner process)
  • There are 3 stand lone browser windows (Chrome, Safari and Firefox)
  • Firefox (top most window) shows the tests being executed (in green the test executed, in yellow the test being executed)

After execution, a quick look at the Karma runner shows that the modified test failed (as expected) on all browsers:

Just to make sure all is working as expected, let’s create a test that will fail only in one browser.

For example Firefox is the only one that has defined the navigator.userAgent.contains Javascript function (so we can use this to detect Firefox, and create a test that will only fail if executed inside it):

After saving the changes, the tests will be executed on all 4 browsers, with the one test that failed being the one executed in Firefox:

Refreshing the Firefox debug runner, shows the failed test and assert:

4) Setup

In addition to creating the views programmatically, I also setup an Eclipse Run configurations for NodeJS and an External Tools Configuration for KarmaJS.

The NodeJS configuration was done here:

… where I created a Node Application runner that was pointed to server.js

… which contains a simple NodeJS web server (we could also had used NodeJS Express, but that would had added a lot of other ‘stuff’ to this project)

The KarmaJS config was done as an External Tools Configuration

… where I simply run the start.sh bash file

… which starts KarmaJS:

… using this simple _karma.conf.js configuration

]

Note: I started the Karma process like this because there was an issue with KarmaJS finding the NodeJS executable from Eclipse (and at the time I didn’t had time to debug why that was happening)

External References:

2.3 Running KarmaJS’s AngularJS example test/e2e/angular-scenario (on Chrome)

To learn and get an idea of how Karma (the ‘Spectacular Test Runner for JavaScript’) works, and how it can be used to create browser automations tests, here are the steps I took to get the test/e2e/angular-scenario example to work.

It all started with a clone of: git@github.com:karma-runner/karma.git

With this KarmaJS test example (see below), being the one that we are going to use:

I then opened an nodejs command prompt and navigated to the folder shown above:

… used node server.js to start a local webserver

… on port 8000:

The test case we are using (on KarmaJS’s test/e2e/angular-scenario) is a simple AngularJS example, which just consumes the angular-min.js file model attribute:

… and uses angular to populate the {{yourName}} value dynamically (wired to the input field via the ng-model=”yourName”)

Next we are going to run this Jasmine test using KarmaJS

… which should work with just: karma start karma.conf.js

… but it didn’t

There is a module dependency missing, which in this case can be resolved by running this command from the root of the karma repository:

Note: the issue above was caused by the fact that I had an the official released version of karma installed globally which is the one that was running when I tried it on the ‘test/e2e/angular-scenario’ folder’

And now (based on an option from the karma.conf.js) a Chrome window opens up:

Clicking on the Debug button:

… we can more details on the test that failed:

In this case the problem is that the ‘proxy mapping’ that karma does is not correct

If we look at the karma.config.js file

…. and the unit test brower().navigateTo command

… we can see that karma will try to open /index.html from http://localhost:8000/test/e2e/angular-scenario/index.html

That is not going to work since the page we want it is on http://localhost:8000/index.html (which happened because we started node server.js on the …/test/e2e/angular-scenario folder)

One way to solved this, is to change the ‘proxies’ mapping to ’/’ : ‘http://localhost:8000/’

… and after stopping and starting the karma server:

all tests pass :)

Changing and executing tests in real time

What is really cool with this set-up is that (as long as the karma process is running), because the autoWatch value (in karma.config.js) was set to true, if I make a change to the test file:

… the karma runner will detect the changes and rerun the tests (note that there are 2 tests executed now)

This 2nd test shows an interesting behaviors since it will make the test wait for 15 seconds (with the browser window opened):

Note how the time execution time for the 2nd test was ~15 secs

And if we modify the 2nd test to:

… the execution test UI will look like this (note that the execution was triggered when I saved the test file :)

NOTE 1: to solve the ENOENT error shown the first screenshot of localhost:8000, we need to add a favicon.ico file to the lib/nodeserver folder

… and now the error doesn’t happen anymore

Note 2: when trying to run Karma for the first time, I had a prob with grunt where it was failing with an Error: spawn ENOENT:

… this was resolved by installed the 32bit version of nodeJS and running npm install on the karma folder (after cloning it)

3. Firebase

This section has the following chapters:

  • First PoC of sending server-side request URLS to Firebase (and seeing it in realtime in an AngularJS page)
  • Trying out Firebase (Beta) hosting solution and good example of Firebase Security rules

3.1 First PoC of sending server-side request URLS to Firebase (and seeing it in realtime in an AngularJS page)

After getting my head around how Firebase works (see Using Firebase to sync data with a webpage (via Javascript, REST and Firebase Admin panel) and Trying our Firebase (Beta) hosting solution and good example of Firebase Security rules), I really wanted to see how it could work on a key feature that I’ve been wanting to add to TeamMentor for ages: Realtime viewing of traffic and logs

And it worked :)

This is really exciting!!! (can you tell :) ), specially since I can see so many great uses of this type of technique and technology in TeamMentor (for example it will allow for much better understanding on how the content is used, and for better collaboration between its readers (and authors))

I’m going to write a number of blog posts that explain how this works in detail, but the core of it is the C# code shown below (gist here) which is running on a test TeamMentor instance, and basically hooks into the ASP.net HTTP pipeline in order to send the current URL to Firebase (using Firebase’s REST API):

 1 Action<string,string,string,string> sendData_Raw =
 2 	(app,token, area, data)=>
 3         	{
 4                      ThreadPool.QueueUserWorkItem((o)=>
 5                        	{
 6 		               var url      = "https://{0}.firebaseio.com/{1}.json?auth={2}".format(app, ar\
 7 ea, token);
 8                 	       var now 	    = DateTime.Now.ToShortTimeString(); // DateTime.Now. Tim\
 9 eOfDay;
10 		               var postData = "\"{0}: {1}\"".format(now, data.replace("\"", "'"));
11 			       url.POST(postData);
12                         });
13                 };
14 
15 Action<string,string> sendData =
16 	(area, data) => {
17 				var app       = "tm-admin-test";
18 				var authToken = "11uXXuQHpzhrG2LzV1DNu17tOBu0psTqR6bNhFZm";
19 				sendData_Raw(app, authToken, area, data);
20                     	};
21 
22 Action<string> logDebugMsg =
23 	(message)=>{
24 			sendData("debugMsg", message);
25         	   };
26 
27 Action<string> logRequestURL =
28 	(url)=>{
29 			sendData("requestUrl", url);
30                };
31 
32 if (TMEvents.OnApplication_BeginRequest.size() > 1)
33     TMEvents.OnApplication_BeginRequest.remove(1);
34 
35 logDebugMsg("mapping TMEvents.OnApplication_BeginRequest to send url to Firebase");
36 
37 TMEvents.OnApplication_BeginRequest.Add(
38 	()=>{
39 		logRequestURL(HttpContextFactory.Request.Url.str());
40             });
41 return TMEvents.OnApplication_BeginRequest.size();
42 
43 //using System.Threading;
44 //using TeamMentor.CoreLib;
45 //O2Ref:TeamMentor.CoreLib.dll

Here is the AngularJS+Firebase Html app that I created in Eclipse which shows the data received from the TeamMentor website (i.e. all requests made by a client visiting its home page):

… here are the logs for a page that doesn’t exist:

… here are the requests for the TeamMentor error page:

Quick look at the C# code executed on the TeamMentor server (gist here):

Here is how the HTTP pipeline is hooked (using the TMEvents helper object from TM)

… the logDebugMsg and logRequestURL lambda functions are used to set the Firebase object to store the received messages:

… the sendData lambda function is used to configure the Firebase target app and authorization token:

… the sendData_Raw lambda function is the one that sends the REST/POST request to the mapped Firebase app:

Let me know if you have any questions regarding this example.

Firebase recovers connection

In terms of being able to recover from going offline, Firebase seems to do a good job at reconnecting back to the server once the host box is back online (see image below which happened between me leaving my house and connecting into my current WIFI location)

3.2 Trying out Firebase (Beta) hosting solution and good example of Firebase Security rules

Since Firebase now offers a Beta hosting service (and I was looking for a quick way to publish one of the firebase PoCs I’m working at the moment), I decided to take it for a spin.

I have to say that I’m really impressed with the end result, and as you will see below, there entire process (from install to published website) was really smooth.

Note 1: in the example below I already had created an Firebase app to hold the data (see Using Firebase to sync data with a webpage (via Javascript, REST and Firebase Admin panel) for details on how to create one)

Note 2: at the time I wrote this post, the website created is/was (depending on when you are reading this) hosted at https://tm-admin-test.firebaseapp.com/

Starting with the instructions from Firebase hosting page:

1) Install the firebase tools

….

2) Run the firebase BootStrap

… after logging in (above), we are asked to chose the ‘firebase’ app to use (below)

… then pick the template to use (I chose the angular one)

… and that’s it:

Here are the files created locally (all under the tm-admin-test folder)

3) Deploy the application

… which is also super quick.

After completion the default browser is opened with the created website, which looked like this:

4) Testing the test app

The created/published test app has a simple Firebase 3-way data binding example, where changes in an angular-binded model, are propagated to the server, and distributed to any connected clients (the image below shows two browsers and the firebase admin panel, all synced in real time (regardless where the data is changed))

There is a basic Chat page:

… which allows new messages to be added and viewed in real time:

Also included is a Login page (with account creation capabilities):

5) Review the security rules

At this stage it’s good to take a look at the security rules added to this Firebase app.

In the image below we can see that:

  • Anonymous users can read all data, but not write by default
  • The syncedValue object can be read and written by all
    • there is a validation on this field that ensures that it is not empty and not bigger that 100 chars
  • The messages object (i.e. ‘firebase section/subdomain’):
    • can be read by all
    • each message must include a property called_ text_ with a max length of 1000 chars
    • (I think) the .validate = false means that no other properties can be added
  • the users object:
    • each $user child object:
      • can be read if the current user matches its name (i.e. it is logged in as that user)
      • each user can write into two fields: name and email (both with a max length of 2000)

6) Create an account

To test the provided authorization solution, let’s start by trying to login with an account that doesn’t exist:

… then click on the Register button (which just adds a confirm pass field)

Here is what the ‘post register/login’ page looks like (note that the top-menu-bar login link was also changed to account)

Clicking on home takes us to the first page, which also shows a different message (now that we are logged in)

Interestingly the Chat page doesn’t seem to take into account that we are logged in now (would be nice to show the current user in the message posted)

6) Take a look at data (as stored in the assigned Firebase app)

Using the control panel for the current app:

a) here is the syncedValue object

b) here is the messages object (which is a firebase kind-of-array, based on a name/value pair. The name is the node text (for example_ -JGoCMZRRGo1WQHmYxmc) and the value is the child _text node value (for example _test _)):

c) There was a new users node/object in the root of the app’s data:

… which contains the user provided (and ‘editable by that user’) data (email and name)

And where is the user’s credentials?

The user created was added to the current list of ‘registered users’ , and it must be stored somewhere inside firebase servers since we don’t have access to it (hopefully it is stored using a nice strong hashing algorithm)

Wrapping up

Kudos to the Firebase team for providing such easy-to-use hosting solution (next step for me is to try to use it in the TeamMentor PoC I’m currently working on)

For reference, the AngularJS+Firebase website that was created by the Firebase cli tool, is the available at https://github.com/firebase/angularFire-seed (see README.md for more details)

4. Misc Tricks

This section has the following chapters:

  • Programatically changing an AngularJS scope variable and adding Firebug Lite to an AngularJs app
  • Hubspot current.js code includes JQuery on it
  • Submitting TM users to HubSpot via TBOT interface

4.1 Programmatically changing an AngularJS scope variable and adding Firebug Lite to an AngularJs app

In this post I’m going to show two really nice tricks that help when developing AngularJS applications:

  • adding Firebug Lite to the current browser
  • changing the scope value outside a normal AngularJS controller, service or module

Let’s say that we are inside Eclipse and have this simple AngularJS app (gist here)

… which looks like this when executed:

To add Firebug Lite to this page, all we need to do is to add a script reference to https://getfirebug.com/firebug-lite-debug.js

… and after refresh we will have a nice Firebug Lite console (and other nice goodies) at the bottom of our page :)

Next lets see how to access and change the AngularJS $scope of the ContactController.

The objective is to access programmatically the New Contact value (set below to Hello Firebug :) )

In Firebug Lite, we can access the scope object by executing: var scope = angular.element(document.body).scope()

… and the New Contact **value value using: **scope.newcontact

If we change the New Contact value here (in Firebug Lite) using scope.newcontact = “Hello AngularJS”

… we will notice that the value will not automagically (ala AngularJS way) change in the binded (via ng-model) input field

The reason that didn’t happen is because the change was done outside the AngularJS $digest cycle.

The solution is to call the scope.$apply() function (and the input field will be updated):

4.2 Hubspot current.js code includes JQuery on it

Although I’m using Angular.js on the HubSpot TBot page (see Submitting TM users to HubSpot via TBOT interface (using Angular JS) ) I’m still more comfortable/knowledgeable in jQuery, so I decided to use it to populate the HubSpot fields.

So my first action was to load jQuery at the top of the TBot page:

which allowed me to do this:

(note the mix of JQuery and AngularJS code)

But then as I looked through the http://js.hubspot.com/forms/current.js file (which you will also notice that for TM, I saved it on the /_Customizations folder)

In the Util - Javascript Format (and Beautify).h2 O2 Platform tool:

I noticed that they had embedded jQuery _** in the **_current.js code, namely at the window.hsJQuery variable

this means that we can use jQuery to access the page’s DOM

Now, if we remove the jQuery script import:

the $ variable will not be available:

But if we assign $ to window.hsJQuery, we are back in action:

Finally, we add that assignment to the TBot page

And the previous code (that used the $) is working again (now using the JQuery version from current.js)

4.3 Submitting TM users to HubSpot via TBOT interface

Following the need to submit TM new users to HubSpot, I just created an TBot razor page (TM admin script) that uses Angular JS to get data about a particular user and populates a form that can then be submitted to HubSpot.

Here is what the Form looks like for the admin user (value provided on the url):

Pressing the Submit button will send the data to HubSpot which is captured like this:

This works using the new HubSpot form API and Structure (which is quite nice).

The form is created using this HubSpot provided script:

Which is then auto-populated using Angular.js

5. IDEs

This section has the following chapters:

  • Eclipse Groovy REPL script to sync a Browser with file changes (with recursive folder search via Java’s WatchService)
  • Eclipse Groovy script to remove the ‘busy’ image from the WebBrowser Editor
  • Using Chrome inside a native VisualStudio pane (using Window Handle Hijacking)
  • Using WebStorm with Chrome and ChromeDriver (to view KarmaJS execution results)
  • When the best way to automate Chrome is to use … Chrome
  • Adding KarmaJS support to WebStorm to automagically run tests on file changes

5.1 Eclipse Groovy REPL script to sync a Browser with file changes (with recursive folder search via Java’s WatchService)

Since I am using Eclipse to develop using AngularJS (see Creating an Eclipse UI to run AngularJS e2e tests using Karma), I needed a way to refresh the browser window every-time I made changes to any AngularJS related file (note that due to the nature of the AngularJS projects, I need the change to trigger on any change made inside the root folder and all its subfolders).

Since there didn’t seem to be an easy way to do this (‘auto browser refresh on file changes’) in Eclipse, I used the Eclipse Grovy REPL Scripting Environment to develop a script/macro that:

  • Based on a title of an opened eclipse editor file:
  • … find the full path of that file, and:
  • … create a Java WatchService that monitors the file’s folder and subfolders, and:
  • … when a StandardWatchEventKinds.ENTRY_MODIFY is received :
    • Create/Open a new Eclipse view with a browser (called Synced Browser), and:
    • …refresh the index page

For reference here is the groovy code for this script (gist here):

Originally I had tried to use Eclipse file change events (like on this SO thread), but that didn’t work as well as the WatchService.

A next step is to create a mini UI to allow the configuration of the target files (maybe as a view added to the next version of the Groovy REPL Eclipse plugin)

Seeing it in action

Here is how to test this script:

1) create a Web project with an Html file on it:

2) run the Groovy code in the REPL window (note that the image below is using a different root file and the version of script is an older one (which didn’t contain the recursive folder monitoring)):

… on execution you will see a new view (called Synced WebBrowser) show up in your current Eclipse instance.

3) make some changes on the Html file

4) and note that the Synced WebBrowser view will be refreshed automatically (it takes about 500ms to 1s for the change to be picked up (see this SO answer for why I had to use the SensitivityWatchEventModifier.HIGH setting on the WatchService))

5) if you open the TeamMentor Console, you will also see a number of log messages that help to see what is going on:

6) here is another example where I added a new Bootstrap css div:

7) which was immediately (~500ms) shown on save

8) note that the the log message shows the events being triggered and the resetting of the WatcherService:

5.2 Eclipse Groovy script to remove the ‘busy’ image from the WebBrowser Editor

Now that I’m doing AngularJS and Firebase development inside Eclipse, there was a little ‘thing’ that was starting to drive me crazy: The animation icon on the top right of the Eclipse WebBrowser!

Apart from the mosaic 2000s look (which is kinda ok), there is a big problem with pages that keep running for a while: the animation doesn’t stop!

This means that if you are developing inside Eclipse, there is this ‘thing’ (i.e. the top right icon) that keeps moving and demand my brain’s attention:

The icons keep animating like this:

Since there didn’t seem to be an preference or option to disable this behavior, it was time to open up the Eclipse Grovy REPL Scripting Environment and fix it with a script :)

The solution

After a quick prototyping, I come up with this script to remove all icons from all opened WebBrowser editors (gist here):

After execution the icon is not there anymore :)

How I found the solution

Here are the steps I took to find the _busy _object to set the visible value to false

1) in the Grovy REPL I started by getting a list the ids of all current opened Eclipse views, but there didn’t seem to be any web browser in there:

2) then I looked at the list the ids of all current opened editors, and found one that sounded promising: org.eclipse.ui.browser.editor

3) to confirm that that was the one I wanted, I looked at the titles of the current editors, and confirmed that the browser window I want to capture was there (the title was “Angular with Firebase Lite”);

4) now that I knew the title, it was easy to get an EditorReference to it:

5) everytime I have an object reference that I want to take a look, I call the show({object}) viewer, since that will give me a nice TreeViewer of all methods, fields and properties ( see Viewing Eclipse and SWT objects (Workbench, Display and Shell) using Groovy’s ObjectBrowser and using TeamMentor’s Plugin ObjectBrowser for more details how this works)

6) in this case we don’t want the EditorReference object. What we really want is the actually editor, which can be retrieved by invoking the getEditor(true) method (note how the object we are currently seeing in the Object Browser is the org.eclipse.ui.internal.WebBrowserEditor)

Here is a better view of this object:

7) looking at the org.eclipse.ui.internal.WebBrowserEditor object, the one that called my attention was the webBrowser field (which is an org.eclipse.ui.internal.browser.BrowserViewer object)

8) Inside the webBrowser object I found the busy field (which is an org.eclipse.ui.internal.browser.BusyIndicator object)

9) and finally inside the busy object I found the visible property (ie the getVisible and setVisible methods)

10) back in the REPL, it was time to do the same thing programmatically.

First I got a reference to the webBrowser field:

… and then the busy field:

11) Once I had the reference to the busy field, it was easy to make it invisible (by just setting the visible property to false)

12) final step was to write a generic script that would work on all opened browser editor windows (a robust script would use the same trick I used in the integration with the Fortify plugin, where a callback is received on every new view opened (i.e. we could wait for new instances of the org.eclipse.ui.browser.editor to be opened, and apply the fix then))

To demonstrate the script in action, here it is the browser before:

… and here it is after:

Note: another way to to stop the constant animation was to just set the stop value to false, but this would only work for the current opened page (i.e. the animation would be back on page reload)

5.3 Using Chrome inside a native VisualStudio pane (using Window Handle Hijacking)

To help me debug and visualize an AngularJS page I was developing, I used the O2’s Window Handle Hijack technique to insert an Chrome window inside VisualStudio 2010.

Here it is in action:

On the right you can see a full chrome window, inserted inside a VisualStudio dockable pane.

On the left you can see the AngularJs file (rendered from a RazorSharp template) that I can edit and quickly view its output on the right-hand-side Chrome window (with no web recompilation needed)

To create this, I searched in O2 Platform for the Util - Win32 Window Handle Hijack (simple).h2 script

which looks like this:

Had a look at its source code to see how it was created (note the Extension Method add_Handle_HijackGui ):

Then inside a VisualStudio, I opened a C# REPL script:

This gave me access to the VisualStudio_2010 API

where I can use the Extension Method add_Handle_HijackGui

to create a native VisualStudio pane with the Windows Handle Hijack Gui

With this GUI, we can grab any Window’s Window, by dragging the target icon (top left) into the window we want to use/hijack:

Tip: before Hikacking a window, it is a good idea to take a screenshot and see if we have the right one:

Once we’re sure, just hit the Hijack link, and we will have have a fully functional Chrome window that we can place anywhere inside VisualStudio’s GUI.

For example, we can place it in the documents area as one of the source code files
(tip: double click on the ‘Hijacked Window/Control’ text to hide the hijack controls)

As a final example, here is what it looks like if we just Hijack the browser’s website window (without the navigation and top bars)

5.4 Using WebStorm with Chrome and ChromeDriver (to view KarmaJS execution results)

Following from the example described inWhen the best way to automate Chrome is to use … Chrome , here is a more practical scenario where I’m creating a GUI that has both WebStorm and Chrome running side-by-side

Here is what it looks like:

What makes this example practical is the KarmaJS auto execution on file change, just like it was described in Adding KarmaJS support to WebStorm to automagically run tests on file changes

Here is the code that creates this GUI (with some functionality since the last example, but it still needs to a bit of work to make it more robust):

 1 //var topPanel      = panel.clear().add_Panel();   
 2 var topPanel  = "Util - Chrome with WebStorm".popupWindow(1200,600);  
 3 var webStormPanel = topPanel.add_GroupBox("WebStorm").add_Panel();
 4 
 5 //we need to stop any existing WebStorm processes  
 6 foreach(var process in Processes.getProcessesCalled("WebStorm"))  
 7 process.stop().WaitForExit();
 8 
 9 // start webstorm   
10 var webStorm = @"C:\Program Files (x86)\JetBrains\WebStorm 6.0.2\bin\WebStorm.exe".startProce\
11 ss();
12 
13 
14 var chromeHijack = new API_Chrome_Hijack().open_ChromeDriver()  
15 .add_Chrome_To_Panel(topPanel.insert_Right());  
16 //.add_WebDriver_ScriptMe_To(replPanel);
17 
18 //wait for main window handle  
19 webStorm.waitFor_MainWindowHandle();  
20 //hack to deal with the splash screen  
21 3000.sleep();
22 
23 //hijack WebStorm window  
24 chromeHijack.hijack_Process_Into_Panel(webStormPanel,webStorm);
25 
26 chromeHijack.ChromeDriver.Navigate().GoToUrl("http://127.0.0.1:9876/__karma/debug.html");
27 
28 
29 //O2File:API_Chrome_Hijack.cs  
30 //O2Ref:WebDriver.dll  

When executed for the first time, this GUI looks like this:

Them if we start KarmaJS:

… a listener will start (waiting for browsers to connect):

Refreshing the browser will ‘capture it’ and manually execute the tests:

Opening up the normal page will start the ‘auto test execution loop’:

… and if we add a new test and save the file, the unit test execution will occour

Note: there is some kind of weird event race-condition happening and the keyboard key’s events are not being fired inside the captured WebStorm. So in the case shown above, I open the file to edit in a WebStorm popup window, where the keyboard events fire-up ok (and I was able to make the changes to the unit test file)

5.5 When the best way to automate Chrome is to use … Chrome (with examples on Google search, direct AngularJS scope manipulation and ChromeDriver javascript access)

On the topic of Web Automation, I always wanted to have a REPL environment for Chrome like I have for IE (using Watin).

In the past I have explored multiple solutions, for example the use of CefSharp (see here and here). But that was never the real thing, and there was always a couple issues (caused by the fact that the ‘real’ chrome wasn’t being used).

For a while, in the back on my mind the solution was simple and obvious: Use the real Chrome process in a way that it can be programmatically accessed from an O2’s C# repl environment!

Well, the good news is that is exactly what I have done :)

I just created the Gui you can see below, which uses the Window-Hikacking technique to inject an (Selenium’s ChromeDriver started) Chrome process’ window in a Panel, and pass its reference (as a variable) to an O2 REPL environment.

The script is called _ Util - Chrome Browser with REPL.h2_ and it has:

  • The cmd.exe window from the ChromeDriver.exe process in the bottom right
  • The Chrome window (started by the ChromeDriver) in the top right
  • A C# REPL (with the CromeDriver object passed in as a parameter) in the left

Now, on the left-hand-side C# REPL, I can write a script like this:

Which will:

  • Open (in Chrome) the http://www.google.com web page
  • Find the ‘q’ element (the search text input)
  • Send the Keys ‘O2 Platform’ to that element
  • Find the element ‘btnG’ (the search button)
  • Click on that element

Automating an AngularJS page

The next example is more interesting where we are going to open AngularJS page and programmatically change a $scope variable

Lets look at the code to see what is going on.

The first line opens up an the Url with the sample AngularJS example described here and here

… next I create a number of variables with the url and commands to execute:

… here:

It will probably be easier if I break these commands individually:

And this is all programmed in a nice REPL environment, which makes it really easy to try thing out.

For example, here are a couple interesting two way data exchange between the C# script and the
Chrome javascript:

1) get an int value

2) get an string value:

3) get an simple object (converted into dictionary)

4) get an array

5) get the document.location object

6) get the ‘this’ object

7) get the first document.element

8) get the html of the first document.element

9) show an alert

10) getting and using an Html element via ExecuteScript

which will populate the search text value and click on the search icon

What is cool about all these examples is that we are running the code on the local installation of Chrome, ie it is the actually chrome process that we are using, which is great for testing, debugging and developing :)

Scripts used in this post

A) C# code that created the GUI (start chromeDriver and chrome processes and hijack they main window)

 1 //var topPanel      = panel.clear().add_Panel();   
 2 var topPanel = "Util - Chrome with REPL".popupWindow(1200,600);
 3 
 4 var replPanel = topPanel.add_GroupBox("C# Repl").add_Panel();  
 5 var chromePanel = topPanel.insert_Right(replPanel.width() / 2 , "Chrome");  
 6 var chromeDriver = chromePanel.parent().insert_Below(150,"Chrome WebDriver");
 7 
 8 var firstScript =   
 9 @"chromeDriver.open(""http://www.google.com"");  
10 chromeDriver.FindElement(By.Name(""q""))  
11 .SendKeys(""O2 Platform"");  
12 chromeDriver.FindElement(By.Name(""btnG""))  
13 .Click();  
14 return ""done"";
15 
16 //using OpenQA.Selenium;  
17 //O2Ref:WebDriver.dll  
18 //O2File:API_ChromeDriver.cs";
19 
20 var chromeHijack = new API_Chrome_Hijack()  
21 .open_ChromeDriver();
22 
23 chromeHijack.ChromeDriver.script_Me(replPanel).set_Code(firstScript);   
24 var hijacked_Chrome = chromePanel.add_Handle_HijackGui(false)   
25 .hijackProcessMainWindow(chromeHijack.ChromeProcess);  
26 var hijacked_ChromeDriver = chromeDriver.add_Handle_HijackGui(false)   
27 .hijackProcessMainWindow(chromeHijack.ChromeDriverProcess);
28 
29 //O2File:API_Chrome_Hijack.cs  
30 //O2File:API_Win32_Handle_Hijack.cs
31 
32 //O2Ref:WebDriver.dll  

B) first code example (open Google and do a search)

 1 chromeDriver.open("http://www.google.com");  
 2 chromeDriver.FindElement(By.Name("q"))  
 3             .SendKeys("O2 Platform");  
 4 chromeDriver.FindElement(By.Name("btnG"))  
 5             .Click();  
 6 return "done";
 7 
 8 //using OpenQA.Selenium;  
 9 //O2Ref:WebDriver.dll  
10 //O2File:API_ChromeDriver.cs  

C) 2nd code example, open AngularJS page and programmatically change a $scope variable

 1 var url          = "http://localhost:12120/AngularJS/Tests/AngularJS/Simple.html";
 2 
 3 //var jQuery = "http://code.jquery.com/jquery-1.10.1.min.js".GET();  
 4 var jQuery = "jquery-1.9.1.min.js".local().fileContents();  
 5 var angular_Cmd1 = "scope = angular.element($('input').eq(0)).scope()";  
 6 var angular_Cmd2 = "scope.yourName='12345'";  
 7 var angular_Cmd3 = "scope.$apply()";
 8 
 9 var console_Cmd1 = "console.log('all done, hello should say 12345')";
10 
11 chromeDriver.Navigate().GoToUrl(url);
12 
13 chromeDriver.ExecuteScript(jQuery);
14 
15 chromeDriver.ExecuteScript(angular_Cmd1);  
16 chromeDriver.ExecuteScript(angular_Cmd2);  
17 chromeDriver.ExecuteScript(angular_Cmd3);
18 
19 chromeDriver.ExecuteScript(console_Cmd1);
20 
21 return "done";
22 
23 // doesn't work to open chrome's inspector  
24 // chromeDriver.Keyboard.SendKeys(OpenQA.Selenium.Keys.F12);
25 
26 //using OpenQA.Selenium;  
27 //O2Ref:WebDriver.dll  

D) last example where search filed was retrieved using two different techniques:

 1 //open Url  
 2 /*chromeDriver.ExecuteScript(  
 3      "document.location= 'http://localhost:12120'"); */
 4 
 5 //Getting the SearchTextBox object via ExecuteScript  
 6 var searchElement = (RemoteWebElement)chromeDriver.ExecuteScript(  
 7 "return document.getElementById('SearchTextBox')");  
 8 searchElement.Clear();  
 9 searchElement.SendKeys("Sql ");
10 
11 //Getting the SearchTextBox object via Selenium selector  
12 chromeDriver.FindElement(By.Id("SearchTextBox"))  
13 .SendKeys("Injection");
14 
15 //Click on search Button  
16 chromeDriver.FindElement(By.Id("ctl00_ContentPlaceHolder1_SearchControl1_SearchButton"))  
17 .Click();
18 
19 return "done";
20 
21 //using OpenQA.Selenium.Remote  
22 //using OpenQA.Selenium;  
23 //O2Ref:WebDriver.dll  

5.6 Adding KarmaJS support to WebStorm to automagically run tests on file changes (and test UI with SublimeText, Chrome and Cmd.exe)

On the AngularJs and KarmaJS theme (see A small AngularJS Jasmine test executed by KarmaJS and the related posts linked at the bottom), here is my first attempt at using Karma to test AngularJS code inside TeamMentor.

I’m using WebStorm instead of VisualStudio, since for Javascript coding WebStorm is MUCH better/faster/cleverer, specially since it has good support for AngularJs and Jasmine (with KarmaJS support easily added, as we are about to see).

Also shown below is a cool tool I created that hijacks windows from SublimeText, Chrome and Cmd.exe windows into the same UI (an O2 Platform .NET Script)

Here is the directory structure:

… with Karma.config.js looking like this:

 1 module.exports = function(karma)  
 2 {  
 3     karma.configure(
 4         {  
 5             frameworks: ['ng-scenario'],
 6 
 7             files:  
 8                         [  
 9                             '../Tests/**/*.Spec.js'  
10                         ],
11 
12             urlRoot: '/__karma/',
13 
14             autoWatch: true,
15 
16             proxies: {  
17                         '/' : 'http://localhost:12120/',  
18                         '/Tests': 'http://localhost:12120/AngularJs/Tests/'
19 
20                  },
21 
22            //browsers: ['Chrome'],
23 
24             reporters: ['dots'],    //reporters: ['progress'],  
25             plugins: [  
26                         'karma-ng-scenario',  
27                         'karma-chrome-launcher'  
28                      //,'karma-firefox-launcher'  
29                      ] ,  
30           //logLevel: karma.LOG_DEBUG  
31             logLevel: karma.LOG_INFO  
32         });  
33 };  

Simple.html like this:

 1 <!DOCTYPE html>  
 2 <html xmlns:ng="http://angularjs.org" id="ng-app" ng-app>  
 3 <head>  
 4     <meta charset="utf-8">  
 5     <meta http-equiv="X-UA-Compatible" __content="IE=EmulateIE9" />  
 6     <title>Simple AngularJS page</title>  
 7     <script src="/Javascript/angularJS/1.0.5/angular.min.js"></script>  
 8 </head>  
 9 <body>  
10 <div>  
11     <label>Name:</label>  
12     <input type="text" ng-model="yourName" placeholder="Enter a name here">  
13     <hr>  
14     <h1>Hello {{yourName}}!</h1>  
15 </div>  
16 </body>  
17 </html>

Simple.Spec.js like this:

 1 describe('Test Angular Simple page', function()  
 2     {  
 3         beforeEach(function()   
 4             {              
 5                 browser().navigateTo('/Tests/AngularJS/Simple.html');              
 6             });  
 7         it('Open Webpage and check URL', function()  
 8             {                  
 9                 expect(browser ().window().path()).toBe("/Tests/AngularJS/Simple.html");  
10                 expect(browser ().location().path()).toBe("");  
11                 browser().navigateTo('/Tests/AngularJS/aaa.html');  
12                 expect(browser ().window().path()).not().toBe("/Tests/AngularJS/Simple.html")\
13 ;  
14                 expect(browser ().window().path()).toBe("/Tests/AngularJS/aaa.html");  
15             });
16 
17         it('Set Field value and check Angular scope update', function()  
18             {  
19                 var testValue = "This is a value";  
20                 var expectedValue = "Hello " + testValue + "!";  
21                 input('yourName').enter(testValue);  
22                 expect(element('.ng-binding').text()).toEqual(expectedValue);  
23             });
24 
25         it('get path value manually (using setTimeout)',function()  
26             {
27                 var path = browser().window().path();  
28                 console.log("(before timeout) path value = " + path.value );  
29                 setTimeout(function()  
30                     {  
31                         console.log("(after timeout) path value = " + path.value );  
32                         //expect("value").toBe("/123");
33 
34                     },200);  
35                 sleep(1);  
36             });  
37     });  

… and MarkDown.Editor.Spec.js currently with just

 1 describe('Markdown Editor - View funcionality', function()  
 2     {  
 3         it("Should open", function()  
 4             {  
 5                 browser().navigateTo('/Markdown/Editor');  
 6                 expect(browser ().window().path()).toBe("/Markdown/Editor");  
 7                 expect(browser ().location().path()).toBe("");  
 8                 //sleep(10);  
 9             });  
10     });

This is how I configured Karma to run on WebStorm:

Here is KarmaJS execution log:

Here are two connected KarmaJS Runners (one in IE and one in Chrome)

And finally, here is the (super useful) AngularJS: Scenario Test Runner view, in a collapsed state:

… and in an expanded state:

With this set-up KarmaJS is configured to run continuously and to monitor any file changes, which is really powerful, since it allows for continuous development and testing.

For example (to see the automagically execution in action), here is what the WebStorm UI looks like:

… with an Javascript error: (KarmaJS execution triggered on Save) :

… without the error: (KarmaJS execution triggered on Save)

This is a really nice environment to quickly develop AngularJS apps, specially since the tests are super quick to execute and we can control what tests are executed (for example by creating multiple karma.conf.js files)

O2 Platform Test GUI (created by hijacking 3 processes’ windows)

I also created a test GUI using the O2 Platform’s window-handle Hijack capabilities, which looked like this:

In the image above:

  • The host process is the O2 Platform Util - Win32 Window Handle Hijack (4x host panels ).h2 script/tool (which is a .Net app)
    • The left-hand side the Sublime Text editor (which is a C++, Pyhton app)
    • The top-right is Chrome (which is C++ app)
    • The bottom-right is cmd.exe (C++ app) running the command karma start karma.conf.js in the E:TeamMentorTM_Dev ReposTM_Dev_DinisWeb ApplicationsTM_WebsiteAngularJSKarma folder

All in a nice, self-contained process/environment

Note that there are 4 separate process at play here (O2 Platform, Sublime, Chrome and Cmd.Exe w/ Karma)

6. Troubleshooting

This section has the following chapters:

  • KarmaJS AngularJS Scenario Test Runner execution variations in IE 7,8,9 and 10 when using AngularJS
  • If AngularJS doesn’t work on your O2 Platform IE scripts (the fix is to change browser compatibility mode)
  • Debugging a weird case of missing module in AngularJS and KarmaJS

6.1 KarmaJS AngularJS Scenario Test Runner execution variations in IE 7,8,9 and 10 when using AngularJS

While trying to get Karma JS to work, I found a number of different behaviors for its AngularJS Scenario Test Runner in IE’s multiple ‘compatibility modes’.

TLDR: some of the Jasmine and AngularJS test apis don’t work (although Angular does seem to work ok)

Here is the default web page I was using:

Here is the test executed

Here is KarmaJS starting and successfully executing the tests

… in this captured IE browser session:

Just to confirm that the target page works in the multiple IE configurations, here it is running in:

IE 10 , IE9, IE 8:

… and even in IE 7:

Now lets click on the _DEBUG _button to open the KarmaJS’s_ AngularJS Scenario Test Runner_ view and see what happens in multiple IE compatibility modes.

IE 10 Works:

IE 10 Compatibility View

IE 9 Fails:

IE 8 Works (WTF!!)

IE 7 Fails

So unfortunately it looks like this technique can’t be used to run e2e (end-to-end) tests on AngularJS apps using KarmaJS

6.2 If AngularJS doesn’t work on your O2 Platform IE scripts (the fix is to change browser compatibility mode)

If when trying to open an AngularJS page inside an O2 Platform script, you see:

… this means that the IE browser embedded in that .NET process is set to run under IE 7

To confirm it, try opening the http://www.whatismybrowser.com and you should see something like:

As mentioned in the set .NET WebBrowser Control to use latest version of IE post to. change it on your system, run this script

… as admin with no UAC

…and now after restarting the O2 Platform process, IE should be on Internet Explorer 9 compatibility mode

… and AngularJS should work:

Note 1: if you control the site you are testing, you can also add this also works to make it work (with the advantage that it is not exe specific)

1 <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />

6.3 Debugging a weird case of missing module in AngularJS and KarmaJS

When I was trying the Running KarmaJS’s AngularJS example test/e2e/angular-scenario (on Chrome) I hit on the the following weird behavior.

TLDR; the solution was to run npm install –g karma@canary

Setup

Chrome window opened in:

… a local website at port 8000:

… which is a nodeJS powered website, started using node server.js (below)

A simple (as I could make it)_ Karma.config.js_ file

…. and AngularJS test

Scenario A) Running from folder with karma clone (and npm install)

1 karma start ..\angular-scenario\karma.conf.js

works OK

Scenario B) running from parent folder

1 karma start angular-scenario\karma.conf.js

fails with a module is not defined error

So what I think is happening is that because I run_ npm install** on the karma folder (the one I got from a GitHub clone), there are more modules in there than in the global karma (which I got when I installed karma using **_npm install –g karma)

At the moment there are 49 modules in the GitHub karma:

And 20 modules in the global karma install

So let’s try running npm install on this folder

And now there are 33 modules installed:

but that still didn’t work :(

At this stage I remembered reading something about installing the latest version of karma (globally), which is probably what I’m getting from the github clone, and that could explain the different number of modules.

So I executed npm install –g karma@canary

which installed ok:

Now running the scenario B) case throws a different error:

It looks that we also need install the karma-ng-scenario module

And now it works :)