Elegant Protractor Logging on browser.wait() - xpath

For some time now, our team has been using the Protractor/Jasmine combo in order to do E2E testing and it has worked out great for us.
Recently, I've been assigned the task of improving the logging and have noticed 2 areas in our code where the logging could be improved.
One of these areas is with using browser.wait(). We're currently using the method in the form of browser.wait(condition, timeOut) and excluding the third parameter which is a message to be written to the console in case of a failure.
I decided to comb the code and insert a message into each of these methods and the result has been OK. Although the code works, I'm wondering if there is a more elegant way of doing this.
I'm currently saving the XPath of whatever element I'm waiting on and then if that fails, displaying a message in the form: getWait() timeout due to <element_name> { XPath = <element_XPath> } being not visible or enabled such that you can click it. The pattern changes if we're using isPresent(), elementToBeClickable(), visibilityOf(), etc.
Is there a way of getting the current context of the code I'm executing when browser.wait() is executed? Can I instead display the element or more information on what caused the timeOut?
Let me know if I can clarify further. Thanks

Even I came faced the same problem when using browser.wait where it won't show any detailed log on time out. So what I have done is, I created a wrapper class for waitHandling and added failure messages based on the wait type. Kindly have a look at below code.
var browserWaitHandler = function () {
var expectedConditions = protractor.ExpectedConditions;
var defaultWaitTime = 5000;
this.waitForElementPresent = function (_element,customWaitTime) {
return browserWait(expectedConditions.presenceOf(_element),customWaitTime,"Wait timeout after waiting for element to be Present with locator "+_element.locator().toString());
};
this.waitForElementVisible = function (_element,customWaitTime) {
return browserWait(expectedConditions.visibilityOf(_element),customWaitTime,"Wait timeout after waiting for element to be Visible with locator "+_element.locator().toString());
};
this.waitForElementClickable = function (_element,customWaitTime) {
return browserWait(expectedConditions.elementToBeClickable(_element),customWaitTime,"Wait timeout after waiting for element to be clickable with locator "+_element.locator().toString())
};
this.waitForElementContainsText = function (_element,expectedText,customWaitTime) {
return browserWait(expectedConditions.textToBePresentInElement(_element,expectedText),customWaitTime,"Wait timeout after waiting for element to Contain text as "+expectedText+" with locator "+_element.locator().toString())
};
var browserWait = function (waitCondition,customWaitTime,timeoutMessage) {
return browser.wait(waitCondition,customWaitTime | defaultWaitTime,timeoutMessage);
};
};
And also in protractor we have a method called locator() which can be used with ElementFinder and ElementArrayFinder objects to get the locator that is used to find the element.Refer below example code,
var loginButton = element(by.buttonText("Login"));
console.log("Locator used for login button is:"+loginButton.locator().toString());
*OUTPUT:*
Locator used for login button is:by.buttonText("Login")

Related

How to wait for next #Composable function in jetpack compose test?

Suppose I have 3 #Composable functions: Start, Loading, Result.
In the test, I call the Start function, click the Begin button on it, and the Loading function is called.
The Loading function displays the loading procedure, takes some time, and then calls the Result function.
The Result function renders a field with the text OK.
How to wait in the test for the Result or few seconds function to be drawn and check that the text is rendered OK?
composeTestRule
.onNodeWithText("Begin")
.performClick()
// Here you have to wait ...
composeTestRule
.onNodeWithText("OK")
.assertIsDisplayed()
You can use the waitUntil function, as suggested in the comments:
composeTestRule.waitUntil {
composeTestRule
.onAllNodesWithText("OK")
.fetchSemanticsNodes().size == 1
}
There's a request to improve this API but in the meantime you can get the helpers from this blog post and use it like so:
composeTestRule.waitUntilExists(hasText("OK"))
So the options are:
It is possible to write to the global variable which function was last called. The disadvantage is that you will need to register in each function.
Subscribe to the state of the screen through the viewmodel and track when it comes. The disadvantage is that you will need to pull the viewmodel into the test and know the code. The plus is that the test is quickly executed and does not get stuck, as is the case with a timer.
I made this choice. I wrote a function for starting an asynchronous timer, that is, the application is running, the test waits, and after the timer ends, it continues checking in the test. The disadvantage is that you set a timer with a margin of time to complete the operation and the test takes a long time to idle. The advantage is that you don't have to dig into the source code.
Implemented the function like this.
fun asyncTimer (delay: Long = 1000) {
AsyncTimer.start (delay)
composeTestRule.waitUntil (
condition = {AsyncTimer.expired},
timeoutMillis = delay + 1000
)
}
object AsyncTimer {
var expired = false
fun start(delay: Long = 1000){
expired = false
Timer().schedule(delay) {
expired = true
}
}
}
Then I created a base class for the test and starting to write a new test, I inherit and I have the necessary ready-made functionality for tests.
Based on Pitry's answer I created this extension function:
fun ComposeContentTestRule.waitUntilTimeout(
timeoutMillis: Long
) {
AsyncTimer.start(timeoutMillis)
this.waitUntil(
condition = { AsyncTimer.expired },
timeoutMillis = timeoutMillis + 1000
)
}
object AsyncTimer {
var expired = false
fun start(delay: Long = 1000) {
expired = false
Timer().schedule(delay) {
expired = true
}
}
}
Usage in compose test
composeTestRule.waitUntilTimeout(2000L)

reducer goes into a loop when returning a new array created from state

I am using react-redux 5.0.6 and have a reducer with the following code:
export default (state = [], action) => {
switch(action.type) {
case 'ADD_ENGAGEMENT':
let newArr = state.slice();
newArr.push(action.payload);
return newArr;
case 'UPDATE_ENGAGEMENT':
console.info('UPDATE_ENGAGEMENT')
return state.slice();
// return state;
default:
return state;
}}
The issue occurs within the 'UPDATE_ENGAGEMENT' case -- the actual logic has been removed and replaced with the simplest example to demonstrate the problem.
When a new array created from state via state.slice() is returned, a loop is triggered, causing the action to be dispatched until an 'Uncaught RangeError: Maximum call stack size exceeded' error is raised. Screenshot of the browser console during the issue's occurrence
The issue is not limited to 'slice()' and occurs whenever an array containing any element of state is returned e.g. return [state[0]].
When the original state is returned, the issue does not occur.
I am completely baffled by this behavior and cannot fathom how anything in my application could be causing it. Any insight would be immensely appreciated.
To provide some additional context, below is the code involved in the action's being dispatched:
componentWillReceiveProps(newProps) {
let engagementTemplateData = newProps.selectedEngagementTemplate;
let engagements = newProps.engagements;
if (engagementTemplateData && engagementTemplateData.engagementUuid === this.props.uuid) {
let template = engagementTemplateData.template;
this.updateEngagementTemplate(template);
}
}
updateEngagementTemplate(template) {
let url = `/engagements/${this.props.uuid}`;
let requestHelper = new AjaxRequestHelper(url);
let data = {template_uuid: template.uuid};
this.props.updateEngagement({uuid: this.props.uuid, template: template});
// requestHelper.put(data, response => {
// this.props.updateEngagement({uuid: this.props.uuid, template: template});
// });
}
Basically, the function which triggers the action is called in componentWillReceiveProps as a result of another action. However, I am not sure how helpful this information is, since the reducer itself appears to be working properly when responding to the action -- it's just that something strange is happening with the state, which prevents its elements from being returned.
From the sounds of it (and from the react callstack), I imagine the array changing (by reference) in the store is being picked up by a react component props, which in its should/did update logic is calling that action without a guard. This is often a mistake when calling actions or setState from componentDidMount/Update -
It works when the original state is returned as the reference is the same so React does not continue with its update logic, and hence call your code that publishes the action
Consider this pure component that will cause an endless loop with your reducer code...
export interface IMyProps {
myArray: any[],
updateEngagementAction: () => void
}
export class EndlessLoopFromArrayPropComponent extends React.PureComponent<IMyProps> {
// PureComponent compares props by reference only
// so will cause update if this.props.myArray reference has changed in store (ie from slice())
render() {
// blahblah...
}
componentDidUpdate() {
// this will call action after every update
// as every time this action is called it passes in a new reference this.props.myArray to this component
// so react will update this component again, causing the action to be called again
// ... endless loop
this.props.updateEngagementAction()
}
}
Your implementation will differ of course but this will be the principal that is causing it to happen, so you need to add a guard condition in whatever code path leads to your action being called.
For the code above you would need to check an escape condition before sending the action OR implement shouldComponentUpdate or similar to do deeper prop comparison to guard against unnecessary updates, and hence it would not reach that action code in the componentDidUpdate method
EDIT This was written before the react code was added to question. Here I refer to the action being called without guard in componentDidUpdate however the same applies when called in any of the other lifecycle methods triggered by a prop change, in this case componentWillRecieveProps. To be fair it did have a guard already, but never returned false as a more in-depth props check was needed, so caused a loop to occur via willreceive -> true -> action -> reducer -> willreceive -> true ........

Amazon IAP Plugin for Xamarin - crash when using TaskCompletionSource

I'm trying to implement a wrapper for the Amazon IAP Plugin for Xamarin. It uses an event based system in the following way:
You can initiate method calls and listen for events. Method calls initiate requests, some of which return a response. Events are asynchronous system-generated messages that are sent in response to method calls to return the requested data to you.
See more here
My goal is to wrap this event based system into some API which allows me to use the plugin with tasks, so I can use the async-await syntax. To achieve that I'm using the TaskCompletionSource like in the following example:
public async Task<bool> GetProductInfoAsync(params string[] productIds)
{
var iapService = AmazonIapV2Impl.Instance;
var tcs = new TaskCompletionSource<bool>();
var skus = new SkusInput { Skus = productIds.ToList() };
var requestId = iapService.GetProductData(skus).RequestId;
GetProductDataResponseDelegator delegator = null;
delegator = new GetProductDataResponseDelegator(response =>
{
if(response.Id == requestId) {
var result = GetResultFromResponse(response);
tcs.SetResult(result);
//iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate);
}
});
iapService.AddGetProductDataResponseListener(delegator.responseDelegate);
return await tcs.Task;
}
This code seems to work fine if the method gets called once, but if it gets called two times in a row the app crashes immediately and the only thing printed to the console is the following message..
[mono] Unhandled Exception:
[mono] System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
..which kinda makes no sense at all.
So is there something obvious I'm missing here? Or could it be a bug from the plugin?
I have created a repository with the code above so you can reproduce the problem. It's my playground, so please ignore the whole structure of the project and just focus on the classes AmazonIAPService and MainActivity.
Hint 1:
The commented line //iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate); causes also a crash with the same message but already at the first call of the method.
Hint 2:
The AmazonIAPService contains a commented method which uses await Task.Delay(TimeSpan.FromMilliseconds(1)) and solves the problem from above in a very hacky way which I really don't like.
Problem seems to be that those functions have to run asynchronously. Also mentioned here in the doc. So once you run those functions synchronously somehow they throw exception, i dont what is happening in the library but your hacky solution is the actual solution for that. If you write the function as below. it also works.
PurchaseResponseDelegator delegator = null;
delegator = new PurchaseResponseDelegator(async response =>
{
await Task.Run(() =>
{
if (response.RequestId == requestId)
{
var result = GetPurchaseEventHandler(response);
var sucess = taskCompletionSource.TrySetResult(result);
context.RemovePurchaseResponseListener(delegator.responseDelegate);
}
} );
});
// Register for an event
context.AddPurchaseResponseListener(delegator.responseDelegate);
One other exception I had despite the async-await solution, somehow, it always throws exception for the line taskCompletionSource.SetResult(result); for PurchaseUpdates functions only. if i use instead this line var sucess = taskCompletionSource.TrySetResult(result); it works fine

Selenium waitForCondition

