Commit 7f6ee3af authored by Sean Hammond's avatar Sean Hammond

Begin adding frontend tests for changing email

Begin adding frontend tests for the new AccountController code that
deals with the new form for changing the logged-in user's email address.

I've done this as a new top-level describe() with a new way of writing
Angular controller tests, rather than adding to the stuff already in
account-controller-test.coffee.

The aims of this way of writing tests is to make the tests easier to understand
and modify (either to fix a broken test or to add new ones) by:

- Reducing special Angular knowledge needed to understand the tests

- Reducing globals (both global variables and beforeEach() functions) used in
  tests.

  These increase the "travel" needed to understand a given test method because
  you have to read the whole test module to understand everything that happens
  before the test runs and what all the global variables the test uses are.

  The aim is that reading only the test function (and not the rest of the code
  in the file) should be enough to understand the test.

  Similarly I shouldn't have to read and understand the whole test file to add
  a new test.

  Globals and beforeEach()'s also tie the tests together. For example if a
  bunch of stub services and a controller are created in beforeEach() functions
  then any new tests added to the file inherit this stubbing behaviour even
  though they may not want it or may need something else. This again makes
  changing or adding just one test harder - need to understand the whole file,
  changes made for one test may break other tests.

Implementation notes:

- Used a new top-level describe(), didn't want to inherit all the globals,
  beforeEach()'s and stubbing of the existing one.

- Because we now have two top-level describe()s had to use a try ... catch in
  the second one to avoid creating the "h" Angular module twice.

- Tried to decouple the tests from Angular as much as possible, reduce the
  amount of Angular knowledge (especially about providing and injecting
  dependencies) needed to understand the tests.

  Angular's $provide isn't used at all, and inject() (which I don't think can
  be removed completely) is contained in two helper functions.

- Rather than a beforeEach() and global variables we use a controller()
  function that creates the AccountController and stubs for the services it
  depends on and returns an object of all of them.

  Tests can then call controller() and use destructuring assignment to get only
  the bits they need.

  Every variable used in a test method is defined in the test method
  (sometimes by calling a helper function and getting a return value).

- controller() by default uses minimal stubbing, but the caller can optionally
  pass in their own object for each of the stubbed dependencies.
parent d255f520
......@@ -233,3 +233,99 @@ describe 'h:AccountController', ->
assert.calledWith(fakeFlash.error,
'Sorry, we were unable to perform your request')
describe "h:AccountController", ->
before(->
try
# If this runs without error then the h module has already been defined
# by an earlier top-level describe() in this file.
angular.module("h")
catch error
# The h module hasn't been defined yet, so we need to define it
# (this happens when it.only() is used in this describe()).
angular.module("h", [])
require("../account-controller")
)
beforeEach module('h')
# Return the $controller service from Angular.
getControllerService = ->
$controller = null
inject((_$controller_) ->
$controller = _$controller_
)
return $controller
# Return the $rootScope service from Angular.
getRootScope = ->
$rootScope = null
inject((_$rootScope_) ->
$rootScope = _$rootScope_
)
return $rootScope
###
Return an AccountController instance and stub services.
Returns an object containing:
* an AccountController instance with all the services it depends on
stubbed, and
* each of the stubbed services
The returned object looks like this:
{"ctrl": the AccountController instance
"$scope": the scope attached to ctrl
"$filter": the stub filter injected into ctrl
"auth": the stub auth service injected into ctrl
... (more stubbed services here)
}
Use CoffeeScript's destructuring assignment to pull out just the things
you need from the returned object. For example:
{ctrl, $scope} = controller()
By default this does the minimum amount of stubbing necessary to create an
AccountController without it crashing. For each of the services that gets
stubbed the caller can optionally pass in their own object to be used
instead of the minimal stub. For example:
session = {profile: -> {$promise: ...}}
{ctrl, $scope} = controller(session: session)
###
controller = ({$scope, $filter, auth, flash, formRespond, identity,
session}) ->
locals = {
$scope: $scope or getRootScope().$new()
$filter: $filter or -> -> {}
auth: auth or {}
flash: flash or {}
formRespond: formRespond or {}
identity: identity or {}
session: session or {profile: -> {$promise: Promise.resolve()}}
}
locals["ctrl"] = getControllerService()("AccountController", locals)
return locals
###
The controller sets $scope.email to the user's current email address on
controller initialization. The templates use this for the placeholder
value of the email input fields.
###
it "adds the current email address to the scope when initialized", ->
# The controller actually calls session.profile() on init which returns
# a promise, and when that promise resolves it uses the value to set
# $scope.email. So we need to stub that promise here.
profilePromise = Promise.resolve({
email: "test_user@test_email.com"
})
session = {profile: -> {$promise: profilePromise}}
{ctrl, $scope} = controller(session: session)
profilePromise.then(->
assert $scope.email == "test_user@test_email.com"
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment