Search code examples
javascriptselenium-webdriverjasmineprotractorbdd

Page objects in Protractor / Jasmine cause error: Failed: Cannot read property 'sendKeys' of undefined


I'm trying to get page objects to work for Protractor and Jasmine, and I'm getting "Failed: Cannot read property 'sendKeys' of undefined" when I try to use page objects.

I have read similar posts but they do not provide any answers for me or the situation is too different to be of any help to me.

I've checked every character of the two files. It appears that the test cannot see the page object file for some reason. BTW Protractor is installed Globally so I don't need to include it in the script.

Everything was working prior to converting to using the "test_page.js" file instead of the hard-coded values.

This is from a tutorial where his code was working fine.

/superhero-tests/test/test_spec.js file

//require() is a notjs function - https://nodejs.org/en/knowledge/getting-started/what-is-require/
const TestPage = require("../page-objects/test.page")
 
//This file runs the browser and executes the script.
describe('Super Hero Page',function(){
 
    var testPage
 
    //Jasmine: Before each test case, do something
    beforeEach(function(){    
        
        testPage = new TestPage()
 
        //Protractor: For non-angular sites
        browser.ignoreSynchronization = true
 
        //Open URL
        browser.get('file:///C:/projects/Protractor/Superhero/index.html') 
        
    })
 
    //Jasmine: After each test case, do something
    afterEach(function(){
 
        //Timer to prevent immediate closure of the window
        //browser.sleep(5000)
    })
 
    it('should load the correct URL',function(){
 
        //Enter text into form fields
        testPage.emailFld.sendKeys('a@b.com')
        testPage.passwordFld.sendKeys('Test.123')
 
        //Check some text in an element
        expect(testPage.loginTitleTxt.getText()).toEqual('Welcome. Please Log In.')
                
        //Check a HTML element's attribute
        expect( testPage.emailFld.getAttribute('value')).toEqual('a@b.com')
        
        //Non-precise matching - don't use with an empty string, will pass
        //expect( testPage.loginTitleTxt.getText() ).toContain('Welcome.')
 
    })
})
 

/superhero-tests/page-objects/test_page.js file

var TestPage = function(){}
 
TestPage.prototype = Object.create({},{
    emailFld: { get: function(){ return element( by.id('loginEmail') ) } }, //this is a page object
    passswordFld: { get: function(){ return element( by.id('loginPassword') ) } }, //this is a page object
    loginTitleTxt: { get: function(){ return element( by.id('login-title') ) } } //this is a page object
})
 
module.exports = TestPage
 

Solution

  • In the end I found alternative Page Object code which works (tutorial linked below):

    The page object:

    var TestPage = (function () {
        function TestPage() {
            this.emailFld = element( by.id('loginEmail') )
            this.passswordFld = element( by.id('loginPassword') )
            this.loginTitleTxt = element( by.id('login-title') )
        }
    
        return TestPage;
    
    })();
    
    module.exports = TestPage;
    

    The spec(test) file:

    //Test for Login page
    
    var TestPage = require("../page-objects/test.page")
    
    //Appears in command line
    describe('Login page tests', function(){
    
        var test = new TestPage();
    
        //Executed before the tests
        beforeEach(function(){
            //Protractor: For non-angular sites
            browser.ignoreSynchronization = true
    
            //Open URL
            browser.get('file:///C:/projects/Protractor/Superhero/index.html')
        })
    
        //Executed after the tests
        afterEach(function(){
    
            //Timer so we can see what is going on
            //browser.sleep(5000)        
    
        })
    
        //The test statements themselves
        it('should display all the Login page elements',function(){
            
            //With page object
            expect(  test.emailFld.getAttribute('placeholder')  ).toEqual('Enter email')
        })
    
    })
    

    It seems to make better use of the initial function and then do the module.exports at the end. The only other difference I can see is that they used var not const in the test file, but I don't know this would change much.

    https://teamgaslight.com/blog/getting-started-with-protractor-and-page-objects-for-angularjs-e2e-testing