acuminous/yadda

View on GitHub
test/InterpreterTests.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict';

var assert = require('assert');
var Yadda = require('../lib/index');
var Interpreter = Yadda.Interpreter;
var EventBus = Yadda.EventBus;
var Library = Yadda.Library;
var Dictionary = Yadda.Dictionary;
var Context = Yadda.Context;
var Counter = require('./Counter');

describe('Interpreter', function () {
  it('should interpret a single line script', function () {
    var counter = new Counter();
    var library = new Library().define('Blah blah blah', counter.count);

    new Interpreter(library).interpret('Blah blah blah');

    assert.equal(counter.total(), 1);
  });

  it('should interpret a multiline script', function () {
    var counter = new Counter();
    var library = new Library().define('Blah blah blah', counter.count);

    new Interpreter(library).interpret(['Blah blah blah', 'Blah blah blah']);

    assert.equal(counter.total(), 2);
  });

  it('should validate scenarios', function () {
    var library = new Library()
      .define('This is defined')
      .define(/[Tt]his is ambiguous/)
      .define(/[tT]his is ambiguous/);

    assert.throws(function () {
      new Interpreter(library).validate(['This is defined', 'This is undefined', 'This is ambiguous']);
    }, /Scenario cannot be interpreted\nThis is defined\nThis is undefined <-- Undefined Step\nThis is ambiguous <-- Ambiguous Step/);
  });

  it('should favour ambiguous steps from the same library as the previous step', function () {
    var library1 = new Library().define('Library 1').define(/[Tt]his is ambiguous/);
    var library2 = new Library().define('Library 2').define(/[tT]his is ambiguous/);

    new Interpreter([library1, library2]).validate(['Library 2', 'This is ambiguous']);
  });

  it('should utilise macros from different libraries', function () {
    var counter = new Counter();
    var library_1 = new Library().define('Blah blah blah', counter.count);
    var library_2 = new Library().define('Whatever', counter.count);

    new Interpreter([library_1, library_2]).interpret(['Blah blah blah', 'Whatever']);

    assert.equal(counter.total(), 2);
  });

  it('should expanded terms to discern macros', function () {
    var patient_name;

    var dictionary = new Dictionary().define('gender', '(male|female)').define('speciality', '(cardio|elderly care)');

    var library = new Library(dictionary)
      .define('Given a $gender patient called $name', function (gender, name) {
        patient_name = name;
      })
      .define('Given a $speciality patient called $name', function (speciality, name) {
        patient_name = name;
      });

    new Interpreter(library).interpret('Given a female patient called Carol');
    assert.equal('Carol', patient_name);

    new Interpreter(library).interpret('Given a cardio patient called Bobby');
    assert.equal('Bobby', patient_name);
  });

  it('should report undefined steps', function () {
    var library = new Library();
    var interpreter = new Interpreter(library);

    assert.throws(function () {
      interpreter.interpret('Blah blah blah');
    }, /Undefined Step: \[Blah blah blah\]/);
  });

  it('should interpret steps asynchronously', function (done) {
    var counter = new Counter();
    var library = new Library().define('Blah blah blah', counter.count);

    new Interpreter(library).interpret(['Blah blah blah', 'Blah blah blah'], {}, function () {
      assert.equal(counter.total(), 2);
      done();
    });
  });

  it('should support variadic asynchronous steps', function (done) {
    var counter = new Counter();
    var library = new Library().define(
      ['Blah (blah)', 'Blah (blah) (blah)'],
      function () {
        counter.count();
        arguments[arguments.length - 1]();
      },
      {},
      { mode: 'async' }
    );

    new Interpreter(library).interpret(['Blah blah', 'Blah blah blah'], {}, function () {
      assert.equal(counter.total(), 2);
      done();
    });
  });

  it('should bind the context to the macro', function (done) {
    var context = new Context({ foo: 'bar' });
    var library = new Library().define('Blah blah blah', function (next) {
      assert.equal(this.foo, 'bar');
      next();
    });

    new Interpreter(library).interpret(['Blah blah blah', 'Blah blah blah'], context, done);
  });

  it('should notify listeners of interpreter events', function (done) {
    var library = new Library().define('Blah blah blah');
    var interpreter = new Interpreter(library);
    var listener = new Listener();
    EventBus.instance().on(/STEP|SCENARIO/, listener.listen);

    interpreter.interpret('Blah blah blah', new Context({ foo: 'bar' }));

    assert.equal(2, listener.events.length);

    assert_event(
      {
        name: EventBus.ON_SCENARIO,
        data: { scenario: 'Blah blah blah', ctx: { foo: 'bar' } },
      },
      listener.events[0]
    );

    assert_event(
      {
        name: EventBus.ON_STEP,
        data: { step: 'Blah blah blah', ctx: { foo: 'bar' } },
      },
      listener.events[1]
    );

    done();
  });

  it('should catch errors thrown by asynchronous steps where possible', function () {
    var library = new Library().define('Blah blah blah', function (next) {
      throw new Error('Oh Noes!');
    });

    new Interpreter(library).interpret('Blah blah blah', {}, function (err) {
      assert.ok(err);
      assert.equal(err.message, 'Oh Noes!');
    });
  });

  function Listener() {
    var _this = this;
    this.events = [];
    this.listen = function (event) {
      _this.events.push(event);
    };
  }

  function assert_event(expected, actual) {
    assert.ok(actual);
    assert.equal(expected.name, actual.name);
    assert.deepEqual(expected.data, actual.data);
  }
});