Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

TypeError: Cannot read property 'ELEMENT' of undefined #915

Closed
mgol opened this issue Jun 10, 2014 · 21 comments
Closed

TypeError: Cannot read property 'ELEMENT' of undefined #915

mgol opened this issue Jun 10, 2014 · 21 comments

Comments

@mgol
Copy link
Member

mgol commented Jun 10, 2014

When issues from #902 were resolved & Protractor 0.24.1 was released, I tried again to upgrade from 0.22.0. Unfortunately, I get a cryptic error:

  1) Item panel toolbox note tab adding notes (type: pin) should remove just created notes
   Message:
     TypeError: Cannot read property 'ELEMENT' of undefined
   Stacktrace:
     TypeError: Cannot read property 'ELEMENT' of undefined
==== async task ====
WebDriver.call(function)
==== async task ====
Asynchronous test function: it()
Error
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:316:17)
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:251:13)
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:243:9)

The test itself is as follows (line 316 column 17 points to the it):

it('should remove just created notes', function () {
    tabElems.notes.element(by.repeater('note in page.notes'))
        .then(function (collection) {
            collection.forEach(function (noteElem) {
                // Activate a note.
                noteElem.element(by.css('.item-header')).click();

                noteElem.element(by.css('.delete')).click();
            });
            tabElems.notes
                .all(by.repeater('note in page.notes'))
                .then(function (collection) {
                    expect(collection.length).toBe(0);
                });
        });
});

The `beforeEach block:

beforeEach(function () {
    ['info', 'search', 'view', 'notes', 'bookmarks'].forEach(function (tabName) {
        tabTriggerElems[tabName] = $('cbn-item-panel-toolbox .tab-links button.tab-' + tabName + '-link');
        tabElems[tabName] = $('cbn-item-panel-toolbox .tabs-content .tab-' + tabName);
    });
});

The whole test file is quite complicated so for now I'm just sharing this info. The error should be more descriptive anyway, especially that it points just to the test definition and not to anything inside it. Does it say a lot to you? I can try to isolate sth further if there's a need but that might not be easy.

@juliemr
Copy link
Member

juliemr commented Jun 10, 2014

This is due to the breaking changes with ElementFinder in 0.24.0 - try

it('should remove just created notes', function () {
    tabElems.notes.element(by.repeater('note in page.notes')).each(function (noteElem) {
                // Activate a note.
                noteElem.element(by.css('.item-header')).click();

                noteElem.element(by.css('.delete')).click();
            });
            tabElems.notes
                .all(by.repeater('note in page.notes'))
                .then(function (collection) {
                    expect(collection.length).toBe(0);
                });
        });
});

@mgol
Copy link
Member Author

mgol commented Jun 11, 2014

@juliemr Your code has mismatched brackets, I assume you meant:

it('should remove just created notes', function () {
    tabElems.notes.element(by.repeater('note in page.notes'))
        .each(function (noteElem) {
            // Activate a note.
            noteElem.element(by.css('.item-header')).click();

            noteElem.element(by.css('.delete')).click();
        });
    tabElems.notes
        .all(by.repeater('note in page.notes'))
        .then(function (collection) {
            expect(collection.length).toBe(0);
        });
});

(why did you change only the first part of the code?)

Anyway, it doesn't work:

  1) Item panel toolbox note tab adding notes (type: pin) should remove just created notes
   Message:
     TypeError: Object [object Object] has no method 'each'
   Stacktrace:
     TypeError: Object [object Object] has no method 'each'
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:396:26)
==== async task ====
Asynchronous test function: it()
Error
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:394:17)
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:329:13)
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:321:9)

@juliemr
Copy link
Member

juliemr commented Jun 11, 2014

Ah, sorry, one more typo - you want to get an ElementArrayFinder, so you need to do:

it('should remove just created notes', function () {
    tabElems.notes.all(by.repeater('note in page.notes')) // Change here - .all instead of .element
        .each(function (noteElem) {
            // Activate a note.
            noteElem.element(by.css('.item-header')).click();

            noteElem.element(by.css('.delete')).click();
        });

@mgol
Copy link
Member Author

mgol commented Jun 11, 2014

With the following code:

it('should remove just created notes', function () {
    tabElems.notes.all(by.repeater('note in page.notes'))
        .each(function (noteElem) {
            // Activate a note.
            noteElem.element(by.css('.item-header')).click();

            noteElem.element(by.css('.delete')).click();
        });
});

I get the same error as at the beginning:

  1) Item panel toolbox note tab adding notes (type: pin) should remove just created notes
   Message:
     TypeError: Cannot read property 'ELEMENT' of undefined
   Stacktrace:
     TypeError: Cannot read property 'ELEMENT' of undefined
==== async task ====
WebDriver.call(function)
==== async task ====
Asynchronous test function: it()
Error
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:394:17)
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:329:13)
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.defs.js:321:9)

where line 394:17 points to it.

As for the change - how is it possible now to get the whole collection and not only iterate over it? I was using then & forEach here so this could be replaced but in a different place I needed to check the size of the collection so I need it as a whole.

@mohsen1
Copy link

mohsen1 commented Jun 13, 2014

FYI we had a conversation about this at #922

@hankduan
Copy link
Contributor

Does this work for you? #922 (comment)
It's because you're removing elements with clicks. (Read the link I sent for details)

The way you wrote your example (especially the count part) is not very clean, and can be cleaned up like this:

it('should remove just created notes', function () {
    var allElems = tabElems.notes.all(by.repeater('note in page.notes'));

    allElems.then(function (collection) {
        for (i = collection.length; i >= 0; --i) {
            var noteElem = collection[i];

            // Activate a note.
            noteElem.element(by.css('.item-header')).click();
            noteElem.element(by.css('.delete')).click();
        }
    });
    expect(allElems.count()).toEqual(0);
});

I didn't try running this, so fix syntax as you encounter them

hankduan added a commit to hankduan/protractor that referenced this issue Jun 16, 2014
See angular#915
- to make error more specific instead of propagate later
hankduan added a commit that referenced this issue Jun 17, 2014
See #915
- to make error more specific instead of propagate later
@hankduan hankduan self-assigned this Jun 17, 2014
@hankduan
Copy link
Contributor

I think this is resolve? Please reopen if necessary

@mgol
Copy link
Member Author

mgol commented Jun 23, 2014

@hankduan The issue is not resolved for me, could you reopen? (I can't do it myself).

I don't understand what you're proposing, could you elaborate what's different to what I currently have?

I tried again with the newest Protractor 0.24.2 and the error is now less cryptic but still surprising. With the test:

it('should remove just created notes', function () {
    tabElems.notes.all(by.repeater('note in page.notes'))
        .then(function (collection) {
            collection.forEach(function (noteElem) {
                noteElem.element(by.css(('.item-header')).click();
                noteElem.element(by.css('.delete')).click();
            });
        });
});

I get the following error:

  1) Item panel toolbox note tab adding notes (type: pin) should remove just created notes
   Message:
     Error: Index out of bound. Trying to access index:1, but locator: by.repeater("note in page.notes") only has 1 elements
   Stacktrace:
     Error: Index out of bound. Trying to access index:1, but locator: by.repeater("note in page.notes") only has 1 elements
==== async task ====
WebDriver.call(function)
==== async task ====
Asynchronous test function: it()
Error
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.js:394:17)
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.js:329:13)
    at null.<anonymous> (/Users/mgol/Documents/projects/bn/cbn/repo/polona-gui/test/e2e/spec/item-panel.js:321:9)

I get the same error when I change the test to what @juliemr suggested, i.e.:

it('should remove just created notes', function () {
    tabElems.notes.all(by.repeater('note in page.notes'))
        .each(function (noteElem) {
            noteElem.element(by.css('.item-header')).click();
            noteElem.element(by.css('.delete')).click();
        });
});

@hankduan hankduan reopened this Jun 23, 2014
@hankduan
Copy link
Contributor

It's just like doing this in javascript:

var x = [1,2,3], len = 3;
for (i=0; i<len; ++i) {
  console.log(i + ':' + x[i]);
  if (x[i] === 1) {x.splice(i,1);} // Remove the element if it's === 1
}

output: 
0:1
1:3
2:undefined

You can see that the last element is undefined. In protractor, instead of returning undefined, we just throw an index out of bounds exception

@mgol
Copy link
Member Author

mgol commented Jun 23, 2014

I don't understand what can return undefined here. I'm just selecting by a repeater - either sth matches it or it doesn't. How can sth match as undefined?

EDIT: Ah, my deleting notes might be causing the issue... Let me check.

@mgol
Copy link
Member Author

mgol commented Jun 23, 2014

@hankduan You were right. :) It's a tricky one! I missed it because I just saw some selection and actions and I didn't add 2 plus 2 and see that elements are actually removed.

I wonder if an error message might be a little more user-friendly in such cases. After all, if we're iterating over an array via forEach and there are empty spaces causing errors, sth must have removed those elements after selection; a little hint to the user about that possibility might help.

This issue is resolved, though. Thanks!

@mgol mgol closed this as completed Jun 23, 2014
@hankduan
Copy link
Contributor

What output would you suggest that would make it less confusing?

Protractor cannot fix the problem for the user because there is no way for protractor to know that someone removed an element, because the user is not removing the element via a protractor API. (they're just clicking and click could mean anything).

But I can do something like. "Index out of bound (maybe the element no longer exists?)". However, this will be thrown even if someone does something like this elements(by.tag(xyz)).get(100000).getText(). That's why I left out any wording that could mean that an element was there before.

@mgol
Copy link
Member Author

mgol commented Jun 23, 2014

Yeah, I can see the problem. Removing an element from an array is low-level and various high-level operations that might be bitten by it differ quite a bit.

I think, though, that sth along the lines of maybe the element was removed? would explain my case and the one you mentioned is clear enough that this message shouldn't confuse the user.

I'm obviously biased, though by the issue that happened to me personally. Your call. :)

@juliemr
Copy link
Member

juliemr commented Jun 23, 2014

FWIW I think the current error message is pretty good. "Index out of bounds" is exactly what's happening.

@jrhite
Copy link

jrhite commented Jul 13, 2014

I still see the problem in some of my protractor tests:

Failures:

  1) customers page display customers correctly when filtered
   Message:
     TypeError: Cannot read property 'ELEMENT' of undefined
   Stacktrace:
     TypeError: Cannot read property 'ELEMENT' of undefined
==== async task ====
WebDriver.findElements(By.cssSelector("[ng-click=\"setCustomer(row.entity.customer_id)\"]"))
        at     test/e2e/customers/customers.spec.js:33:15
    at Array.forEach (native)
==== async task ====
Asynchronous test function: it()
Error
    at null.<anonymous>     (test/e2e/customers/customers.spec.js:27:3)

The code that hits this problem is:

it('display customers correctly when filtered', function() {
  customersPage.search(100000);

  var foundCustomer = protractor.promise.defer();

  element.all(by.css('[ng-click="setCustomer(row.entity.customer_id)"]')).each(function(element) {
    element.getText().then(function(customerId) {
      if (+customerId === 100000) {
        foundCustomer.fulfill(true);
      }
    });
  });

  expect(foundCustomer).toBeTruthy();
});

@hankduan
Copy link
Contributor

Can you share the portion of the app you're testing? I just tested this and cannot reproduce.
Also, what version of protractor are you using?

@jrhite
Copy link

jrhite commented Jul 13, 2014

Using "protractor": "~0.24.0"

The controller code is pasted in below. Perhaps the key is here the use of ng-grid. I search for items using an input field associated with the ng-grid. And after doing the search customersPage.search(100000), this makes ng-grid do a server side call and load data based on the search criteria, so the new data gets loaded in asynchronously (customerService.getCustomersAsync) and then I expect certain results/rows to be in the grid.

customerService.getCustomersAsync()

just calls into $http() posting a JSON request and getting back a JSON response:

Note that sometimes I see the error and sometimes I don't.

function($rootScope, $scope, $q, customerService, ngGridBuilder) {
  <snip>

  ngGridBuilder.Builder($scope, 'customerTable', customerService.getCustomersAsync)
    .withPagingOptions({pageSize: 25})
    .withFilterOptions({ filterText: '' })
    .withGridOptions({
      columnDefs: [
        { field: 'customer_id', displayName: 'CustomerId' },
        { field: 'name', displayName: 'Customer Name' },
        { field: 'customerType', displayName: 'Customer Type' },
        { field: 'dateAdded', displayName: 'Date Added' },
        { field: 'dateRegistered', displayName: 'Date Registered' },
      ] 
    })
    .build();     

  <snip>

  $scope.customerTable.loadDataAsync();
}

And further below is the html, but specifically

<input type="text" placeholder="Search" ng-model="customerTable.filterOptions.filterText" class="livesearch">

is what customersPage.search(100000) enters text into in the test.

<div class="main_section">
  <h2>Please choose a customer to launch Merchant Center</h2>
  <div class="action_bar cf">
        <div class="livesearch_wrapper">
          <input type="text" placeholder="Search" ng-model="customerTable.filterOptions.filterText" class="livesearch">
          <i class="fa fa-search" ng-show="!customerTable.filterOptions.filterText && !customerTable.loading"></i>
          <i class="fa fa-spinner fa-spin" ng-show="customerTable.loading"></i>
          <i class="fa fa-times" ng-show="customerTable.filterOptions.filterText && !customerTable.loading" 
            ng-click="clearCustomerTableFilter()"></i>       
        </div>
  </div>
  <grid-header namespace="customerTable"></grid-header>
  <div id='customerTable' ng-if='customerTable.isDataAvailable()' ng-grid='customerTable.gridOptions' class='gridStyle'>
  </div>         
</div>

@mgol
Copy link
Member Author

mgol commented Jul 13, 2014

@jrhite

Using "protractor": "~0.24.0"

This is not the newest version, try 1.0.0-rc4.

@jrhite
Copy link

jrhite commented Jul 14, 2014

Upgraded to 1.0.0-rc4 and still see the same problem.

If I have time I'll try to create a more distilled example of the problem.

On Sun, Jul 13, 2014 at 5:19 PM, Michał Gołębiowski <
notifications@github.com> wrote:

@jrhite https://github.com/jrhite

Using "protractor": "~0.24.0"

This is not the newest version, try 1.0.0-rc4.


Reply to this email directly or view it on GitHub
#915 (comment).

@smahalingam
Copy link

Any got work around for this or is it still an issue ?

@hankduan
Copy link
Contributor

Not sure what the issue is in this case. The original issue is resolved.
This issue is very stale, can you open a new issue and perhaps link to this issue instead.

@hankduan hankduan removed their assignment Nov 4, 2015
bodyduardU pushed a commit to bodyduardU/protractor that referenced this issue Dec 5, 2022
See angular/protractor#915
- to make error more specific instead of propagate later
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants