intern.js refactoring to before block behavior not consistent - javascript

I'm using the intern.js library with Chai and BDD to test my javascript application. I have the following code:
// Login as admin
bdd.before(function() {
indexPage = new IndexPage(this.remote, adminUsername, adminPass);
});
bdd.it('should turn a user to an input box', function () {
return indexPage.login(baseUrl)
.clearLocalStorage()
.get(baseUrl + '#/details')
.findAllByCssSelector('.user-filter')
.findByName('user')
.clearValue()
.click().pressKeys(['Functional Test', '\uE015', '\uE006'])
.end()
.findByXpath('//td[#class="grid-column-user"]/span')
.click()
.end()
.findByXpath('//td[#class="grid-column-user"]/input')
.then(function (elem) {
assert.lengthOf(elem, 1, "Yay");
})
.end();
});
bdd.it('should get the error state class when incorrect input is added', function () {
return indexPage.login(baseUrl)
.clearLocalStorage()
.get(baseUrl + '#/details')
.findAllByCssSelector('.user-filter')
.findByName('user')
.clearValue()
.click().pressKeys(['Functional Tes', '\uE015', '\uE006'])
.end()
.findByXpath('//td[#class="grid-column-user"]/span')
.click()
.pressKeys(['adsf', '\uE006'])
.end()
.findByXpath('//td[#class="grid-column-user"]/input[#class="user-error"]')
.then(function (elem) {
assert.lengthOf(elem, 1, "User should be input");
})
.end();
});
So I want to extrapolate out a lot of the logic that is duplicated between the tests. It seems like the following code could be in the before block:
bdd.before(function() {
indexPage = new IndexPage(this.remote, adminUsername, adminPass);
testUser = indexPage.login(baseUrl)
.clearLocalStorage()
.get(baseUrl + '#/details')
.findAllByCssSelector('.user-filter')
.findByName('user')
.clearValue()
.click().pressKeys(['Functional Test', '\uE015', '\uE006'])
});
bdd.it('should get the error state class when incorrect input is added', function () {
return testUser.end()
.findByXpath('//td[#class="grid-column-user"]/span')
.click()
.pressKeys(['adsf', '\uE006'])
.end()
.findByXpath('//td[#class="grid-column-user"]/input[#class="user-error"]')
.then(function (elem) {
assert.lengthOf(elem, 1, "User should be input");
})
.end();
});
When I put this code into the before block and store it as a variable, the behavior of the code doesn't run as it did when it was all in one long chained call and not in the before block. I'm not sure what I'm doing wrong here as I've tried multiple different iterations on what I've extrapolated out.
Thanks!

In your original code, you're resetting the page state for each test by logging in to a new session and clearing local storage. In your new code, you're only doing that once at the beginning of the suite, so all of the tests within the suite are going to run in the same session on the test page.
To replicate the behavior of your original tests, use a beforeEach rather than a before.

Related

doc.createTreeWalker is not a function

