5

I'm writing tests for a directive that basically expand over it's container to reach the full width of the browser window.

To exemplify this I'll write some sample code.

HTML in the page:

<div style="width: 800px; margin: 0 auto;">
  <full-width></full-width>
</div>

The directive then replace the fullWidth directive with:

<div class="full-width">
  <div ng-transclude=""></div>
</div>

And assign margins to the element, like this:

var sideOffset = element[0].offsetLeft*-1;

element.css('margin-left', sideOffset+'px')
element.css('margin-right', sideOffset+'px')


Now, the directive works perfectly fine, but when I try to test the offset with:

describe('Directive: fullWidth', function () {

  beforeEach(module('myApp'))

  var element,
      scope

  beforeEach(inject(function ($rootScope) {
    scope = $rootScope.$new()
  }))

  it('should have the same right and left margin', inject(function ($compile) {
    element = angular.element('<tt-full-width-section></tt-full-width-section>');
    element = $compile(element)(scope)

    var leftMargin = element.css('margin-left')
    console.log(leftMargin)
    console.log(element)
  }))
})

I get a nice 0px. Also inspecting the logged element[0].offsetLeft results in 0.

Any idea on how can I (if possible) tell Karma (Jasmine?) to render the div so I can check the offset?


Based on daveoncode suggestion, I made some changes to the test, and we're improving. However, the directive doesn't seems to work in the test.
I still get 0px as margin-left while having 265px of offsetLeft

it('should have the same right and left margin', inject(function ($compile) {
  element = angular.element(
      '  <div style="width:50%; margin: 0 auto;">' +
      '    <full-width></full-width>' +
      '  </div>'
  );
  element = $compile(element)(scope)
  angular.element(document).find('body').append(element);

  var el2 = element.find('div')

  var leftMargin = el2.css('margin-left')
  var rightMargin = el2.css('margin-right')
  console.log(leftMargin, el2)
}
2
  • 1
    I had the same problem days ago, please read my own answer here: stackoverflow.com/questions/25654043/… ;) Commented Sep 10, 2014 at 12:36
  • Definitely helpful, but not decisive. Please take a look at my update Commented Sep 10, 2014 at 15:37

1 Answer 1

10

TIP 1:

CSS styles are not applied to elements until they get added to the DOM! and this is not an issue related to karma, angular, jasmine or what else... this is how a browser engine works! A browser parses CSS definitions and it renders elements in the page according, but when in angular test you write:

var element = angular.element('<my-directive></my-directive>');
$compile(element)(scope);

You are dealing with in-memory DOM nodes, which are unknown to all but your JavaScript code! How should the browser engine apply CSS to an in-memory node living in a js variable? It can't obviously... it can only traverse the nodes tree in the page, so... the "fix" is very simple: you have to add the element to the DOM:

angular.element(document).find('body').append(element);

TIP 2:

Ok, I think that now your problem is actually another one, you are using jQlite .css() implementation, which differently from the jQuery one does not retrieve the computed style but only the inline one (hence I fear that is not able to "translate" auto to the actual number of pixels!)... Try to use this snippet to retrieve the computed margin left:

window.getComputedStyle(element, null).marginLeft

(getComputedStyle should work for each browser except IE, so if you are testing using PhantomJS it's ok)

Sign up to request clarification or add additional context in comments.

3 Comments

getComputedStyle(element, null) returns null (Chrome). Also, if I compile after appending the element I get the margin but not the offset... It's driving me insane.
I guess this offsetLeft property is working in a way that is little bit obscure... Anyway, add your suggestion regarding the append to your answer and I will mark it as correct, since the question issue ends there. Thank you very much for your help, I'll figure it out
A good practice would be to add a afterEach(function() { element.remove(); }); so that you don't end up with hundreds of elements in your test document.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.