Thursday, 14 January 2016

Coded UI and the Curse of the Docking Station

I’ve a love / hate relationship with Coded UI. Well hate / hate might be more accurate. Hate perhaps married with a very grudging respect still underpinned by a wary bitterness. Yes, that’s about the size of it. Why? Well, when Coded UI works, it’s fab. But it’s flaky as anything. Anybody who’s used the technology is presently nodding sagely and holding back the tears. It’s all a bit... tough.

I’ve recently discovered another quirk to add to the list. Docking stations. I was back working on a project which had a Coded UI test suite. I’d heard tell that there were problems with the tests and was just taking a look at them. The first hurdle I fell at was getting the tests to run locally. The tests had first been developed on a standard desktop build and, as much as this can ever be said of Coded UI tests, they worked. However, the future had happened. The company in question was no longer using the old school desktop towers. Nope, they’d reached for the sky and equipped the whole office with Surface Pro 3’s, hot desks, docking stations and big, big monitors. It looked terribly flash.

Coded UI was not happy.

The Mouse.Click behaviour wasn’t working. Most tests need the ability for users to click on buttons, dropdowns etc. That’s part of a normal UI. And so it was with these tests. This is where they fell over. The reason they fell over at this point didn’t become clear for a while. It wasn’t until we tried tweaking our implementation of the tests that we realised what was happening. The tests normally found buttons / dropdowns etc on the screen and then attempted to perform a Mouse.Click upon them. We changed the implementation to be subtly different. Instead of just clicking on the element we amended the test to move the mouse to the button and then perform the click.

Aha!

Rather than steadily moving towards an element and clicking, the pointer was swerving like a drunk man crossing the road at 3am. It completely missed the element it was aiming for and clicked upon a seemingly random area of the screen. This is Coded UI doing “pin the tail on the donkey”.

After more time than I'd like to admit I happened upon the solution. I tended to dock my Surface and then tune my monitor resolution to the one most optimal for coding. (ie really high res.) This is what messes with Coded UI's head; the resolution change. If I wanted to be able to run tests successfully all I had to do was switch back to the resolution I initially booted with. Alternately I could restart my computer so it launched with the resolution I was presently using.

Once you do follow this guidance Coded UI has a moment of clarity, gets sober and starts Mouse.Click-ing like a pro.

Friday, 1 January 2016

UseStaticFiles for ASP.Net vOld

That's what we're calling the original ASP.Net now, right? This is a guide on how not to expose all your static files to the world at large when working with the old (well, current) ASP.Net stack. How to move from a blacklisting approach to a whitelisting approach. Not clear? Stick around; I'll get better.

Oh and that's not all, we've also got.... drumroll:

Support for HTML5 History API!

What that means, in as close to English as I can get it, is real URLs for Single Page Applications. None of that hash-based routing malarkey. So, https://i-am-your-domain.com/i-am-your-route rather than https://i-am-your-domain.com/#/i-am-your-route. (For a more in depth look at the different sorts of routing SPA's can use then take a look at the excellent docs by the folk behind React Router. These concepts are not React specific and can be applied to any SPA technology.)

UseStaticFiles

You may be aware that historically ASP.Net has been somewhat unusual in its approach to serving static files. Essentially, all the files in a project are theoretically servable. Okay, that's not entirely true; things like the web.config files etc are not going to be handed over to someone browsing your site. But other files that you might well want kept away from prying eyes may be. So your TypeScript files, your Less files are all up for grabs unless you take action to block access to them. This is, and has always been, bad.

The ASP.Net team know this and things are changing with ASP.Net 5. With the new stack you have to say "these are the static files we want people to access" in the form of an app.UseStaticFiles() declaration. This is mighty similar to how you do things in other frameworks such as Express. To quote the docs:

By default, static files are stored in the webroot of your project. The location of the webroot is defined in the project’s project.json file where the default is wwwroot.

"webroot": "wwwroot"

Static files can be stored in any folder under the webroot and accessed with a relative path to that root. For example, when you create a default Web application project using Visual Studio, there are several folders created within the webroot folder - css, images and js. In order to directly access an image in the images subfolder, the URL would look like the following:

http://<yourApp>/images/<imageFileName>

So how do we get this behaviour with ASP.Net vOld? Well, it's just a matter of web.config URL rewrite twiddling:

