Running the multiple scenarios in a single feature files fails in cypress - cucumber

For Cypress cucumber project, I have 1 feature file login.feature which has 2 scenario outlines
Valid Login
Invalid Login
When I am running the login.feature file. The Valid login scenario has a checkpoint to verify I am on the homepage. Which it takes times to verify and within couple of seconds, it moves to the next scenarios causing the first scenario to failed.
If i run both of them one by one, Nothing is failing. Cypress uses their own feature to wait for the particular element to check and then move to next one. But here, it is waiting for sometime and gradually moving to the next scenario.
login.feature
Feature: Login to Application
As a valid user I want to log in to the Application
#login
Scenario Outline: Valid Login
Given user open the login Page
When user enter a username "<userName>"
And user enter a password "<password>"
And user click the sign-in button
Then user should be able to login
Examples:
| userName | password |
| abc | ########### |
#login
Scenario Outline: Invalid Login
Given user open the login Page
When user enter a username "<userName>"
And user enter a password "<password>"
And user click the sign-in button
Then error should displayed as "<error_message>"
Examples:
| userName | password | error_message |
| admin | sd444-fdf-ffr | Unable to sign in |
login_steps.js
import { Given, When, And, Then } from "cypress-cucumber-preprocessor/steps";
import loginPage from "../pageObjects/pages/login_page";
before(() => {
cy.log("I will only run before the first scenario of login.feature");
});
beforeEach(() => {
cy.log("I will run before each Scenario of login.feature");
});
Given("user will run before all the scenarios", () => {
cy.log("Scenario - Started");
});
Given("user open the login Page", () => {
loginPage.visitLoginPage();
});
When("user enter a username {string}", (username) => {
loginPage.fillUsername(username);
});
And("user enter a password {string}", (password) => {
loginPage.fillPassword(password);
});
And("user click the sign-in button", () => {
loginPage.submitLoginDetails();
});
Then("user should be able to login", () => {
loginPage.checkLoginSuccess();
});
Then("error should displayed as {string}", (error_message) => {
loginPage.checkErrorMessage(error_message);
});
login_page.js
class loginPage {
static visitLoginPage() {
cy.visit('/');
cy.url().should('include', 'login');
}
static fillUsername(username) {
cy.get('#abc').type(username);
}
static fillPassword(password) {
cy.get('#def').type(password);
}
static submitLoginDetails() {
cy.get('[type="submit"]').click();
}
static checkLoginSuccess() {
cy.get('#large-device').should('be.visible');
}
static checkErrorMessage(error_message) {
cy.get('#form1').should('contain.text', error_message);
}
static loginWithValidCredentials(username, password) {
cy.visit('/');
cy.url().should('include', 'login')
cy.get('#abc').type(username);
cy.get('#def').type(password);
cy.get('[type="submit"]').click();
}
}
export default loginPage
Let me know for any more information. I am new to cypress cucumber. Please help me out here.

Related

How can I get CucumberJS to recognizing my Scenario Outline parameters in a step definition?

Using CucumberJS I'm trying to implement a Scenario Outline for UI testing. Cucumber isn't recognizing or passing my arguments correctly. Here's what I have.
test.feature
Scenario Outline: User with permissions can Import Payment files with any file format
Given a <username> has logged into site
Examples:
|username |fileName |
|administrator |test1.csv |
|userA |step2.csv |
test_step.js
Given('a {string} has logged into site', async function (username) {
console.log('username = ' + username);
return this.userLogin(username);
});
world.js
'use strict';
const { setWorldConstructor } = require('cucumber');
class testApp {
// Write metrics data object to JSON file
async userLogin(username) {
await this.navigateLoginPage();
}
}
setWorldConstructor(testApp);
Now when I run this, I get the following:
Warnings:
1) Scenario: User with permissions can Import Payment files with any file format # features/importPaymentFile.feature:28
? Given a administrator has logged into site
Undefined. Implement with the following snippet:
Given('a administrator has logged into site', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
2) Scenario: User with permissions can Import Payment files with any file format # features/importPaymentFile.feature:29
? Given a administrator has logged into site
Undefined. Implement with the following snippet:
Given('a userA has logged into site', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
So now I'm confused. It looks like my parameters are getting correctly read but not recognizing them in the step definitions.
Can anyone please give me some insight on how I should be implementing the Scenario Outline's parameters?
UPATE #3 - FINAL UPDATE
So it worked for me like this:
test.feature
Scenario Outline: User with permissions can Import Payment files with any file format
Given a "<username>" has logged into site and uploads "<fileName>"
Examples:
|username |fileName |
|administrator |test1.csv |
|userA |step2.csv |
test_step.js
Given('a {string} has logged into site and uploads {string}', async function (username, fileName) {
console.log('username = ' + username);
console.log('fileName = ' + fileName);
return this.userLogin(username);
});
world.js
'use strict';
const { setWorldConstructor } = require('cucumber');
class testApp {
// Write metrics data object to JSON file
async userLogin(username) {
await this.navigateLoginPage();
}
}
setWorldConstructor(testApp);
Results:
> . ./.env; node app.js "--feature" "importPaymentFile"
username = administrator
filename = oneStepApproval_MediaOcean.csv
.username = operations
filename = twoStepApproval_MediaOceanDan.csv
Sorry if I was to verbose. I'll pair this down if told to do so :)
UPDATE #1
I tried quotes, it this didn't work. Putting quotes around the parameter in the feature file seemed to cause the parameter to not be passed.
test.feature
Scenario Outline: User with permissions can Import Payment files with any file format
Given a "<username>" has logged into site
Examples:
|username |fileName |
|administrator |test1.csv |
|userA |step2.csv |
resulting error:
username =
.username =
.
2 scenarios (2 passed)
2 steps (2 passed)
0m00.015s
(node:16642) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 7): Error: Protocol error(Emulation.setDeviceMetricsOverride): Session closed. Most likely the page has been closed.
(node:16642) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:16642) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 8): Error: Protocol error (Emulation.setDeviceMetricsOverride): Session closed. Most likely the page has been closed.
events.js:183
throw er; // Unhandled 'error' event
^
Error: Timed out while authenticating with server
at Timeout._onTimeout (/Users/huckcarignan/Desktop/sprint26/epay-test-automation/node_modules/imap/lib/Connection.js:139:17)
at ontimeout (timers.js:475:11)
at tryOnTimeout (timers.js:310:5)
at Timer.listOnTimeout (timers.js:270:5)
Update #2
Combo 1: & {string}
Feature File:
Given a <username> has logged into Site
Step Definition:
Given('a {string} has logged into Site', async function (username) {
console.log('username = ' + username);
return this.userLogin(username);
});
Results:
? Given a administrator has logged into Site
Undefined. Implement with the following snippet:
Given('a administrator has logged into Site', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
Combo 2: & ([^"]*)
Feature File:
Given a <username> has logged into Site
Step Definition:
Given('a ([^"]*) has logged into Site', async function (username) {
console.log('username = ' + username);
return this.userLogin(username);
});
Results:
? Given a administrator has logged into Site
Undefined. Implement with the following snippet:
Given('a administrator has logged into Site', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
Combo 3: "" & "([^"]*)"
Feature File:
Given a "<username>" has logged into Site
Step Definition:
Given('a "([^"]*)" has logged into Site', async function (username) {
console.log('username = ' + username);
return this.userLogin(username);
});
Results:
? Given a {string} has logged into Site
Undefined. Implement with the following snippet:
Given('a administrator has logged into Site', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
Combo 4: "" & ([^"]*)
Feature File:
Given a <username> has logged into Site
Step Definition:
Given('a "([^"]*)" has logged into Site', async function (username) {
console.log('username = ' + username);
return this.userLogin(username);
});
Results:
? Given a {string} has logged into Site
Undefined. Implement with the following snippet:
Given('a administrator has logged into Site', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
Combo 5: "" & {string} THE WINNER - sort of
Feature File:
Given a <username> has logged into Site
Step Definition:
Given('a "([^"]*)" has logged into Site', async function (string) {
console.log('username = ' + string);
return this.userLogin(string);
});
Results:
username = administrator
.
1 scenarios (1 passed)
1 steps (1 passed)
0m01.637s;
Sooooo...this works, multiple parameters are handled by order - I'll put my results at the very top
I used regular expressions
So for your example:
Scenario Outline: User with permissions can Import Payment files with any file format
Given a <username> has logged into site
Examples:
|username |fileName |
|administrator |test1.csv |
|userA |step2.csv |
Then in the code I would do:
Given(/^a (.*) has logged into site$/, async function (username) {
console.log('username = ' + username);
return this.userLogin(username);
});
Scenario Outline: User with permissions can Import Payment files with any file format
Given a "<username>" has logged into site
Examples:
|username |fileName |
|administrator |test1.csv |
|userA |step2.csv |
Add quotes to the example,"" it could be that cucumber is expecting a string parameter to be passed in which is not the case in your test

Microsoft Graph API Permissions for non-admins?

I am trying to create a dropdown with all the users in my Office365 tenant. I created an app in Azure AD and gave it all the necessary permissions. I gave it all the permissions for Microsoft Graph actually, app and delegated. All of them.
Then I wrote up my script to query all users with https://graph.microsoft.com/v1.0/users.
I had my tenant admin go in and accept the permissions then output the list of users in the UI. Works fine for the admin
I'm not an admin but when I go to the page I get the following error:
This application requires application permissions to another
application. Consent for application permissions can only be performed
by an administrator. Sign out and sign in as an administrator or
contact one of your organization's administrators.
I need to know if this will work for users with even lower permissions. From what I understand the API request and the App is running under the permissions given to the application in Azure. So even if the user as Read Only, the request isn't running under the user, it's running under the Application I set up. So why would I get the error regarding permissions?
This is the code I'm using:
(function () {
"use strict";
// Some samples will use the tenant name here like "tenant.onmicrosoft.com"
// I prefer to user the subscription Id
var subscriptionId = "metenant.onmicrosoft.com";
// Copy the client ID of your AAD app here once you have registered one, configured the required permissions, and
// allowed implicit flow https://msdn.microsoft.com/en-us/office/office365/howto/get-started-with-office-365-unified-api
var clientId = "cccb1f2f-xxx-x-xxxxx-x-x-x-x-x-";
window.config = {
// subscriptionId: subscriptionId,
clientId: clientId,
postLogoutRedirectUri: window.location.origin,
endpoints: {
graphApiUri: 'https://graph.microsoft.com'
},
cacheLocation: 'localStorage' // enable this for IE, as sessionStorage does not work for localhost.
};
var authContext = new AuthenticationContext(config);
// Check For & Handle Redirect From AAD After Login
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
if (isCallback && !authContext.getLoginError()) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
// If not logged in force login
var user = authContext.getCachedUser();
// NOTE: you may want to render the page for anonymous users and render
// a login button which runs the login function upon click.
if (!user) authContext.login();
// Acquire token for Files resource.
authContext.acquireToken(config.endpoints.graphApiUri, function (error, token) {
// Handle ADAL Errors.
if (error || !token) {
console.log('ADAL error occurred: ' + error);
return;
}
// Execute GET request to Files API.
var filesUri = config.endpoints.graphApiUri + "/v1.0/users";
$.ajax({
type: "GET",
url: filesUri,
headers: {
'Authorization': 'Bearer ' + token,
}
}).done(function (response) {
console.log('Successfully fetched from Graph.');
console.log(response);
var container = $(".container")
container.empty();
$.each(response.value, function(index, item) {
container.append($('<li>').text(item.displayName + " " + item.mail + " " + item.mobilePhone))
})
}).fail(function (response) {
var err = JSON.parse(response.responseText)
console.log('Failed:', err.error.message);
});
});
})();
There are two kinds of permission/scope for Microsoft Graph. One is that require administrator’s consent. The other is not required.
What’s the permission you were config for this app? To list the users without administrator’s consent, we can use the scope User.ReadBasic.All like figure below:
You can get more detail about the permission/scope from here.
Modify:
At present, the adal.js doesn’t provide the admin consent. If you want to use this feature, you can modify the code to add a prameter like below:
AuthenticationContext.prototype.login = function (prompt) {
// Token is not present and user needs to login
var expectedState = this._guid();
this.config.state = expectedState;
this._idTokenNonce = this._guid();
this._logstatus('Expected state: ' + expectedState + ' startPage:' + window.location);
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST, window.location);
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.STATE_LOGIN, expectedState);
this._saveItem(this.CONSTANTS.STORAGE.NONCE_IDTOKEN, this._idTokenNonce);
this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, '');
var urlNavigate = this._getNavigateUrl('id_token', null) + '&nonce=' + encodeURIComponent(this._idTokenNonce);
if (prompt && prompt === "admin_consent") {
urlNavigate = urlNavigate + "&prompt=admin_consent"
}
this.frameCallInProgress = false;
this._loginInProgress = true;
if (this.config.displayCall) {
// User defined way of handling the navigation
this.config.displayCall(urlNavigate);
} else {
this.promptUser(urlNavigate);
}
// callback from redirected page will receive fragment. It needs to call oauth2Callback
};
And if you were using Angular, we also need to modify the adal-angular.js:
this.$get = ['$rootScope', '$window', '$q', '$location', '$timeout', function ($rootScope, $window, $q, $location, $timeout) {
...
return {
// public methods will be here that are accessible from Controller
config: _adal.config,
login: function (prompt) {
_adal.login(prompt);
},
...
}
Then we can provide two button for users login in. One button is for the users sign-in with themselves. And the other is for admin to give the consent for the organization. Here is the code redirect to the login page for the admin consent in the control of Angular:
$scope.login = function () {
adalService.login("admin_consent");
};

Callback redirect not preserving session

I have a scenario that works just fine when I am using real omniauth, but fails when I run it with the mock auth in cucumber/capybara.
In the callback, when I do sign_in #user, it successfully creates the user and logs in... current_user is set. But when I then do redirect_to request.env['omniauth.origin'] || '/', inside the action that follows, current_user is now nil.
I've confirmed via screenshots/pausing the browser that it's not working with the mock auth. The same error occurs in firefox and chrome drivers.
Any idea as to why this would be happening?
/features/support/env.rb:
Cucumber::Rails::Database.javascript_strategy = :truncation
Scenario:
#javascript
Scenario:
Given I am on the home page
When I press "Login"
And I should see "Login with Twitter" in the selector "#login-modal"
Given Omniauth returns a user with provider "twitter" and uid "1" and nickname "foo"
When I login with Twitter
Then I should be logged in as "foo"
Step Definitions:
Given(/^Omniauth returns a user with provider "(.*?)" and uid "(.*?)" and nickname "(.*?)"$/) do |provider, uid, nickname|
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(provider.to_sym, {
:uid => uid,
:info => {
:name => nickname
}
})
end
Then(/^I should be logged in as "(.*?)"$/) do |nickname|
expect(page).to have_content(nickname)
end
Auth callback:
def twitter
#user = User.from_omniauth(request.env["omniauth.auth"]) # this works-- I get the mock
sign_in #user
puts ">> in auth callback: just signed in user #{current_user.id}"
redirect_to request.env['omniauth.origin'] || '/'
end
Controller:
def new
puts ">> in my_controller#new: current_user = #{current_user.id if current_user}"
end
Cucumber Output:
Given Omniauth returns a user with provider "twitter" and uid "1" and nickname "foo"
>> in auth callback: just signed in user 1
>> in my_controller#new: current_user =
When I login with Twitter
Then I should be logged in as "foo"
expected to find text "foo" in [redacted] (RSpec::Expectations::ExpectationNotMetError)
You are getting the user and collecting it to new variable #user but while you are calling the sign_in method again you did initialize the new variable user with using(eg. #user)

Allowing both email and username for authentication

I'm creating two projects (MVC 5 and Web API) using ASP.Net Identity 2.1 and I couldn't find how to use both email and username for authentication (an area called Admin must use a username and the common area must use email addresses for authentication).
The problem is that there is only one method for authentication and it does not allow you to specify if you will compare with the email address or the username.
SignInHelper.PasswordSignIn
What should I do to achieve this?
SignInManager will not you help with it, you'll need to use UserManager and a bit more jiggery-pokery (that's technical term!):
This is what I have for this scenario:
var unauthUserByUsername = await userManager.FindByNameAsync(command.UserName);
var unauthUserByEmail = await userManager.FindByEmailAsync(command.UserName);
var unauthenticatedUser = unauthUserByUsername ?? unauthUserByEmail;
if (unauthenticatedUser == null)
{
logger.Warn("User {0} is trying to login but username is not correct", command.UserName);
return View(); // stop processing
}
var loggedInUser = await userManager.FindAsync(unauthenticatedUser.UserName, command.Password);
if (loggedInUser == null)
{
// username is correct, but password is not correct
logger.Warn("User {0} is trying to login with incorrect password", command.UserName);
await userManager.AccessFailedAsync(unauthenticatedUser.Id);
return View(); // stop processing
}
// Ok, from now on we have user who provided correct username and password.
// and because correct username/password was given, we reset count for incorrect logins.
await userManager.ResetAccessFailedCountAsync(loggedInUser.Id);
if (!loggedInUser.EmailConfirmed)
{
logger.Warn("User {0} is trying to login, entering correct login details, but email is not confirmed yet.", command.UserName);
return View("Please confirm your email"); // stop processing
}
if (await userManager.IsLockedOutAsync(loggedInUser.Id))
{
// when user is locked, but provide correct credentials, show them the lockout message
logger.Warn("User {0} is locked out and trying to login", command.UserName);
return View("Your account is locked");
}
logger.Info("User {0} is logged in", loggedInUser.UserName);
// actually sign-in.
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
await userManager.SignInAsync(authenticationManager, loggedInUser, false);
This checks if user has confirmed email, if user is locked out and does lock user out after a certain number of attempts (given all other settings for locking-out are enabled).
This way both are allowed
var userEmail = await UserManager.FindByEmailAsync(model.Login);
if (userEmail == null)
{
var user = await UserManager.FindByNameAsync(model.Login);
if (user == null)
{
model.Login = "";
}
}
else
{
model.Login = userEmail.UserName;
}
var result = await SignInManager.PasswordSignInAsync(model.Login, model.Password, model.RememberMe, shouldLockout: false);

Refresh window when username and password mismatch

I am building an application using node.js. I made a login form and it is working fine, but if the user enters the wrong username and password then he has to refresh the window and then type the right username and password in order to continue with the next screen.
What should I do to let the user enter after the login window without a refresh?
Explaining the above problem step-by-step:
User enters username and password (if both are correct).
User is logged in.
But:
User enters username and password (if either of them is wrong).
Refresh window.
User enters correct username and password.
User is logged in.
How can I avoid the "refresh window" step? I can explain more if this is not clear.
I have edited the answer, you can do it like this.
socket.on('login_check', function(email, pwd) {
connection.query("SELECT id,user_type FROM user WHERE email = '"+email+"' AND password = '"+pwd+"'ORDER BY name ",
function (error, results, fields) {
if (error) {
console.log(error);
}
if (results[0]) {
// some code
} else {
response.writeHead(200, {
'Location': '/login-path'
// add other headers here...
// you may also show the email in the text box
// again by passing the variable email to the template
// engine you are using or how ever you are doing it
});
response.end();
}
});
});

Resources