dalekjs / dalek-internal-config

[unmaintained] DalekJS internal configuration helper
1 stars 2 forks source link

Ability to define javascript functions globally #2

Open ryanzec opened 11 years ago

ryanzec commented 11 years ago

Now maybe the config file is not the best place for this, you might have a better idea of where something like this should go, but I think it would be really useful to have a collection of globally accessible javascript function within the context of at least tests, but not limited to that scope, that can be used in conjunction with execute()/waitFor().

For example with the AngularJS framework, if you want to know if AngularJS is processing something, you would doing this:

$browser = angular.element(el).injector().get('$browser').
$browser.notifyWhenNoOutstandingRequests(callback);

Right now with the API for waitFor, I might have to do something like this:

{
  "test": function(test) {
    test.open('http://www,example.com')
    .waitFor(function() {
      $browser = angular.element(el).injector().get('$browser').
      $browser.notifyWhenNoOutstandingRequests(callback);
    })
    .click('.some-element')
    .waitFor(function() {
      $browser = angular.element(el).injector().get('$browser').
      $browser.notifyWhenNoOutstandingRequests(callback);
    })
    .type('.some-input', 'some text')
    .waitFor(function() {
      $browser = angular.element(el).injector().get('$browser').
      $browser.notifyWhenNoOutstandingRequests(callback);
    })
    //do some assertion
    .done();
  }
}

Which is a lot of typing/copying and pasting and it also makes reading the tests a lot harder.. Imagine I have these in hundreds of places within my tests (which is not unreasonable in a large application). Now imagine that is a new thing in AngularJS that I have to check to make sure angular is done, now I have to update all those locations. I imagine this type of scenario is probable in other libraries/framework.

My solution would be to have this in a config or somewhere:

{
  "clientScripts": [{
    "waitForAngular": function() {
      $browser = angular.element(el).injector().get('$browser').
      $browser.notifyWhenNoOutstandingRequests(callback);
    }
  }]
}

And then the test would look something like this:

{
  "test": function(test) {
    test.open('http://www,example.com')
    .waitFor(test.clientScript.waitForAngular)
    .click('.some-element')
    .waitFor(test.clientScript.waitForAngular)
    .type('.some-input', 'some text')
    .waitFor(test.clientScript.waitForAngular)
    //do some assertion
    .done();
  }
}

The test is now a lot shorter and simpler to read and if there is a change to the waitForAngular logic, I only have to update 1 location.

I think a feature this this would be really helpful (I think we could make things even simpler but this would be the first step towards that and provide generic functionality that would be useful in other use cases).

asciidisco commented 11 years ago

The idea is great, but I think we should use nodes regular features to achieve this.

Just add a separate file, clientScript.js or whatever, which looks like this

module.exports = {
  waitForAngular: function() {
    $browser = angular.element(el).injector().get('$browser').
    $browser.notifyWhenNoOutstandingRequests(callback);
  }
}

and require this in your test like so:

var clientScript = require('./clientScript');
module.exports = {
  "test": function(test) {
    test.open('http://www,example.com')
    .waitFor(clientScript.waitForAngular)
    .click('.some-element')
    .waitFor(clientScript.waitForAngular)
    .type('.some-input', 'some text')
    .waitFor(clientScript.waitForAngular)
    //do some assertion
    .done();
  }
}

So, this should be documented as a best practice, but I don't think that we need to reimplement the wheel, as this functionality is already there.

Do you have a different opinion? Also, I would like to hear if @rodneyrehm has an opinion on this issue.

ryanzec commented 11 years ago

I didn't even think about that, I'll have to give that a try when the wait()/waitFor() functions are fixed.

rodneyrehm commented 11 years ago

We've already discussed the matter of plugin API offline a couple of weeks ago. In case you lost the minutes of that discussion:

// dalek actions to register plugins
.addAction('foobar', callback) // persistent in action context, allows fetching data() or recurring tests, in action namespace
.addAssertion('foobar', callback) // action/element context, to simplify complex base assertions, in assert namespace
.addQuery('foobar', callback) // from action/element context, switches to element context, may run assertions, in query namespace

// addAction or registerAction or whatver

On top of that, the Dalekfile.json could define plugins to load:

{
  "tests": ["test/a.js", "test/b.js"],
  "browser": ["chrome"],
  "reporter": ["console"],
  "plugins": ["plugins/*.js"]
}

Your Angular plugin example could then look somewhat like this:

module.exports = function (dalek) {
  'use strict';
  dalek.addAction('waitForAngular', function() {
    return this.waitFor(function() {
      var done = this.async();

      var el = null;
      var $browser = window.angular.element(el).injector().get('$browser').
      $browser.notifyWhenNoOutstandingRequests(done);
    });
  });
};

Having loaded this file (by means of Dalekfile.js or by using require() or using dalek.addAction() in the same file) you could access your plugin by name:

{
  "test": function(test) {
    test.open('http://www,example.com')
    .waitForAngular()
    .click('.some-element')
    .waitForAngular()
    .type('.some-input', 'some text')
    .waitForAngular()
    //do some assertion
    .done();
  }
}

Note: