Verifying a navigated to URL using Webdriverio and Chai - javascript

I'm using webdriverio v4 and chai to enter values into a login form, click the login button and verify the navigated to URL:
describe('login form', function () {
it('should allow access with correct credentials', function () {
LoginPage.open();
LoginPage.username.setValue('name');
LoginPage.companyCode.setValue('100');
LoginPage.password.setValue('password');
LoginPage.loginButton.click();
expect(browser.getUrl()).to.equal('URL path');
});
});
the values are entered successfully and the expected URL is navigated to. However, browser.getURL() is returning the base URL rather than the new URL?
What am I doing wrong?

The URL may be retrieved too quickly. You may have to wait until the page has loaded the new page before getting the URL.
Could use something like with waitUntil and check the URL within the callback. Like so:
browser.waitUtil(function() {
return browser.getUrl() === urlToCheck;
}, 5000);
See: http://webdriver.io/api/utility/waitUntil.html

Related

Angular - Service worker : How to get dynamic ID from route into the "Notificationclick" event?

I have put some push notifications in my code, in order to someone to be notified when an action is made. I made the back end with lib.net.webpush 3.1.0 with .net 4.5.2 in C#.
So far, the notification system is working very well, but there is something I can't succeed :
Within my service worker file (sw.js), using Angular 9, i want, when someone received the notification, that when a user click on it, he is redirect to the page from which it was sent.
first i made it like it (as i read in the doc):
self.addEventListener('notificationclick', function (event) {
const urlToOpen = new URL(event.notification.data, self.location.origin).href;
event.notification.close();
event.waitUntil(clients.openWindow(urlToOpen));
});
But i was always redirect to the "localhost://4200" and not to the complete url.
In order to test, i made this :
self.addEventListener('notificationclick', function (event) {
const urlToOpen = new URL(self.location.origin + "/#/rdc/rapports/2015");
event.notification.close();
event.waitUntil(clients.openWindow(urlToOpen));
});
and it worked. But i can't get the dynamic URL.
Does anyone knows how to get a dynamic URL within this file? Or how to be redirect to the page that sent the notification?
I also tried something like this here : notificationclick event service worker
But i really dont understand.
Thanks.
I just succeed in doing it.
So, for anyone in the same case as I was : within the service worker, you can't access the DOM, i wasn't able to get any ID or any path I was trying to aim in my code.
The solution was to, in my C# code, to add a "URL" property and parameter to my "SendNotification" funtion. Then, when i got a user, i can target his ID because it's already here.
Then, in my Angular code (within the service worker file), I just had to to this (i am storing my url in "data" here) :
self.addEventListener('notificationclick', function (event) { console.log("notif click event", event);
const urlToOpen = new URL(self.location.origin + event.notification.data);
event.notification.close();
event.waitUntil(clients.openWindow(urlToOpen)); });
An then, when i click on the notification, I am redirected to the desired URL.

Cypress login using request method

I register & login a user, however, when in my test I navigate to a page behind authentication, Cypress fails & takes me back to the login page. From the looks of it, the before function is successfully executed (as verified by the API log). Here is my code:
describe("Dashboard page", () => {
before(() => {
cy.fixture("authUserRegistrationDetail.json").then(userDetail => {
cy.fixture("authUserLoginDetail.json").then(userLoginDetail => {
cy.visit("http://localhost:3000/login");
cy.get(".cookieConsent button").click();
// create a random email for registration
userDetail.email = `${Math.random()
.toString(36)
.slice(-5)}#aaa.aaa`;
// share the email between userLogin & userRegistration obj
userLoginDetail.email = userDetail.email;
// register the user
cy.request("POST", "http://localhost:9000/users/", userDetail)
.its("body")
// login the same user
cy.request("POST", "http://localhost:9000/api-token-auth/", userLoginDetail).then($res => {
cy.request({
url: "http://localhost:9000/loggedinuser/",
headers: {
Authorization: `Token ${$res.body.token}`
}
});
});
});
});
});
// run the test
it("visits the dashboard...", () => {
cy.visit("http://localhost:3000/dashboard/");
cy.get("h2").contains("Your deals");
});
});
Once the code is run, the test fails on assertion and the user is not logged in. Here is the screenshot of the test result. I get a status code 200 when user signs up & then logs in. Why is the user login not persisting in the tests & the dashboard link fails.
EDIT:
I just realised that I am programmatically logging in, however, once logged in, how do I get Cypress browser to recognise the change in state & that the user is logged in. I.e, how do I refresh the Cypress screen to recognise the the user login?
From the above code, it doesn't look like you are preserving the cookie once logged in. Cypress automatically clears all cookies before each test to prevent state from building up. You should be able to do something similar to this:
before(() => {..cy.login() })
beforeEach(() => {
Cypress.Cookies.preserveOnce('session_id', 'remember_token')
})
This cypress doco should provide more context https://docs.cypress.io/api/cypress-api/cookies.html#Preserve-Once

javascript "callbacks" across redirecting / after reloading

I've got a site (asp.net mvc razor) on wich some functionalities require authorization / login.
These functionalities can be started by clicking on a button for example.
By clicking on such a button, the system checks whether the user is logged in or not.
If not, the user is redirected to the login page where he can sign in.
After that he will be redirected to the initial page again without initiating the users action.
So heres the workflow:
->Page x -> button y -> click -> redirect to login -> login -> redirect to x.
The redirects are simple Url.Action() statements.
What I want to do is to dynamically redirect to the page the click came from and ideally jump to the senders selector in order to simplify things for users.
What possibilities do I have to achieve this?
Only things coming to my mind are quite ugly stuff using ViewBag and strings
Update:
Info: As storing session variables causes problemes concerning concurrent requests this feature is disabled solution wide so I cannot use session variables.
Besides: One of the main problems is, that I cannot sign in without making an ajax call or sending a form. And by sending a form or making an ajax call I loose the information about the original initiator of the action and the parameters.
I solved this by adding by adding this to all such actions in their controllers:
[HttpPost]
public ActionResult ActionA(Guid articleId, Guid selectedTrainerId)
{
//if user is not authenticated then provide the possibility to do so
if (!Request.IsAuthenticated)
{
var localPath = this.ControllerContext.RequestContext.HttpContext.Request.Url?.LocalPath;
var parameter = this.ControllerContext.RequestContext.HttpContext.Request.Params["offeringRateId"];
var returnUrl = localPath + "?articleId=" + parameter;
return PartialView("LoginForOfferingPreview", new LoginForOfferingPreviewViewModel
{
RequestUrl = returnUrl,
//this will be used in the view the request was initiated by in order to repeat the intial action (after login has been successfull)
Requester = OfferingPreviewRequester.CourseTrialAdd,
//this will be used in the view to initiate the request again
RequestParameters = new List<dynamic> { new { articleId = articleId },new { selectedTrainerId = selectedTrainerId }}
});
}
//actual action
SendBasketEvent(new CourseAddMessage
{
BasketId = BasketId,
OfferingRateId = articleId,
SelectedTrainerId = selectedTrainerId,
SelectedTime = selectedTime,
Participants = selectedParticipants,
CurrentDateTime = SlDateTime.CurrentDateTimeUtc(SlConst.DefaultTimeZoneTzdb),
ConnectionId = connectionId
}, connectionId);
return Json(JsonResponseFactory.SuccessResponse());
}
the hereby returned view for login contains following js code that is called if the login has been succesfull:
function onLoginFormSubmit(data) {
//serialize form containing username+pw
var datastring = $("#loginForm").serialize();
$.ajax({
type: "POST",
url: '#Url.Action("Login_Ajax","Account",new {area=""})',
data: datastring,
success: function (data) {
debugger;
// display model errors if sign in failed
if (!!!data.Success) {
$(".buttons-wrap").append('<span id="loginFormError" style="color:red;"></span>');
$("#loginFormError").append(data.ErrorMessage);
}
//call method of initiating view that will decide what to dow now
if (data.Success) {
var parametersObjectAsString = JSON.parse('#Html.Raw(JsonConvert.SerializeObject(Model.RequestParameters))');
window.onLoginForOfferingPreviewSuccess('#Model.RequestUrl', parametersObjectAsString, '#((int)Model.Requester)');;
}
},
error: function () {
}
});
}
this works fine as long sigining does not fail due to wrong username or pw.
If that happens, the view shows the errors but by now signing in again somethign really strange happens:
At first it seems to work exaclty like signing in successfully by the first time but then the ajax calls in window function onLoginForOfferingPreviewSuccess will always reach the error block without beeing able to tell you why.
Fiddler reveals weird http resonse codes like 227,556 or something
Thx

Calling the app config method inside ajax response - AngularJS

I am developing an app using angularjs and this is my first hands on using angular. Although, I have started understanding it and have developed some part of the app but I am stuck at one particular point.
I am trying to implement login functionality, so as the page loads, I am authenticating user and redirecting him to login page. On successful login, I am storing some values of user in one of the config provider.
Now I am using an API which has their own method of authentication and they have expose the ajax method which I can use to authenticate a user.
I have provided a snippet below. What I am primarily doing is using the external API, authenticating the user and once authenticated, I am getting roles associated to that user using another ajax method of the API, called "GetUserDetails".
And inside the response of the "GetUserDetails", I am injecting a provider and setting some values, so I can use this across my app.
The problem here is the app.config method is never called/executded. I mean the ajax request is returning response, and the alert is displayed on my page, but app.config is never executed.
But the same app.config if I call inside the done() of GetUser method, the app.config gets executed and stores values in my provider. But I want the GetuserDetails values also to be stored before I do anything in my app as I want to execute certain functionality based on user.
Below is my function in main.js file
function(angular,angularRoute,app,routes,configService){
var $html = angular.element(document.getElementsByTagName('html')[0]);
angular.element().ready(function() {
$.c.authentication.getUser()
.done(function(response){
if(response.userName!="anonymous"){
$.c.ajax({
method: "GetUserDetails",
parameters: {
User: response.user
}
})
.done(function(res) {
alert("I have reached the destination").
app.config(['configServiceProvider', function(configServiceProvider){
configServiceProvider.setLoginStatus(true);
configServiceProvider.setUserName(response.userName);
configServiceProvider.setUserObject(response);
configServiceProvider.setUserRoleDetails(res);
}]);
})
.fail(function(res) {
alert("Error while getting user roles ."+res);
});
angular.resumeBootstrap([app['name']]);
}
else
{
app.config(['configServiceProvider', function(configServiceProvider){
configServiceProvider.setLoginStatus(false);
configServiceProvider.setUserName(response.userName);
}]);
//Show Login Screen
var url = window.location.href.split("#")[0];
window.location.href = url + "#/Login";
angular.resumeBootstrap([app['name']]);
}
})
.fail(function(response){
$rootScope.isLoggedIn=false;
});
});
Here is my configServiceProvider
define(['../app'],function(app){
return app.provider('configService', function(){
var options={};
this.setLoginStatus = function(status){
//$rootScope.isLoggedIn = status;
options.isLoggedIn=status;
};
this.setPreLoginInfo=function(info){
options.preLoginInfo=info;
};
this.setUserName=function(name){
options.username=name;
}
this.setUserObject = function(userObject) {
options.userObject = userObject;
}
this.setUserRoleDetails = function(userRoleDetails) {
options.userRoleDetails = userRoleDetails;
}
this.$get=[function(){
if(!options){
}
return options;
}];
});
})
Can anyone please explain me what's going wrong here or what I am missing ?
Also, is there any alternative to achieve the same functionality ?
No luck in figuring out why the above scenario was not working. Since I had already spent lot of time behind this, I have found a workaround to achieve the same with the use of services.

Get URL after sending form with webdriverio

I need to automate a sign in process test. I'm using webdriver.io in order to do that.
Here is my problem.
I have this bizarre sign in process:
I have to fulfil a form in a regular webpage
After sending the form, I'll be redirected to an URL that does not exist, but it contains an access code in one of its query params
Then I take this access code and send it to another location to obtain an access token.
What I need is a way of programmatically complete step 2, since 1 and 3 I have already figured out.
I'm trying like this:
var webdriverio = require('webdriverio');
var options = {
desiredCapabilities: {
browserName: 'chrome'
}
};
webdriverio
.remote(options)
.init()
.url(myUrl)
.title(function(err, res) {
console.log('Title was: ' + res.value);
})
.setValue('#usuario', user)
.setValue('#password', password)
.submitForm('form', function(){
this.url(function(err, res){
console.log(res.value);
});
this.pause(5000, function(){
this.end();
});
});
However, it seems like the callback for submitForm is called before the page gets loaded. This way, my log shows the very same URL I passed before.
If I try like this:
.submitForm('form', function(){
this.pause(5000, function(){
this.url(function(err, res){
console.log(res.value);
this.end();
});
});
});
I get this strange output:
data:text/html,chromewebdata
I know that a pause is not flawless in this context, but I could not find an asynchronous way of doing this.
Can anyone help?
You are probably not waiting for specific state after your form is submitted. What is the expected result after the form is submitted?
For example, you are expecting browser to redirect to a page with a header
<h1 class="success">Your form is submitted successfully</h1>
Then you should do something like this:
webdriverio
.remote(options)
.init()
.url(myUrl)
.waitForExist('form')
.setValue('#usuario', user)
.setValue('#password', password)
.submitForm('form')
.waitForExist('h1.success', 5000)
.getText('h1', function (err, res) {
expect(res).to.contain('submitted successfully'); //chai.js
})
.call(done);

Categories