New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Screenplay-style explicit and fluent wait #7

Closed
srinivasbudh opened this Issue Dec 1, 2016 · 13 comments

Comments

3 participants
@srinivasbudh

srinivasbudh commented Dec 1, 2016

Hi,

I Loved the concept of Screenplay pattern. I would love to learn this. Concept is clear.
But I'm new to Automation with Javascript. Used serenity in the past with Java. I'm wondering if there is a boilerplate available for javascript/serenityjs(Screenplay pattern)/protractor combination.

Waiting to see a boilerplate using javascript :)
Thanks in Advance

Regards
Srini

@srinivasbudh

This comment has been minimized.

Show comment
Hide comment
@srinivasbudh

srinivasbudh Dec 1, 2016

Kindly let me if this is not a relevant area for this query as this is not a real issue. I can close this if required

srinivasbudh commented Dec 1, 2016

Kindly let me if this is not a relevant area for this query as this is not a real issue. I can close this if required

@jan-molak

This comment has been minimized.

Show comment
Hide comment
@jan-molak

jan-molak Dec 1, 2016

Owner

Hey @srinivasbudh and thank you for your kind words and your interest in Serenity/JS :-)

The best way to get started with Serenity/JS is to go through the tutorials.

If you want to just have a look at a finished implementation of some example projects, you can head to the serenity-js-getting-started, which is the project you'd be working on as part of the tutorial, and check out the branch called 2-reports.

There's also an example implementation located in the examples folder of this repository, which you might find useful.

Does this answer your question?

Best,
Jan

Owner

jan-molak commented Dec 1, 2016

Hey @srinivasbudh and thank you for your kind words and your interest in Serenity/JS :-)

The best way to get started with Serenity/JS is to go through the tutorials.

If you want to just have a look at a finished implementation of some example projects, you can head to the serenity-js-getting-started, which is the project you'd be working on as part of the tutorial, and check out the branch called 2-reports.

There's also an example implementation located in the examples folder of this repository, which you might find useful.

Does this answer your question?

Best,
Jan

@srinivasbudh

This comment has been minimized.

Show comment
Hide comment
@srinivasbudh

srinivasbudh Dec 1, 2016

Hey @jan-molak,

Thanks for your response. Yes I have gone through the implementation using your tutorials. In fact I have implemented 2-reports branch using your tutorials.

I was looking for a project which would use .js files rather than .ts files for step definition. Is this possible for implementation??

Reason I am asking this is Few of the protractor commands I tried executing are not compilable in .ts files.

Example: browser.waitForCondition(.....)

Thanks
Srini

srinivasbudh commented Dec 1, 2016

Hey @jan-molak,

Thanks for your response. Yes I have gone through the implementation using your tutorials. In fact I have implemented 2-reports branch using your tutorials.

I was looking for a project which would use .js files rather than .ts files for step definition. Is this possible for implementation??

Reason I am asking this is Few of the protractor commands I tried executing are not compilable in .ts files.

Example: browser.waitForCondition(.....)

Thanks
Srini

@jan-molak

This comment has been minimized.

Show comment
Hide comment
@jan-molak

jan-molak Dec 1, 2016

Owner

Ah, I see. TypeScript is a superset of JavaScript, so technically speaking, any code that's valid JavaScript should work when defined in a .ts file.

What I guess might be happening in your case might not necessarily be due to JavaScript/TypeScript problem. What it feels like is that browser.waitForCondition(.....) does not get synchronised correctly with the rest of the flow due to how webdriver-js handles promises using what they call the ControlFlow, which although interesting, is a webdriver-specific, non-standard way of handling asynchronous operations.

In order to resolve this problem Serenity/JS needs to provide a Task centred around waiting for things to happen, effectively wrapping the browser.waitForCondition(.....) and converting it to a standard JS Promise, so that it can be chained with other tasks.

Is that's what you think might help?

Would you mind giving me an example of what is that you'd like the test scenario to wait for in your case (I might just prioritise it over other "waits" I need to implement ;-) ).

Owner

jan-molak commented Dec 1, 2016

Ah, I see. TypeScript is a superset of JavaScript, so technically speaking, any code that's valid JavaScript should work when defined in a .ts file.

What I guess might be happening in your case might not necessarily be due to JavaScript/TypeScript problem. What it feels like is that browser.waitForCondition(.....) does not get synchronised correctly with the rest of the flow due to how webdriver-js handles promises using what they call the ControlFlow, which although interesting, is a webdriver-specific, non-standard way of handling asynchronous operations.

In order to resolve this problem Serenity/JS needs to provide a Task centred around waiting for things to happen, effectively wrapping the browser.waitForCondition(.....) and converting it to a standard JS Promise, so that it can be chained with other tasks.

Is that's what you think might help?

Would you mind giving me an example of what is that you'd like the test scenario to wait for in your case (I might just prioritise it over other "waits" I need to implement ;-) ).

@srinivasbudh

This comment has been minimized.

Show comment
Hide comment
@srinivasbudh

srinivasbudh Dec 1, 2016

That's good to see a response, so quick.

In my case I am using Protractor with a non-angular application my UI is designed in reactJS. So I can't use waitForAngular for the page to load.

When I navigate in between pages (example: gmail) It's easy to land on login page, However After login it might take few seconds(1-10 depending on the speed of internet) for the inbox to be displayed. In this case I want to make my code to wait till it find the compose button(may be with a time limit of 10 seconds).

I assume this wait can be handled using Explicit wait, which is throwing compilation error on this files.

srinivasbudh commented Dec 1, 2016

That's good to see a response, so quick.

In my case I am using Protractor with a non-angular application my UI is designed in reactJS. So I can't use waitForAngular for the page to load.

When I navigate in between pages (example: gmail) It's easy to land on login page, However After login it might take few seconds(1-10 depending on the speed of internet) for the inbox to be displayed. In this case I want to make my code to wait till it find the compose button(may be with a time limit of 10 seconds).

I assume this wait can be handled using Explicit wait, which is throwing compilation error on this files.

@jan-molak jan-molak changed the title from Screenplay pattern with Javascript to Support for Screenplay-style explicit and fluent wait Dec 6, 2016

jan-molak added a commit that referenced this issue Dec 29, 2016

feat(interactions): Screenplay-style explicit and fluent Wait
`Wait.for(d: Duration)` allows to make the test wait for an arbitrary period of time (explicit

wait); `Wait.until(t: Target, c: Condition)` and `Wait.upTo(d: Duration).until(t: Target, c:

Condition)` allow to make the test wait until a condition is met. For example:

`Wait.upTo(Duration.ofSeconds(0.5)).until(Notification.Message, Is.visible())`

Partially addresses #7

@jan-molak jan-molak closed this in 660f618 Dec 29, 2016

@jan-molak

This comment has been minimized.

Show comment
Hide comment
@jan-molak

jan-molak Dec 29, 2016

Owner

Hey @srinivasbudh,

The latest version of Serenity/JS should support your use case.
Please have a look at the spec/cookbook/waiting.recipe.ts

There are two ways you can wait for things to happen:

  • Using PassiveWait (an equivalent of browser.sleep() in Protractor):
actor.attemptsTo(
  Wait.for(Duration.ofMillis(500)),        // *assumes* that the button will show up during 500ms
  Click.on(InboxNavigation.Compose_Button)
)
  • Using ActiveWait (waiting up to some timeout, polling the page repeatedly to check if the condition is met):
actor.attemptsTo(
  Wait.until(InboxNavigation.Compose_Button, Is.clickable()),
  Click.on(InboxNavigation.Compose_Button)
)

You can also specify the maximum timeout:

actor.attemptsTo(
  Wait.upTo(Duration.ofSeconds(10)).until(InboxNavigation.Compose_Button, Is.clickable()),
  Click.on(InboxNavigation.Compose_Button)
)

I hope that this helps you solve your problem! Please let me know how you're getting on with this new feature :-)

Best,
Jan

Owner

jan-molak commented Dec 29, 2016

Hey @srinivasbudh,

The latest version of Serenity/JS should support your use case.
Please have a look at the spec/cookbook/waiting.recipe.ts

There are two ways you can wait for things to happen:

  • Using PassiveWait (an equivalent of browser.sleep() in Protractor):
actor.attemptsTo(
  Wait.for(Duration.ofMillis(500)),        // *assumes* that the button will show up during 500ms
  Click.on(InboxNavigation.Compose_Button)
)
  • Using ActiveWait (waiting up to some timeout, polling the page repeatedly to check if the condition is met):
actor.attemptsTo(
  Wait.until(InboxNavigation.Compose_Button, Is.clickable()),
  Click.on(InboxNavigation.Compose_Button)
)

You can also specify the maximum timeout:

actor.attemptsTo(
  Wait.upTo(Duration.ofSeconds(10)).until(InboxNavigation.Compose_Button, Is.clickable()),
  Click.on(InboxNavigation.Compose_Button)
)

I hope that this helps you solve your problem! Please let me know how you're getting on with this new feature :-)

Best,
Jan

jan-molak added a commit that referenced this issue Dec 29, 2016

feat(interactions): Active Wait - Screenplay-style equivalent of Prot…
…ractor's `ExpectedConditions`

You can now wait for a condition to occur before the actor proceeds with their tasks.

For example,you can wait for an element to become visible:
`Wait.upTo(Duration.ofMillis(500)).until(t: Target, Is.visible())`. C

heck out the `spec/cookbook/waiting.recipe.ts` to see other examples of usage.

Closes #7
@srinivasbudh

This comment has been minimized.

Show comment
Hide comment
@srinivasbudh

srinivasbudh Dec 29, 2016

Hi,

This Looks great I was looking for a similar solution, However this is not working when I tried Implementing it. Duration.seconds(10) gives me a error sating second is not a parameter. Tried using Duration.ofSeconds(60), Which is not throwing any error but Not Waiting for the element to be displayed and not throwing an error when locator is not available.

Wait.upTo(Duration.ofSeconds(10)).until(InboxNavigation.Compose_Button, Is.clickable())

srinivasbudh commented Dec 29, 2016

Hi,

This Looks great I was looking for a similar solution, However this is not working when I tried Implementing it. Duration.seconds(10) gives me a error sating second is not a parameter. Tried using Duration.ofSeconds(60), Which is not throwing any error but Not Waiting for the element to be displayed and not throwing an error when locator is not available.

Wait.upTo(Duration.ofSeconds(10)).until(InboxNavigation.Compose_Button, Is.clickable())

@jan-molak

This comment has been minimized.

Show comment
Hide comment
@jan-molak

jan-molak Dec 29, 2016

Owner

Sorry to hear that, would you be able to come up with a minimal test case to reproduce the issue?

Owner

jan-molak commented Dec 29, 2016

Sorry to hear that, would you be able to come up with a minimal test case to reproduce the issue?

@nbarrett

This comment has been minimized.

Show comment
Hide comment
@nbarrett

nbarrett Dec 30, 2016

Contributor

I've not had a look into the latest serenity recipes yet, but isn't the protractor 'way' to use expected conditions?

I've had success in the past with:

browser.wait(protractor.ExpectedConditions.and(
protractor.ExpectedConditions.visibilityOf(element()),
protractor.ExpectedConditions.elementToBeClickable(element())
)

Contributor

nbarrett commented Dec 30, 2016

I've not had a look into the latest serenity recipes yet, but isn't the protractor 'way' to use expected conditions?

I've had success in the past with:

browser.wait(protractor.ExpectedConditions.and(
protractor.ExpectedConditions.visibilityOf(element()),
protractor.ExpectedConditions.elementToBeClickable(element())
)

@srinivasbudh

This comment has been minimized.

Show comment
Hide comment
@srinivasbudh

srinivasbudh Dec 30, 2016

@jan-molak : This is working fine as expected Using it in my current project,I assume initially something went wrong with npm install. Now I'm able to use both active and passive waits.

Thanks a ton for your help.

srinivasbudh commented Dec 30, 2016

@jan-molak : This is working fine as expected Using it in my current project,I assume initially something went wrong with npm install. Now I'm able to use both active and passive waits.

Thanks a ton for your help.

@srinivasbudh

This comment has been minimized.

Show comment
Hide comment
@srinivasbudh

srinivasbudh Dec 30, 2016

@nbarrett : Yes, Protractor can handle this, I was also using something similar to handle this.
However we cant call below function inside performAs of any task as this not a user task. so this solution will help

export function WaitForElementToBeDisplayed(webElement, browser) {
    return browser.wait(function () {
        return browser.isElementPresent(webElement);
    }, 55000).then(isElementStable(webElement,browser));
}

function isElementStable(webElement,browser){
    var isStable = false;
    var count = 0;
    var elementLocation=webElement.getLocation();
    var initTop= elementLocation.y;
    var initLeft=elementLocation.x;
        while (!isStable) {
            if (initTop == webElement.getLocation().y
                && initLeft == webElement.getLocation().x) {
                count++;
            } else {
                count = 0;
                 initTop= elementLocation.y;
                 initLeft=elementLocation.x;

            }

            if (count == 10) {
                console.log('This Element is stable');
                isStable = true;
            }

            try {
                // sleep for 100ms to give page a little time
                browser.sleep(15);
            } catch (err) {
            }
        }

}

srinivasbudh commented Dec 30, 2016

@nbarrett : Yes, Protractor can handle this, I was also using something similar to handle this.
However we cant call below function inside performAs of any task as this not a user task. so this solution will help

export function WaitForElementToBeDisplayed(webElement, browser) {
    return browser.wait(function () {
        return browser.isElementPresent(webElement);
    }, 55000).then(isElementStable(webElement,browser));
}

function isElementStable(webElement,browser){
    var isStable = false;
    var count = 0;
    var elementLocation=webElement.getLocation();
    var initTop= elementLocation.y;
    var initLeft=elementLocation.x;
        while (!isStable) {
            if (initTop == webElement.getLocation().y
                && initLeft == webElement.getLocation().x) {
                count++;
            } else {
                count = 0;
                 initTop= elementLocation.y;
                 initLeft=elementLocation.x;

            }

            if (count == 10) {
                console.log('This Element is stable');
                isStable = true;
            }

            try {
                // sleep for 100ms to give page a little time
                browser.sleep(15);
            } catch (err) {
            }
        }

}
@srinivasbudh

This comment has been minimized.

Show comment
Hide comment
@srinivasbudh

srinivasbudh Dec 30, 2016

@nbarrett : isElementStable() method will avoid stale element exception

srinivasbudh commented Dec 30, 2016

@nbarrett : isElementStable() method will avoid stale element exception

@nbarrett

This comment has been minimized.

Show comment
Hide comment
@nbarrett

nbarrett Dec 30, 2016

Contributor

Hmm, I'd always avoid browser.sleep() as it's a generally recipe for unreliability. I've also never had to resort to verifying an element's stability by reference to it's x/y location as there are probably more conventional ways to overcome StaleElementReferenceException as discussed here.

Contributor

nbarrett commented Dec 30, 2016

Hmm, I'd always avoid browser.sleep() as it's a generally recipe for unreliability. I've also never had to resort to verifying an element's stability by reference to it's x/y location as there are probably more conventional ways to overcome StaleElementReferenceException as discussed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment