I am trying to create a Tampermonkey script to automatically fill a login form with my username and password and then I would like to click the login button. However, with the code below, it appears to wait the three seconds, and THEN it fills the text into the boxes. How can I "flush" the changes so that they appear before the wait time occurs?
Thanks!
(function() {
'use strict';
function wait(ms){
var start = new Date().getTime();
var end = start;
while(end < start + ms) {
end = new Date().getTime();
}
}
window.onbeforeunload = function (){
return "Leaving page...";
}
$(document).ready(function() {
document.getElementById("username").value = "userValue";
document.getElementById("password").value = "passwordValue";
wait(3000);
document.getElementsByClassName("btn-large").click();
});
})();
That wait function is locking up the browser tab (and probably the browser, and potentially the whole computer). Don't code like that!
Also:
(function() {... is completely superfluous in a Tampermonkey script.
$(document).ready(function()... is not helpful except in rare cases that do not apply here.
Lots, lots more, etc...
Here's that code refactored:
window.onbeforeunload = function () {
return "Leaving page...";
}
//-- Hacker easter egg! See linked questions.
document.getElementById ("username").value = "userValue";
document.getElementById ("password").value = "passwordValue";
setTimeout (clickBtnAfterDelay, 3000);
function () {
//-- Brittle! See linked questions.
document.getElementsByClassName ("btn-large")[0].click ();
}
Related
I'm refactoring my PageObject for my tests. Currently I'm checking the labels present in 2 different buttons in a modal.
/* home-spec.js */
it('Some test', function(){
expect(homePage.getButton1Label()).toEqual(expectations.btn1);
expect(homePage.getButton2Label(true)).toEqual(expectations.btn2);
});
Although this currently works I need to pass a variable indicating if the modal is open or not. That's the bit I'm trying to fix.
/* home-page.js */
var HomePage = function () {
function getModalContent(modalName, isModalOpen){
/* isModalOpen = element(by.css('.modal-content')).isPresent(); */
if(!isModalOpen){
var manageProductsView = getUiView('SOME_VIEW');
var btn = getButton(manageProductsView);
btn.click();
browser.waitForAngular();
}
return element(by.css('.modal-content'));
}
function getButtonLabel(buttonBinding, isModalOpen){
/* isModalOpen = element(by.css('.modal-content')).isPresent(); */
var modalcontent = getModalContent('MODAL_NAME', isModalOpen);
var modalFooter = modalcontent.element(by.css('.modal-footer'));
var btn = modalFooter.element(by.binding(buttonBinding));
return btn.getText();
}
return {
getButton1Label: function(isModalOpen){
return getButtonLabel('btn1', isModalOpen);
},
getButton2Label: function(isModalOpen){
return getButtonLabel('btn2', isModalOpen);
}
}
}
What I would like to do is remove that isModalOpen dependency but I don't seem to find the correct way to do it. The comments indicate what I've tried and seemed to be the way to go. Also tried to wrap it in the then block.
EDIT
Based on Vlad answer I edited my getButtonLabel function so it checks if the modal is open
function getButtonLabel(buttonBinding){
return element(by.css('.modal-content')).isPresent().then(function(isModalOpen){
var modalcontent = getModalContent('MODAL_NAME', isModalOpen);
var modalFooter = modalcontent.element(by.css('.modal-footer'));
var btn = modalFooter.element(by.binding(buttonBinding));
return btn.getText();
});
}
Was trying to avoid handling promises manually but I guess in some cases it's unavoidable
Your commented part is the way to go:
function getModalContent(modalName){
var modalContent = element(by.css('.modal-content'));
var isModalOpen = modalContent.isPresent();
return isModalOpen.then(function(open) {
if(!open){
var manageProductsView = getUiView('SOME_VIEW');
var btn = getButton(manageProductsView);
return btn.click()
.then(function(){
return modalContent;
});
}
return modalContent;
});
}
function getButtonLabel(buttonBinding){
var modalcontent = getModalContent('MODAL_NAME');
var btnText = modalcontent
.then(function(content) {
return content
.element(by.css('.modal-footer'))
.element(by.binding(buttonBinding))
.getText();
});
return btnText;
}
Beware that the modal remains open after the test, you might want to add something to close it if it's open to maintain consistent state throughout tests.
It's much better to know for sure if it's open before doing operations on it, so that you can open it yourself if it isn't and you need it to be, instead of doing conditionals like these - they are expensive because of promise chaining.
My general feeling is that the logic looks a bit too complicated, you might want to refactor some of the stuff in the future :p
When I am running the below code, I observed that the last 3 lines with 'var robot' are executed at the beginning itself and not after clicking on the textArea. How can I control the sequence of code execution?
var robot = require("robotjs");
it('Testing robotjs', function () {
browser.driver.get(www.abc.com");
element(by.xpath(button1)).click();
element(by.xpath(textArea)).click();
robot.typeString("hello world");
robot.moveMouse("605", "429");
robot.mouseClick("left");
});
This is the Protractor's Control Flow in action, put the last 3 lines into the last click's promise resolution function:
var robot = require("robotjs");
it('Testing robotjs', function () {
browser.driver.get("www.abc.com");
element(by.xpath(button1)).click();
element(by.xpath(textArea)).click().then(function () {
robot.typeString("hello world");
robot.moveMouse("605", "429");
robot.mouseClick("left");
});
});
I've a tricky question for you guys out there. I've made a simple exercise webapp using AngularJS and ngRoute.
Inside my index.html I got an ng-view element which provide two pages search_page.html and detail_of_result_page.html. The code works pretty fine, I put something in the first page input field, hit search button and all results magically appears in my page. The troubles comes with protractor that seems to not see my result in results repeater with his:
element.all(by.repeater("result in results"))
I've tried to put in browser.pause() and watch for errors, but everything seems right.
I've forgot the error code from Protractor:
Failed: Index out of bound.
Trying to access element at index: 0, but there are only 0 elements that match locator by.repeater("result in results")
OK, under your searchTest.js line: 27
beforeEach(function () {
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf($('li')),5000);
var resultItem = element.all(by.repeater('result in results')).first(); //BUG
var resultLink = resultItem.element(by.css('a'));
resultLink.click();
resultId = resultLink.getAttribute('href').then(function (attr) {
//var res = attr.match('/\/TDDBook\/Search\/#\/detail\/(\d+)/')[1]; // TODO: fix this shit
return 1;
});
});
Things seem alright until you do resultLink.click(); Assuming that it work fine for the first it ("should set the url to the selected detail view"). But when it come to second it("should see the details in the main page component") at this moment you are no longer on /#/splash route. Therefore your pereater no longer available to be located when your beforeEach() run again.
Solution
Your beforeEach doesn't seem useful and logically not run-able for your second it("should see the details in the main page component"). So just move all the thing like this:
it ("should set the url to the selected detail view",function () {
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf($('li')),5000);
var resultItem = element.all(by.repeater('result in results')).first(); //BUG
var resultLink = resultItem.element(by.css('a'));
resultLink.click();
resultId = resultLink.getAttribute('href').then(function (attr) {
//var res = attr.match('/\/TDDBook\/Search\/#\/detail\/(\d+)/')[1]; // TODO: fix this shit
return 1;
});
resultId.then(function (id) {
var expectedUrl = '/detail/'+id;
browser.getLocationAbsUrl().then(function (url) {
expect(url).toBe(expectedUrl);
})
})
})
P.S. just for your information, there is Page Object in protractor, which will be fit with the thing you attempt to do with your beforeEach() . Plus with a tiny bit of knowledge of commonJS (module.exports & require()) it will perfectly suit your needs ;) Cheer!
My application needs a function to be called at initialize:after stage. After user has provided that input in function, i want application to go in start stage. But currently, it just go to all the 3 stages without waiting for input. Can anyone suggest how can i do acheive my desired functionality?
var app= new Marionette.Application();
app.on('initialize:before', function(options){
//do Something
});
app.on('initialize:after', function(options){
//do Something
//wait for User to give input before continuing to start stage.
});
app.on('start', function(options){
//start only when User provide input.
//do Something with input.
});
var options = {};
app.start(options);
This is less a Backbone/Marionette question and more of a pure JavaScript problem. If you don't care about usability, just use window.prompt and save yourself a lot of work.
If you're not able to use alerts, then things get more complicated. User inputs are usually done using some sort of form. Unfortunately, these are asynchronous operations, meaning you can't just halt JavaScript execution while you wait for the user to get out their glasses and look for their SSN. jQuery's Deferred library is a good tool to use to handle situations like these, but you'll need to rework your code a little.
In our project we've created a the concept of a Lifecycle that works roughly as follows:
Module.lifecycle = {
step1: function() {
var dfd = new $.Deferred().resolve();
// get user input from a custom dialog control
var dialog = new MyDailogControl("What do you want to do?");
dialog.onSuccess = dfd.resolve;
dialog.onFail = dfd.reject;
dialog.show();
return dfd.promise();
},
step2: function() { /* load some data from the server */ },
step3: function() { /* get even more user input... */ },
};
Next we have a Lifecycle Walker object that pushes the lifecycle through each state; it looks something like this (from memory; you'll want to test this...)
var Walker = function(lifecycle) {
this.lifecycle = lifecycle;
this._states = _.keys(this.lifecycle);
}
_.extend(Walker.prototype, Backbone.Events, {
start: function() {
this._index = 0;
this.moveNext();
},
moveNext: function() {
var step = this.states[this._index];
this.trigger('before:' + step);
$.when(this.lifecycle[step]())
.then(function() {
this.trigger('after:' + step);
this._index++;
if (this._active < this._states.length) {
this.moveNext();
}
});
}
});
Tying the two ideas together, you'll do something like:
var walker = new Walker(MyModule.lifecycle);
walker.on('before:step1', function() { /* do something amazing */ });
walker.on('before:step2', function() { /* do something fantastic */ });
walker.on('after:step3', function() { /* do whatever */ });
walker.start();
So, we've basically implemented a bastardized Command Pattern using deferreds to solve problems like this. We hook into module.start, but there's no reason you couldn't do something similar on app.start instead.
I am a little desperate here. I have been reading everything I was able to find on Drupal.behaviours but obviously its still not enough. I try running a masonry grid with the infinitescroll plugin to attach the new images to the masonry. This works fine so far. The next thing I wanted to implement to my website is a hover effect (which shows information on the images) and later fancybox to show the images in a huger size.
(function ($) {
Drupal.behaviors.views_fluidgrid = {
attach: function (context) {
$('.views-fluidgrid-wrapper:not(.views-fluidgrid-processed)', context).addClass('views-fluidgrid-processed').each(function () {
// hide items while loading
var $this = $(this).css({opacity: 0}),
id = $(this).attr('id'),
settings = Drupal.settings.viewsFluidGrid[id];
$this.imagesLoaded(function() {
// show items after .imagesLoaded()
$this.animate({opacity: 1});
$this.masonry({
//the masonry settings
});
});
//implement the function of jquery.infinitescroll.min.js
$this.infinitescroll({
//the infinitescroll settings
},
//show new items and attach behaviours in callback
function(newElems) {
var newItems = $(newElems).css({opacity: 0});
$(newItems).imagesLoaded(function() {
$(newItems).animate({opacity: 1});
$this.masonry('appended', newItems);
Drupal.attachBehaviours(newItems);
});
});
});
}
};
})(jQuery);
Now I read that I need to Reattach the Drupal.behaviours if I want the hover event to also take place on the newly added content.
(function ($) {
Drupal.behaviors.imgOverlay = {
attach: function (context) {
var timeout;
$('.img_gallery').hover(function() {
$this = $(this);
timeout = setTimeout(change_opacity, 500);
}, reset_opacity);
function change_opacity() {
//set opacity to show the desired elements
}
function reset_opacity() {
clearTimeout(timeout);
//reset opacity to 0 on desired elements
}
}
};
})(jQuery)
Where do I now write the Drupal.attachBehaviours() to make it work actually? Or is there some other error I just dont see atm? I hope I wrote the question so that its understandable and maybe it also helps somebody else, since I experienced that there is no real "official" running Version of this combination in drupal 7.
Ok, the solution is actually pretty simple. When writing it correctly than it also runs. its of course not Drupal.attachBehaviours() but Drupal.attachBehaviors() . So this combination now works and I am finally relieved :).