<configuration>
  <!-- other config -->

  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Map empty URLs to the index.html">
          <match url="^$" />
          <action type="Rewrite" url="/index.html" />
        </rule>
        <rule name="Map all requests with a '.' in to the 'build' directory" stopProcessing="true">
          <match url="^(.*[.].*)$" />
          <action type="Rewrite" url="/build/{R:1}" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

My webroot is named build rather than wwwroot. The 2 URL rewrite rules above do the following:

Map empty URLs to the index.html
Empty URLs (ie the URL for the root of your site) are mapped to index.html. The index.html in the build folder is the home page of this particular site and the next rule will make sure that we hit that. (Since we haven't set stopProcessing to true for this particular rule the next rule will be processed after this one.)
Map all requests with a '.' in to the 'build' directory
All URLs with a "." in the title (including index.html) are redirected to the build folder. All static files have a "." in them because filenames have suffixes. This essentially means all requests for files are served from the build folder. In this case we have set stopProcessing to true which means that any URLs that matched this rule will be not be affected by any subsequent rules.

So if anyone sneakily tries to sneakily browse to say, http://<yourApp>/js/app.ts then they'll be nicely redirected to the non-existent build/js/app.ts. 404 in your face!

"I am SPArtucus"

When you have a Single Page Application you want the same web experience as a server side rendered web app. What I mean by this is: routing just works. You want people to be able to go to https://i-am-your-domain.com/i-am-your-route and get your site at the specified route. Happily, whether you're using React Router, Angular UI Router or something else, the client side is covered. They can be configured to detect the route that you enter at and serve up the SPA in the relevant state. But you have to meet them halfway; the server needs to do its bit.

When a URL is requested that doesn't look like a request for a static file, let's make the (reasonable) assumption that this is a route URL and serve up the shell SPA page. So, for my own example of an Angular 1.x app I want the server to hand over /build/index.html.

This is the magic that makes real URLs and SPAs work. Provided the client hasn't requested a static file, every request to the server will be responded to with our very own "I am SPArtucus"; the shell SPA page. This is catered for by the addition of another new rule to our web.config:

<configuration>
  <!-- other config -->

  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Map empty URLs to the index.html">
          <match url="^$" />
          <action type="Rewrite" url="/index.html" />
        </rule>
        <rule name="Map all requests with a '.' in to the 'build' directory" stopProcessing="true">
          <match url="^(.*[.].*)$" />
          <action type="Rewrite" url="/build/{R:1}" />
        </rule>
        <rule name="Map all other URLs to the index.html - this to support our SPA routes">
          <match url="^.*$" />
          <action type="Rewrite" url="/build/index.html" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>
Map all other URLs to the index.html - this to support our SPA routes
Our new rule says "whatever URL turns up, if it hasn't been catered for by an existing rule, well it must be a SPA route, so we'll serve up the shell SPA page of build/index.html".

Data! Data! Data!.. I can't make bricks without clay.

Sherlock Holmes was onto something; most applications are nothing without data. What you've got at present is an application that carefully restricts access to static files and, for all other requests, serves up our shell SPA page. So let's relax our final rule a little to make data access a thing:

<configuration>
  <!-- other config -->

  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Map empty URLs to the index.html">
          <match url="^$" />
          <action type="Rewrite" url="/index.html" />
        </rule>
        <rule name="Map all requests with a '.' in to the 'build' directory" stopProcessing="true">
          <match url="^(.*[.].*)$" />
          <action type="Rewrite" url="/build/{R:1}" />
        </rule>
        <rule name="Map non-api URLs to the index.html - this to support our SPA routes">
          <match url="^(api/).*$" negate="true" ignoreCase="true" />
          <action type="Rewrite" url="/build/index.html" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>
Map non-api URLs to the index.html - this to support our SPA routes
This amended rule says "whatever URL turns up, unless it begins "api/", if it hasn't been catered for by an existing rule, well it must be a SPA route, so we'll serve up the shell SPA page of build/index.html".

This allows us to perform data access by prefixing all the Web API routes with "api/". I've picked this because it is the default location for ASP.Net Web API routes. It is (like most things) entirely configurable. To see a working implementation of this complete approach then take a look at the PoorClaresAngular project here.