ng-directive-compiler-helper
Advanced tools
Comparing version 0.0.4 to 1.0.0
@@ -7,8 +7,9 @@ /* global angular */ | ||
* createCompiler | ||
* @param {string} template string representing the template of directive | ||
* @param {object} $rootScope the rootScope service | ||
* @param {object} $compile the compile service | ||
* @return {function} compiler function | ||
* @param {string} template string representing the template of directive | ||
* @param {object} $rootScope the rootScope service | ||
* @param {object} $compile the compile service | ||
* @param [{object}] driver optional driver object literal | ||
* @return {function} compiler function | ||
*/ | ||
function createCompiler(template, $rootScope, $compile) { | ||
function createCompiler(template, $rootScope, $compile, driver) { | ||
/** | ||
@@ -19,6 +20,7 @@ * directive template compiler | ||
* @param {function} callback function to fire when template has been compiled | ||
* @param {function} driver function to fire when template has been compiled | ||
* @return {object} object with element and scope properties | ||
*/ | ||
return function(/*parentScope, attrs, callback*/) { | ||
var args = Array.prototype.slice.call(arguments, 0), | ||
return function (/* parentScope, attrs, callback */) { | ||
var args = [].slice.call(arguments), | ||
callback = angular.isFunction(args[args.length - 1]) ? args.pop() : angular.noop, | ||
@@ -35,6 +37,31 @@ parentScope = args.shift() || {}, | ||
callback(extendedScope.$$childHead, element); | ||
if(driver) { | ||
var tempDriver = angular.copy(driver); | ||
return { scope: extendedScope.$$childHead, element: element }; | ||
tempDriver.$ = element; | ||
var newDriver = | ||
Object | ||
.keys(tempDriver) | ||
.reduce(function (acc, curr) { | ||
acc[curr] = typeof tempDriver[curr] === 'function' ? function () { | ||
return arguments.length ? | ||
tempDriver[curr].apply(tempDriver, arguments) : | ||
tempDriver[curr].call(tempDriver, element); | ||
} : tempDriver[curr]; | ||
return acc; | ||
}, {}); | ||
callback(extendedScope.$$childHead, element, newDriver); | ||
} else { | ||
callback(extendedScope.$$childHead, element); | ||
} | ||
return { | ||
scope: extendedScope.$$childHead, | ||
element: element, | ||
driver: newDriver | ||
}; | ||
}; | ||
} |
{ | ||
"name": "ng-directive-compiler-helper", | ||
"author": "Arijus Šukys <argshook@gmail.com> (http://arijus.net)", | ||
"version": "0.0.4", | ||
"version": "1.0.0", | ||
"description": "helper function for easier angularJS 1.x directive compilation in unit tests", | ||
@@ -6,0 +6,0 @@ "main": "lib/ng-directive-compiler-helper.js", |
@@ -33,3 +33,3 @@ [![Build Status](https://travis-ci.org/argshook/ng-directive-compiler-helper.svg?branch=master)](https://travis-ci.org/argshook/ng-directive-compiler-helper) | ||
`compile` is now a function which can be used in two major ways: | ||
`compile` is now a function which can be used in two ways: | ||
@@ -117,5 +117,74 @@ 1. using *callbackFn* which is called after directive is compiled. *callbackFn* is passed with *scope* and *element* arguments | ||
1. working with drivers | ||
```js | ||
// 1. define driver | ||
let driver = { | ||
parent: e => e.find('.imaginary-parent-with-3-children'); // e - reference to element, passed if no other arguments given, | ||
children: parent => parent.children; | ||
alsoChildren: function() { return this.$.children; } // this.$ - also reference to element | ||
}; | ||
// 2. hook driver when creating compiler (as last argument) | ||
let compile = createCompiler(templateString, $rootScope, $compile, driver); | ||
// 3. use in tests | ||
it('should contain 3 items', () => { | ||
compile(function(scope, element, driver) { // <-- driver is passed as third argument | ||
expect(driver.parent().length).toBe(1); | ||
expect(driver.children(element).length).toBe(3); | ||
expect(driver.alsoChildren().length).toBe(3); | ||
}) | ||
}); | ||
``` | ||
testing like this should be cool because: | ||
* driver can be reused for multiple tests, drying up the test suite | ||
* no need to repeat selectors everywhere | ||
* other more complicated logic can be reused (e.g. do some component setup for assertions) | ||
### Few notes about drivers | ||
* if driver method is called without arguments, it automatically gets element reference (but ONLY if there are no | ||
arguments given): | ||
> Note: the following examples assume you have `let compile = createCompiler` setup with driver. | ||
```js | ||
let driver = { | ||
myTitle: e => e.find('.title-element') | ||
} | ||
it('should have title', () => { | ||
compile((scope, element, driver) { | ||
expect(driver.myTitle().text()).toBe('some text'); | ||
}); | ||
}); | ||
``` | ||
* if driver method is called with arguments, element reference is available through `this.$`: | ||
```js | ||
let driver = { | ||
myListItem: function(n) { | ||
return this.$.find('.my-list').get(n); | ||
} | ||
}; | ||
it('should have correct item', () => { | ||
compile(function(scope, element, driver) { | ||
expect(driver.myListItem(2).text()).toBe('my list item #3'); | ||
}); | ||
}); | ||
``` | ||
# More examples | ||
i use this helper thing to test one of mine angular projects, you can check here: [argshook/orodarius](https://github.com/argshook/orodarius) | ||
# Contributing | ||
Please provide test for pull requests. | ||
Please provide tests for pull requests. | ||
@@ -122,0 +191,0 @@ Testing with karma: |
@@ -1,20 +0,22 @@ | ||
describe('createCompiler', function() { | ||
var createdCompiler, | ||
mockTemplate = '<my-directive></my-directive>', | ||
mockIsolateTemplate = '<my-isolate-directive></my-isolate-directive>'; | ||
/* global jasmine, describe, angular, it, expect, beforeEach, inject, createCompiler */ | ||
describe('createCompiler', function () { | ||
var createdCompiler; | ||
var mockTemplate = '<my-directive></my-directive>'; | ||
var mockIsolateTemplate = '<my-isolate-directive></my-isolate-directive>'; | ||
angular | ||
.module('mockModule', ['ngMock']) | ||
.directive('myDirective', function() { | ||
.directive('myDirective', function () { | ||
return { | ||
restrict: 'E', | ||
scope: true, | ||
template: '<div>directive</div>' | ||
template: '<div id="directive">directive <div class="child">Child content</div></div>' | ||
}; | ||
}) | ||
.directive('myIsolateDirective', function() { | ||
.directive('myIsolateDirective', function () { | ||
return { | ||
restrict: 'E', | ||
scope: { isolateProperty: '@' }, | ||
template: '<div>isolate directive</div>', | ||
template: '<div id="isolate-directive">isolate directive <button ng-click="isolateProperty = \'changed\'">Click me</button><div class="child">Isolate child content</div></div>', | ||
}; | ||
@@ -25,4 +27,4 @@ }); | ||
describe('createCompiler()', function() { | ||
it('should return function', function() { | ||
describe('createCompiler()', function () { | ||
it('should return function', function () { | ||
expect(typeof createCompiler()).toBe('function'); | ||
@@ -32,16 +34,17 @@ }); | ||
describe('compiler with non-isolate scope directive', function() { | ||
beforeEach(inject(function($rootScope, $compile) { | ||
describe('compiler with non-isolate scope directive', function () { | ||
beforeEach(inject(function ($rootScope, $compile) { | ||
createdCompiler = createCompiler(mockTemplate, $rootScope, $compile); | ||
})); | ||
it('should return object with scope and element properties', function() { | ||
it('should return object with scope and element properties', function () { | ||
var compiledDirective = createdCompiler(); | ||
expect(compiledDirective.scope).toBeDefined(); | ||
expect(compiledDirective.element.text()).toBe('directive'); | ||
expect(compiledDirective.element.text()).toMatch('directive'); | ||
expect(compiledDirective.element[0].querySelector('.child').innerText).toBe('Child content'); | ||
}); | ||
describe('when one argument given', function() { | ||
describe('which is function', function() { | ||
it('should call that one argument as if it was a callback function', function() { | ||
describe('when one argument given', function () { | ||
describe('which is function', function () { | ||
it('should call that one argument as if it was a callback function', function () { | ||
expectCallbackToBeCalled(); | ||
@@ -51,7 +54,8 @@ }); | ||
describe('which is object', function() { | ||
it('should return object with scope and element properties where scope is extended with first argument', function() { | ||
describe('which is object', function () { | ||
it('should return object with scope and element properties where scope is extended with first argument', function () { | ||
var compiledDirective = createdCompiler({ someProperty: 'someValue' }); | ||
expect(compiledDirective.scope.someProperty).toBe('someValue'); | ||
expect(compiledDirective.element.text()).toBe('directive'); | ||
expect(compiledDirective.element.text()).toMatch('directive'); | ||
expect(compiledDirective.element[0].querySelector('.child').innerText).toBe('Child content'); | ||
}); | ||
@@ -61,5 +65,5 @@ }); | ||
describe('when two arguments given', function() { | ||
it('should set parent scope from first argument', function() { | ||
createdCompiler({ parentScope: 'value' }, function(scope) { | ||
describe('when two arguments given', function () { | ||
it('should set parent scope from first argument', function () { | ||
createdCompiler({ parentScope: 'value' }, function (scope) { | ||
expect(scope.parentScope).toBe('value'); | ||
@@ -69,5 +73,5 @@ }); | ||
describe('when type of second argument is', function() { | ||
describe('function', function() { | ||
it('should call second argument as if it was a callback function', function() { | ||
describe('when type of second argument is', function () { | ||
describe('function', function () { | ||
it('should call second argument as if it was a callback function', function () { | ||
expectCallbackToBeCalled(); | ||
@@ -77,4 +81,4 @@ }); | ||
describe('object', function() { | ||
it('should return object with scope and element properties where element has attributes set from second argument', function() { | ||
describe('object', function () { | ||
it('should return object with scope and element properties where element has attributes set from second argument', function () { | ||
var compiledDirective = createdCompiler({}, { 'some-attribute': 'someAttributeValue' }); | ||
@@ -87,5 +91,5 @@ expect(compiledDirective.element.attr('some-attribute')).toBe('someAttributeValue'); | ||
describe('when three arguments given', function() { | ||
it('should set parent scope from first argument', function() { | ||
createdCompiler({ parentScope: 'value' }, {}, function(scope) { | ||
describe('when three arguments given', function () { | ||
it('should set parent scope from first argument', function () { | ||
createdCompiler({ parentScope: 'value' }, {}, function (scope) { | ||
expect(scope.parentScope).toBe('value'); | ||
@@ -95,4 +99,4 @@ }); | ||
it('should set directive attributes from second argument', function() { | ||
createdCompiler({}, { moustache: 'french', 'big-phat': 'azz' }, function(scope, element) { | ||
it('should set directive attributes from second argument', function () { | ||
createdCompiler({}, { moustache: 'french', 'big-phat': 'azz' }, function (scope, element) { | ||
expect(element.attr('moustache')).toBe('french'); | ||
@@ -103,9 +107,13 @@ expect(element.attr('big-phat')).toBe('azz'); | ||
it('should call third argument as if it was a callback function', function() { | ||
it('should call third argument as if it was a callback function', function () { | ||
expectCallbackToBeCalled(); | ||
}); | ||
it('should return object with scope and element properties that are extended from 1st and 2nd args', function() { | ||
var callbackSpy = jasmine.createSpy('createdCompilerCallbackSpy'), | ||
compiledDirective = createdCompiler({ someProperty: 'someValue' }, { 'some-attribute': 'someAttributeValue' }, callbackSpy); | ||
it('should return object with scope and element properties that are extended from 1st and 2nd args', function () { | ||
var callbackSpy = jasmine.createSpy('createdCompilerCallbackSpy'); | ||
var compiledDirective = createdCompiler( | ||
{ someProperty: 'someValue' }, | ||
{ 'some-attribute': 'someAttributeValue' }, | ||
callbackSpy | ||
); | ||
@@ -119,16 +127,17 @@ expect(callbackSpy).toHaveBeenCalled(); | ||
describe('compiler with isolate scope directive', function() { | ||
beforeEach(inject(function($rootScope, $compile) { | ||
describe('compiler with isolate scope directive', function () { | ||
beforeEach(inject(function ($rootScope, $compile) { | ||
createdCompiler = createCompiler(mockIsolateTemplate, $rootScope, $compile); | ||
})); | ||
it('should return object with scope and element properties', function() { | ||
it('should return object with scope and element properties', function () { | ||
var compiledDirective = createdCompiler(); | ||
expect(compiledDirective.scope).toBeDefined(); | ||
expect(compiledDirective.element.text()).toBe('isolate directive'); | ||
expect(compiledDirective.element.text()).toMatch('isolate directive'); | ||
expect(compiledDirective.element[0].querySelector('.child').innerText).toBe('Isolate child content'); | ||
}); | ||
describe('when one argument given', function() { | ||
describe('which is function', function() { | ||
it('should call that one argument as if it was a callback function', function() { | ||
describe('when one argument given', function () { | ||
describe('which is function', function () { | ||
it('should call that one argument as if it was a callback function', function () { | ||
expectCallbackToBeCalled(); | ||
@@ -138,7 +147,8 @@ }); | ||
describe('which is object', function() { | ||
it('should return object with scope and element properties where scope is NOT extended with first argument', function() { | ||
describe('which is object', function () { | ||
it('should return object with scope and element properties where scope is NOT extended with first argument', function () { | ||
var compiledDirective = createdCompiler({ parentScopeProperty: 'someValue' }); | ||
expect(compiledDirective.scope.parentScopeProperty).not.toBe('someValue'); | ||
expect(compiledDirective.element.text()).toBe('isolate directive'); | ||
expect(compiledDirective.element.text()).toMatch('isolate directive'); | ||
expect(compiledDirective.element[0].querySelector('.child').innerText).toBe('Isolate child content'); | ||
}); | ||
@@ -148,5 +158,5 @@ }); | ||
describe('when two arguments given', function() { | ||
it('should ignore parent scope from first argument', function() { | ||
createdCompiler({ isolateProperty: 'value' }, function(scope, element) { | ||
describe('when two arguments given', function () { | ||
it('should ignore parent scope from first argument', function () { | ||
createdCompiler({ isolateProperty: 'value' }, function (scope, element) { | ||
expect(scope.isolateProperty).not.toBeDefined(); | ||
@@ -156,5 +166,5 @@ }); | ||
describe('when type of second argument is', function() { | ||
describe('function', function() { | ||
it('should call second argument as if it was a callback function', function() { | ||
describe('when type of second argument is', function () { | ||
describe('function', function () { | ||
it('should call second argument as if it was a callback function', function () { | ||
expectCallbackToBeCalled(); | ||
@@ -164,4 +174,4 @@ }); | ||
describe('object', function() { | ||
it('should return object with scope and element properties where element has attributes set from second argument', function() { | ||
describe('object', function () { | ||
it('should return object with scope and element properties where element has attributes set from second argument', function () { | ||
var compiledDirective = createdCompiler({}, { 'some-attribute': 'someAttributeValue' }); | ||
@@ -174,5 +184,5 @@ expect(compiledDirective.element.attr('some-attribute')).toBe('someAttributeValue'); | ||
describe('when three arguments given', function() { | ||
it('should ignore parent scope from first argument', function() { | ||
createdCompiler({ isolateProperty: 'value' }, function(scope, element) { | ||
describe('when three arguments given', function () { | ||
it('should ignore parent scope from first argument', function () { | ||
createdCompiler({ isolateProperty: 'value' }, function (scope, element) { | ||
expect(scope.isolateProperty).not.toBeDefined(); | ||
@@ -182,4 +192,4 @@ }); | ||
it('should set isolate scope properties from attributes object', function() { | ||
createdCompiler({}, { 'isolate-property': 'value' }, function(scope, element) { | ||
it('should set isolate scope properties from attributes object', function () { | ||
createdCompiler({}, { 'isolate-property': 'value' }, function (scope, element) { | ||
expect(scope.isolateProperty).toBe('value'); | ||
@@ -189,10 +199,14 @@ }); | ||
it('should call third argument as if it was a callback function', function() { | ||
it('should call third argument as if it was a callback function', function () { | ||
expectCallbackToBeCalled(); | ||
}); | ||
it('should return object with scope and element properties', function() { | ||
var callbackSpy = jasmine.createSpy('createdCompilerCallbackSpy'), | ||
// since it's isolate scope directive, first argument should not extend scope properties | ||
compiledDirective = createdCompiler({ someProperty: 'someValue' }, { 'some-attribute': 'someAttributeValue' }, callbackSpy); | ||
it('should return object with scope and element properties', function () { | ||
var callbackSpy = jasmine.createSpy('createdCompilerCallbackSpy'); | ||
// since it's isolate scope directive, first argument should not extend scope properties | ||
var compiledDirective = createdCompiler( | ||
{ someProperty: 'someValue' }, | ||
{ 'some-attribute': 'someAttributeValue' }, | ||
callbackSpy | ||
); | ||
@@ -206,2 +220,61 @@ expect(callbackSpy).toHaveBeenCalled(); | ||
describe('when compiled with driver', function() { | ||
var driver = { | ||
text: function(element) { | ||
return element[0].querySelector('.child').innerText; | ||
}, | ||
driveMeCrazy: function(element, scope) { | ||
var click = document.createEvent('MouseEvent'); | ||
click.initEvent('click', true, true); | ||
element[0].querySelector('button').dispatchEvent(click); | ||
scope.$digest(); | ||
}, | ||
testicle: 'shit', | ||
goodbye: function() { | ||
// matching since apparently depending on platform it's | ||
// either HTMLElement or HTMLUnknownElement | ||
// (e.g. on linux it's former whereas on osx it's the latter) | ||
expect(this.$.toString()).toMatch('\[object HTML.*Element\]'); | ||
return 'you little ' + this.testicle; | ||
}, | ||
uppercase: function(me) { | ||
return me.toUpperCase(); | ||
} | ||
}; | ||
beforeEach(inject(function($rootScope, $compile) { | ||
createdCompiler = createCompiler(mockIsolateTemplate, $rootScope, $compile, driver); | ||
})); | ||
it('should use last argument as driver', function() { | ||
createdCompiler(function(scope, element, driver) { | ||
expect(driver.text()).toBe('Isolate child content') | ||
driver.driveMeCrazy(element, scope); | ||
expect(scope.isolateProperty).toBe('changed') | ||
}); | ||
}); | ||
it('should pass arguments to driver methods', function() { | ||
createdCompiler(function(scope, element, driver) { | ||
expect(driver.uppercase('trick')).toBe('TRICK'); | ||
}); | ||
}); | ||
it('should set expected context to driver methods', function() { | ||
createdCompiler(function (scope, element, driver) { | ||
expect(driver.goodbye()).toBe('you little shit'); | ||
expect(driver.testicle).toBe('shit'); | ||
}); | ||
}); | ||
it('should not change original driver object', function() { | ||
expect(driver.$).toBe(undefined); | ||
}); | ||
}); | ||
function expectCallbackToBeCalled() { | ||
@@ -213,1 +286,2 @@ var callbackSpy = jasmine.createSpy('callbackSpy'); | ||
}); | ||
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
22283
9
330
1
194