I am doing Selenium testing for the first time. On the homepage, I call some AJAX, and i want Selenium to wait for the element to load finish. I not sure it works, but i just type selenium and the waitForCondition are able to choose.
I not matter what I choose it always return "false". I do not now if the waitForCondition even work?
How can I test if it works?
And what am I doing wrong in this codes?
selenium.waitForCondition("//input[#name='Report'", "3000");
selenium.waitForCondition("//*[#id='MyTable']", "3000");
selenium.waitForCondition("css=.someClass2", "3000");
If I implement by own class - it return "true"
private boolean isElementPresent(By by) {
try {
driver.findElement(by);
return true;
} catch (NoSuchElementException e) {
return false;
}
}
isElementPresent(By.xpath("//*[#id='MyTable']")) - return "true"
waitForCondition is for Javascript calls only, not for waiting for elements to load.
What you have in isElementPresent is fine. I would combine it with explicit waits to be a bit more accurate about when an element is actually loaded and present on the screen:
http://seleniumhq.org/docs/04_webdriver_advanced.html
C#
You can do it like this:
First of all you can set timeout value for the condition.
Then you can use the condition.
var Wait = new WebDriverWait(GlobalDriver, TimeSpan.FromMinutes(1));
Wait.Until(ExpectedConditions.PresenceOfAllElementsLocatedBy(By.XPath("xPath"))));
OR
Wait.Until(driver => driver.FindElement(By.XPath("xPath")));
Thats all.
You can do it like this :
selenium.waitForCondition("selenium.isElementPresent(\"//input[#name='Report']\")", "30000");
This will wait for the element to be loaded till 30 seconds.
Hope this works for you
new WebDriverWait(driver, 30).until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
JavascriptExecutor js = (JavascriptExecutor) driver;
return (Boolean) js.executeScript("return jQuery.active == 0");
}
});
This will check if the jQuery library has any active AJAX requests for 30 seconds.
Aaran referred you to the correct documentation for Selenium WebDriver waits.
You can see that they also write about ExpectedConditions class. This contains several helpful implementation of ExpectedCondition classes, such at the "is element present one", which is ExpectedConditions.presenceOfElementLocated .
Here is an example of using it:
WebDriver driver = new ChromeDriver();
driver.get("http://www.degraeve.com/reference/simple-ajax-example.php");
driver.findElement(By.name("word")).sendKeys("bird is the word");;
driver.findElement(By.cssSelector("input[type='button']")).click();
WebDriverWait driverWait = new WebDriverWait(driver, 10);
WebElement dynamicElement = driverWait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("#result p")));
System.out.println(dynamicElement.getText());
If you find it too verbose, why won't you just refactor it and extract a function which will accept the element locator and the webdriver, and returns you the element?
DriverWait.until() accepting an ExpectedCondition instance is like a way of passing a predicate function, just doing it through a class, or in the documentation example an anonymous nested class, since under Java you can't send a function.
The ExpectedCondition "function" you pass also returns a value, which can be useful in case you're waiting for a condition on some element (or some other value from the WebDriver), so returning it will save you an extra call.
Try this once:
await().atMost(10, SECONDS).until(() -> driver.findElements(By.id("elementId")).size() >1);

jQuery.ajax(): discard slow requests

I've build a livesearch with the jQuery.ajax() method. On every keyup events it receives new result data from the server.
The problem is, when I'm typing very fast, e.g. "foobar" and the GET request of "fooba" requires more time than the "foobar" request, the results of "fooba" are shown.
To handle this with the timeout parameter is impossible, I think.
Has anyone an idea how to solve this?
You can store and .abort() the last request when starting a new one, like this:
var curSearch;
$("#myInput").keyup(function() {
if(curSearch) curSearch.abort(); //cancel previous search
curSearch = $.ajax({ ...ajax options... }); //start a new one, save a reference
});
The $.ajax() method returns the XmlHttpRequest object, so just hang onto it, and when you start the next search, abort the previous one.
Assign a unique, incrementing ID to each request, and only show them in incrementing order. Something like this:
var counter = 0, lastCounter = 0;
function doAjax() {
++counter;
jQuery.ajax(url, function (result) {
if (counter < lastCounter)
return;
lastCounter = counter;
processResult(result);
});
}
You should only start the search when the user hasn't typed anything for a while (500ms or so). This would prevent the problem you're having.
An excellent jQuery plugin which does just that is delayedObserver:
http://code.google.com/p/jquery-utils/wiki/DelayedObserver
Make it so each cancels the last. That might be too much cancellation, but when typing slows, it will trigger.
That seems like an intense amount of traffic to send an ajax request for every KeyUp event. You should wait for the user to stop typing - presumably that they are done, for at least a few 100 milliseconds.
What I would do is this:
var ajaxTimeout;
function doAjax() {
//Your actual ajax request code
}
function keyUpHandler() {
if (ajaxTimeout !== undefined)
clearTimeout(ajaxTimeout);
ajaxTimeout = setTimeout(doAjax, 200);
}
You may have to play with the actual timeout time, but this way works very well and does not require any other plugins.
Edit:
If you need to pass in parameters, create an inline function (closure).
...
var fun = function() { doAjax(params...) };
ajaxTimeout = setTimeout(fun, 200);
You will want some kind of an ajax queue such as:
http://plugins.jquery.com/project/ajaxqueue
or http://www.protofunc.com/scripts/jquery/ajaxManager/
EDIT:Another option, study the Autocomplete plug-in code and emulate that.(there are several Autocomplete as well as the one in jquery UI
OR just implement the Autocomplete if that serves your needs

Resources