so I am new to Cucumber and I just wanted to create an easy test that selects some tabs. I created a page object with this function
var tabOne = $('[ui-sref="tab1"]');
this.clickTabOne = function() {
tabOne.click();
};
Then in the step defintion..
this.Then(/^On the home page I switch to first tab$/, function() {
return homePage.clickTabOne();
});
And then finally, the feature file
Feature: tabs test
#warmup
Scenario: As a user
I want to select through tabs
Given I land on the homepage
Then I click the first tab
I understand that the gherkin is terrible and the test makes no sense, but I am new to JavaScript, protractor & cucumber so I am trying to wrap my head around this.. Why is it saying steps are undefined? There is a segment in the printout that says "//Write code here that turns the phrase above into concrete actions" but I already have an action when I say return homePage.clickTabOne(); Thanks for the help!
When Cucumber finds a matching Step Definition it will execute it. There isn't any Step definition that matches the steps in your feature file therefore the error.
You should define steps that match those used on the feature. In your case:
this.Given(/^I land on the homepage$/, function() {
// Your code
});
this.Then(/^I click the first tab$/, function() {
// Your code
});
Cucumber will use the Regexps in order to perform the matches and will execute the associated callback.
Hope it helps
Related
I get the folowing failure on several features of Cucumber "NoSuchElementError: No element found using locator: By(css selector, h1)"
I have tried to set a bigger timeout in order to give Cucumber more time to find the elements, but it doesn't seem to work
Here are the main components of teh test:
cardTitle.feature:
#cardTitle-feature
Feature: See card title
Display the card title
#cardTitle-scenario
Scenario: Card Page
Given I am on the card page
When I do nothing
Then I should see the card title
app.steps.ts:
// Go to the card - Display the title
Given(/^I am on the card page$/, async () => {
await page.navigateToCard();
});
When(/^I do nothing$/, () => {
});
Then(/^I should see the card title$/, async () => {
expect(await page.getCardTitleText()).to.equal('Profile');
});
app.po.ts:
navigateToCard() {
this.sleep(3000);
return browser.get('/card');
}
getCardTitleText() {
this.sleep(3000);
return element(by.css('h1')).getText();
}
card.html:
<div class="profile-container">
<!-- EXAMPLE TOP NAV -->
<h1>Profile</h1>
...
I think that this could happen because "card" may not be accessible without login in the application. If this is the problem, how could I perform a test that logs into the application and then checks the "h1" element?
Thank you!
Great write up! Thanks for all the detail, that helps a lot. Here are my thoughts:
Don't you need to await the sleeps in your page object? Or promised chain the sleep to the get, if you're avoiding async functions in your page object for some reason.
Given that you're using protractor, and presumably this is an angular app, I'm surprised that you would need sleeps at all. Usually the built in waitForAngular that runs as part of every browser.get should wait until the page is fully loaded, before continuing
Your css looks perfect, that should work just fine. You can locate both html tags and attribute with css.
I'm assuming this.sleep is a method in your page object base class or something? I was kind of expecting browser.sleep.
Btw, I've gotten a lot of mileage out of the $ and $$ aliases in protractor, they're so clean. You could shorten
return element(by.css('h1')).getText();
To just
return $('h1').getText();
If you prefer.
I have a test which contains steps that I'll want to reuse in multiple files.
I'm thinking I could create a file called common.js, list all the functions in there and just call as and when I need.
Is this a recommended approach?
The only issue I feel is having a super long file of common methods and if i seperate then I'd need to use lots of require statements.
The simplest would be to do what you have hinted to make a commonSpec.js file and use it anywhere by importTest() which would be something like this :
commonSpec.js
describe('Common Steps that will be used by all', () => {
it('Can log in', () => {
//log in code
});
it('land on a particular page', () => {
// assertion code for the particular page
});
});
commonSpecUsed.js
describe("Common Specs", () => {
importTest("common specs", './commonSpec.js');
});
The above approach is best and simple when they share the same specs and assert the same values. But when the assertions are different for e.g
A normal user will land on a simple user page
Registered user will land on their "my account" page
Admin will land on their dashboard page ..etc..etc..
Then you might want to make your commonSpec.js file more dynamic by enabling it to accept parameters. This would be entirely based on your test requirements. Can be more helpful if you could please share some code snippet.
Hope this helps.
One option is to set up Page Objects, as defined in the official docs. I also have a video covering subject on YouTube.
If you don't want to do page objects, you can add custom commands to WebdriverIO using the 'addCommand' command.
I am having a hard time trying to adjust to asynchronous using node.js. I ran into an issue when using selenium-webdriver and the page object pattern. I feel like somethings have to be synchronous when doing automation testing or your tests will fail because you clicked a button before inserting data. I am having an issue similar to this. I want to add an employee and then search for the employee, but the search for employee is performing before add employee.
var employee = new Employee('grimlek', 'Charles', 'Sexton', 'TitleTitle',
'Upper Management', 'Company Admin', 'Contractor', '-7', 'Remote',
'05212016', '3369407787', '3368791234', 'charles#example.com',
'charles.sexton', 'Skype', 'abcdefgh');
driver.get('https://website.com/login')
.then(function() {
//This behaves as intended
loginPage.login('company.admin', 'password') })
.then(function() {
//Add employee
employeePage.addEmployee(employee) })
.then(function() {
//Search for employee after employee is added
employeePage.searchEmployee(employee)});
EmployeePage Object
var EmployeePage = function (driver) {
this.addEmployee = function (employee) {
driver.findElement(webdriver.By.css('button[class=\'btn btn-default\']')).then(function (element) {
//
//Search employee function is done before the line below this
//
element.click();
}).then(function () {
setTimeout(function () {
driver.findElement(webdriver.By.id('employee_username')).then(function (element) {
element.sendKeys(employee.username);
});
driver.findElement(webdriver.By.id('employee_first_name')).then(function (element) {
element.sendKeys(employee.firstName);
});
driver.findElement(webdriver.By.id('employee_last_name')).then(function (element) {
element.sendKeys(employee.lastName);
});
driver.findElement(webdriver.By.id('employee_title_id')).then(function (element) {
element.sendKeys(employee.title);
});
driver.findElement(webdriver.By.id('employee_role')).then(function (element) {
element.sendKeys(employee.role);
});
}, 5000);
});
//
//
//Search employee should occur when the thread leaves the function
//
};
this.searchEmployee = function (employee) {
driver.findElement(webdriver.By.css('input[class=\'form-control ng-pristine ng-valid\']')).then(function(element) {
element.sendKeys(employee.firstName + ' ' + employee.lastName);
});
};
};
module.exports = EmployeePage;
I know that both searchEmployee and addEmployee functions don't return a promise and I am trying to chain them with the .then function. I do believe this is sorta my problem but I need help with how it should be done and not how I can rig it. Should I use callbacks? I have worked on this problem for going on four hours now and I have tried googling and doing research on various topics. If I didn't provide enough code please let me know and I will provide a simplified runnable example.
A laudable goal is to make each test independent. If a change is made to the application (e,g, bug fix) only the impacted test(s) need to be executed. Also, it makes moving to grid thinkable.
But this is difficult to achieve in practice. Your test has to include all tests needed to satisfy the prerequisites.
Cucumber has feature files that include scenarios Each scenario is a test. Scenarios are executed in the order they are listed in the feature file. So one way to organize things is to include all the prerequisite scenarios before your test in a feature file, You can add tag(s) before the Feature statement so that when you execute that tag the entire feature file runs. Perhaps the first scenario resets (a subset of) the database to a know state.
The trick would be to run features in parallel on multiple machines. If you point those multiple clients to the same server beware that the features should not create or update overlapping entities that could collide when written to the database by the server. E.g. "What do you mean that user 'tom' already exists?" Each feature needs to create a unique user name.
The way of approach using cucumber is to divide you steps for every individual operation.
Ex:
Given I am on XYZ Form
And I provide all form details
In above case, for step And I provide all form details you will be including all the fields in step definition and start filling the fields say name, last name, address in single step definition.
Instead of this we should divide the step for every individual field like:
Given I am on XYZ Form
And I provide name details in XYZ Form
And I provide last name details in XYZ Form
And I provide address details in XYZ Form
And then we will be writing 3 step definition which of course will be running sequentially.
You may feel that the typing work got increased and step definitions got increased unnecessarily, but this will actually help you when a field gets removed from the application itself, you will be only needing to delete related step from future file.
More over you can easily test validation for fields by just commenting one of the step in your feature file.
And your code will be more easy to maintain as every steps is working independently.
And of course sequential work will get achieved.
In one of our tests, we need to make sure that the tab keyboard navigation inside a form is performed in the correct order.
Question: What is the conventional way to check the tab navigation order with protractor?
Currently we are solving it by repeating the following step for as many input fields existing in a form (code below):
check the ID of the currently focused element (using getId())
send TAB key to the currently focused element
Here is the example spec:
it("should navigate with tab correctly", function () {
var regCodePage = new RegCodePage();
browser.wait(protractor.ExpectedConditions.visibilityOf(regCodePage.title), 10000);
// registration code field has focus by default
expect(regCodePage.registrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Remember Registration Code
regCodePage.registrationCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.rememberRegistrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Request Code
regCodePage.rememberRegistrationCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.requestCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Cancel
regCodePage.requestCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.cancelButton.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved back to the input
regCodePage.cancelButton.sendKeys(protractor.Key.TAB);
expect(regCodePage.registrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
});
where regCodePage is a Page Object:
var RegCodePage = function () {
this.title = element(by.css("div.modal-header b.login-modal-title"));
this.registrationCode = element(by.id("regCode"));
this.rememberRegistrationCode = element(by.id("rememberRegCode"));
this.requestCode = element(by.id("forgotCode"));
this.errorMessage = element(by.css("div.auth-reg-code-block div#message"));
this.sendRegCode = element(by.id("sendRegCode"));
this.cancelButton = element(by.id("cancelButton"));
this.closeButton = element(by.css("div.modal-header button.close"));
};
module.exports = RegCodePage;
It is working, but it is not really explicit and readable which makes it difficult to maintain. Also, another "smell" in the current approach is a code duplication.
If the current approach is how you would also do it, I would appreciate any insights about making it reusable.
I think the PageObject should define a tab order list, since that is really a direct property of the page, and should be expressible as simple data. An array of items seems like a sufficient representation, so something like:
this.tabOrder = [ this.registrationCode, this.rememberRegistrationCode, this.requestCode, this.cancelButton ];
Then you need a bit of generic code that can check a tab order.
function testTabOrder(tabOrder) {
// Assumes TAB order hasn't been messed with and page is on default element
tabOrder.forEach(function(el) {
expect(el.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
el.sendKeys(protractor.Key.TAB);
});
}
Then your test would be something like:
it('has correct tab order', function() {
var regCodePage = new RegCodePage(); // this should probably be in the beforeEach
testTabOrder(regCodePage.tabOrder);
});
Of course, this assumes each element has a "getId()" method that works. (That seems like a reasonable assumption to me, but some environments may not support it.)
I think this keeps the tab-order nicely isolated on the PageObject (so its easy to keep in sync with the page content and doesn't get lost in the code that verifies the order). The testing code seem "optimistic" (I suspect the real world will introduce enough problems that you will end up expanding this code a bit).
I haven't tried any of this yet, so feel free to downvote if this doesn't work. :)
Also, I believe the forEach loop will work as-is, but I wouldn't be surprised if it needs some more explicit promise handling to make the dependencies explicit.
My first run at E2E tests. I'm trying to digest someone else's protractor tests.
Problem: There are a lot of browser.driver.sleep and this seems fragile.
Goal: not to use browser.driver.sleep
Question: What is a better approach to browser.driver.sleep? Something less fragile like a promise or something I dont know about lol?
var config = require('../../protractor.conf.js').config;
describe('this Homepage Body Tests', function(){
browser.driver.get(config.homepageUrl);
it("should open find a clinic page", function(){
// page loads :: want to fix this random wait interval
browser.driver.sleep(2000);
browser.ignoreSynchronization = true;
var string = 'clinic';
var main = '.search-large-text';
var link = element(by.cssContainingText('.submenu li a', string));
link.click().then(function() {
// page reloads :: want to fix this random wait interval
browser.driver.sleep(3000);
var title = element(by.cssContainingText(main, string));
expect(title.getText()).toBe(string);
});
});
});
Since there is an ignoreSynchronization turned on, you cannot use waitForAngular(), which would be a solution in case of an angular-site testing.
A better solution here would be to set a page load timeout:
browser.manage().timeouts().pageLoadTimeout(10000); // 10 seconds
See also these relevant threads on explicit waits and timeouts:
Use protractor to test login on non-AngularJS page (Leo's answer is very detailed)
Protractor : How to wait for page complete after click a button?
Timeouts