When I click the button with text 'Create' the text is replaced to 'Confirm?'
This is the HTML:
and the pageObject:
create() {
return cy.get('im-page.hydrated', { includeShadowDom: true })
.find('im-button', { includeShadowDom: true })
.eq(1)
.find('button', { includeShadowDom: true })
.click({ force: true })
}
confirmBtn() {
return cy.get('im-page.hydrated').find('im-button')
.eq(1)
.find('button.success.outline')
.contains('Confirm?')
}
Then when Cypress click on Confirm I got this error:
There's an issue logged doc.createTreeWalker is not a function #20813 but not yet resolved.
From the source code the doc part refers to the previous subject, i.e the element before the .find() which above is cy.get('im-page.hydrated').find('im-button').eq(1).
My guess is because the button changes on the Create click, something in that previous subject becomes invalid when you try to access the Confirm button.
A couple of ideas to try (just guesses at this stage)
// using jquery to avoid the treeWalker (used by the .find() command)
cy.get('im-page.hydrated im-button:eq(1) button.success.outline:contains(Confirm)')
// using an alias and "withinSubject" option to explicitly define `doc`
cy.get('im-page.hydrated im-button:eq(1)`).as('parent')
cy.get('#parent').then($parent => {
cy.get('button.success.outline:contains(Confirm)', { withinSubject: $parent })
You should turn on shadowDOM globally to avoid missing any parts that need it.
// cypress.json
{
...
"includeShadowDom": true
}
#3 - just do a simple search for "Confirm", since likely only one thing at a time needs confirming.
cy.contains('button.success.outline', 'Confirm')

My refactored vanilla JS button click is not working

I am working in refactoring a jQuery web sdk into a vanilla JavaScript one.
The original jQuery file has a button click like so:
sdk.initialize().then(function() {
$('#loginButton').click(function() {
// more logic in here
});
});
I started my refactor like so:
sdk.initialize().then(() => {
document.querySelector('#loginButton').addEventListener('click', (event) => {
event.preventDefault();
console.log('you clicked me!');
});
});
If the code looks right, then it might be a scope issue, as this is inside of RequireJS like so:
require(['XmSdk', 'XmUIHandler'], (xm, xmui) => {
const sdk = XmSdk();
// the above code resides in here
});
When I click on the button, I get no output and no error.
I even tried this:
sdk.initialize().then(() => {
console.log('initialized!');
}).catch((err) => {
console.log('Failed to initialize');
});
and I get no output and no error.
try with a function instead, not an arrow function on event handler
sdk.initialize().then(() => {
document.querySelector('#loginButton').addEventListener('click', function(event){
event.preventDefault();
console.log('you clicked me!');
});
});

Executing Multiple IT blocks inside a Protractor For loop for web testing

We need to use for loop after login to a web page and perform multiple tests inside the for block on the page. My ideal test scenario should be like the snippet below. We have a table that have buttons on each of the row, and we will navigate to the next page for that particular button and validate data. currently we plugged all expects and assertions in a single IT block, but that is not a good solution. We need to split the sections of tests in different IT block.
require('..\\waitAbsent.js');
require("../node_modules/jasmine-expect/index.js");
var EC = protractor.ExpectedConditions;
describe('Student Enrollment Page Content Validation', function() {
beforeAll(function () {
browser.driver.manage().window().maximize();
browser.get(globalVariables.loginMain);
globalVariables.Email_Input_box.sendKeys(globalVariables.Demo_User);
globalVariables.Password_Input_Box.sendKeys(globalVariables.Demo_PWD);
globalVariables.Submit_Button.click();
browser.wait(EC.invisibilityOf(globalVariables.Submit_Button), 25000, 'submit button is not disappearing yet');
});
async function Row_Numbers() {
const RowCount = (globalVariables.tableData_Dashboard.all(by.tagName("tr")).count()).then(function(RC){
return RC;
});
}
for (var i = 1; i<Row_Numbers(); i++){
function tableData(n){
var row_1 = globalVariables.tableData_Dashboard.all(by.tagName("tr")).get(n);
// get cell values
var cells = row_1.all(by.tagName("td"));
it ('should return the data fo the first cell', function(){
var Student_ID = cells.get(0).getText().then(function (SID) {
console.log(SID);
return SID;
});
expect(Student_ID.toEqual('Something'));
});
it ("should show the button in this row", async function(){
const Button = globalVariables['Edit_Button_' + n];
// console.log(Button)
expect(await Button.isDisplayed());
Button.click();
// do some thing
});
}tableData(i)
}
});
When we run the test using this script, we get the following error:
E/launcher - Error while waiting for Protractor to sync with the page: "both angularJS testability and angular testability are undefined. This could be either because this is a non-angular page or because your test involve
s client-side navigation, which can interfere with Protractor's bootstrapping. See https://github.com/angular/protractor/issues/2643 for details"
How can I use the for loop to achieve our goal?
I have a couple of comments about this.
Firstly, Your angular error is because the Row_number function you declare is outside of any it block and therefore running before your beforeAll has run.
Next there should be no need for your tableData function as it appears replacing it's parameter n with the i counter from the loop would have the same effect.
Finally if your code needs to go across multiple pages to achieve these tests it will likely be much better to use a data-driven approach and write separate data files for each test. Do the values of these tables row change or would they be consistent?
Update:
This approach may look something like this but I have not tested this.
beforeAll(function () {
browser.driver.manage().window().maximize();
browser.get(globalVariables.loginMain);
globalVariables.Email_Input_box.sendKeys(globalVariables.Demo_User);
globalVariables.Password_Input_Box.sendKeys(globalVariables.Demo_PWD);
globalVariables.Submit_Button.click();
browser.wait(EC.invisibilityOf(globalVariables.Submit_Button), 25000, 'submit button is not disappearing yet');
});
it('test it', async () => {
globalVariables.tableData_Dashboard.all(by.tagName("tr")).forEach((row) => {
var cells = row.all(by.tagName("td"));
var Student_ID = cells.get(0).getText().then(function (SID) {
console.log(SID);
return SID;
});
expect(Student_ID.toEqual('Something'), 'should return the data fo the first cell');
const Button = globalVariables['Edit_Button_' + n];
// console.log(Button)
expect(Button.isDisplayed(), 'should show the button in this row').toBe(true);
Button.click();
// do some thing
});
})

.type and .click selectors in Nightmare.js

I have been working with nightmare.js to run tests using mocha. This is my first time working with these framework/libraries. I am currently creating a test that loads a login page on a web app im working on and attempts to sign in. This is the code I am using as a reference
Source: https://segment.com/blog/ui-testing-with-nightmare/
describe('Login Page', function () {
describe('given bad data', () => {
it('should fail', done => {
nightmare
.goto('https://gethoodie.com/auth')
.on('page', (type, message) => {
if (type == 'alert') done()
})
.type('.login-email-input', 'notgonnawork')
.type('.login-password-input', 'invalid password')
.click('.login-submit')
.wait(2000)
.end()
.then()
.catch(done)
})
})
The main question I have about tis code is the .type and .click functions. Reading the nightmare api both of these functions work like so
.type(selector[, text])
Enters the text provided into the selector element. Empty or falsey values provided for text will clear the selector's value..type(selector[, text])
I have tried to implement this functionality using the code below
describe('/ (authentication)', () => {
it('should pass by logging into the system', done => {
// testing urls will be `http://localhost:port/path`
nightmare.goto('http://localhost:4000/login')
.on('page', (type, message) => {
if (type === 'alert') done()
})
.type('input[id="username"]', 'admin')
.type('input[id="password"]', 'toetagging')
.click('button[data-reactid=".0.0.1.3.0"]')
.wait(2000)
.end()
.then()
.catch(done)
})
})
My main question is, what exactly is the selector and how do I get it?
A selector is useful to match some node(s) in the DOM. Suppose, you have the following DOM:
<div class="login-submit">
<input id='input-login' type="text" class="fancy" name="login" />
</div>
To select the input box, you can use the following selectors, as you would with CSS:
input.fancy
.login-submit input
#input-login
input[name=login]
Let's say that with nightmare you wanted to type into the input box, you could use the following code:
nightmare.type('input[name=login]', 'superwoman')
Here 'input[name=login]' is a selector, and 'superwoman' is the text that you want nightmare to type inside the input line.

Protractor sequencing basic test. Run test after login

I am trying to figure out how to make this basic test pass consistently.
describe('home page', function () {
beforeEach(function () {
browser.driver.get('https://localhost:0000/xxxx/');
});
it('should have a title', function () {
var userName = browser.driver.findElement(by.id('userid_inputtext')).sendKeys('userNameXX');
var passWord = browser.driver.findElement(By.id("password_inputtext")).sendKeys("passWordXX");
var login = browser.driver.findElement(By.id('sign_in_button'));
login.click();
browser.driver.getCurrentUrl();
browser.driver.getTitle().then(function (title) {
expect(title).toEqual('Webpage Title');
});
});
});
The login in page is not Angular but after login it loads the Angular app. Right now my test is passing some of the time. The problem is that sometimes it picks up the title of the login page and sometimes it picks up the title of the home page(I want it to consistently test the title of the home page after login).
I am have played around with using a promise and browser.wait a little bit but have not really nailed this down. Any advice would be great!
Couple of things I could think of - most of the methods in protractor API are asynchronous and return promises.
In your login page once you login.click(), your homePage takes some time to load and therefore following async methods like - browser.getCurrentUrl & browser.getTitle are called first. This happens inconsistently as you pointed out.
Also you should assign variables to element locators and then perform actions on them! You should use Page Objects to store your locators. Please refer the Official Protractor Style Guide
browser.getCurrentUrl also returns a promise you have to resolve it as well.
To solve this issue you should use browser.wait in the right way:
describe('home page', function () {
beforeEach(function () {
browser.driver.get('https://localhost:0000/xxxx/');
});
it('should have a title', function () {
var userName = browser.driver.findElement(by.id('userid_inputtext'));
userName.clear();
userName.sendKeys('userNameXX');
var passWord = browser.driver.findElement(By.id("password_inputtext"));
passWord.clear();
passWord.sendKeys("passWordXX");
var login = browser.driver.findElement(By.id('sign_in_button'));
browser.driver.wait(login.click(),5000,'homePage should be loaded within 5 secs'); // would throw an error if the page takes more than 5 secs to load
// you could also use `browser.driver.sleep(500)` but not advisable as sleeps slow the tests down!
browser.driver.getCurrentUrl().then(function (url) {
expect(url).toEqual('homePage url');
});
browser.driver.getTitle().then(function (title) {
expect(title).toEqual('Webpage Title');
});
});
});
Also since your login page is non-angular, you could write your test in the right way which is to use browser.ignoreSynchronization and using the protractor api methods in a cleaner way.
describe('home page', function () {
beforeEach(function () {
browser.get('https://localhost:0000/xxxx/');
});
it('should have a title', function () {
browser.ignoreSynchronization = true; // set it to true for non-angular pages(loginPage)
var userName = element(by.id('userid_inputtext'));
userName.clear();
userName.sendKeys('userNameXX');
var passWord = element(By.id("password_inputtext"));
passWord.clear();
passWord.sendKeys("passWordXX");
var login = element(By.id('sign_in_button'));
browser.wait(login.click(),5000,'homePage should be loaded within 5 secs');
browser.ignoreSynchronization = false; // set it to false for angular pages(homePage)
browser.getCurrentUrl().then(function (url) {
expect(url).toEqual('homePage url');
});
browser.getTitle().then(function (title) {
expect(title).toEqual('Webpage Title');
});
});
});
Notice there is no need of accessing the browser.driver object, you can directly use protractor's methods!

Categories

Resources