-
Notifications
You must be signed in to change notification settings - Fork 14
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
fix: premature resolution of wait conditions when using scopedBy page objects #326
base: main
Are you sure you want to change the base?
fix: premature resolution of wait conditions when using scopedBy page objects #326
Conversation
Travis CI has been really bad lately. It's not picking up new builds. |
Is this because this is on a fork? Did a force push and seems to have picked it up that time |
1bf8a3c
to
c5f670a
Compare
@kellyselden finally got this to behave 🎉 |
try { | ||
return await this._browser.waitUntil(async () => await this.isExisting()); | ||
} catch (e) { | ||
// if the waitUntil condition fails fall through to the original non-scoped |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will unfortunately double the waiting time of failing tests, right? 30 secs to 60 secs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the default timeout is still 30 seconds. I tested this by commenting out all of the timeout overrides in one of the tests and allowing it to fail, which it did after 30 seconds
|
||
await this.open('index.html'); | ||
|
||
this.page._browser._browser.options.waitforTimeout = 10000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The browser is global, this will leak across the whole test suite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a timeout value reset in afterEach
hook
element.disabled = false; | ||
|
||
document.body.appendChild(element); | ||
}, 100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has a race condition. If the assertion runs before this, the test will fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intentional I want scopeBy
to start running and respond to the element changing state in order to test that the wait condition is working.
@@ -41,6 +41,16 @@ class BaseElement extends BasePageObject { | |||
|
|||
async waitForInsert() { | |||
await handleError.call(this, async () => { | |||
if (typeof this._selector === 'function') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These can go in a reusable function, kinda like handleError
so they both can use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you want it called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
waitForScoped?
@@ -41,6 +41,16 @@ class BaseElement extends BasePageObject { | |||
|
|||
async waitForInsert() { | |||
await handleError.call(this, async () => { | |||
if (typeof this._selector === 'function') { | |||
try { | |||
return await this._browser.waitUntil(async () => await this.isExisting()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this go in the browser's waitForExist
implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that first and ran into so many issues because of all of the areas in which waitForExists
impacts. Opted to hoist it up to here for simplicity sake.
packages/page-objects/src/element.js
Outdated
@@ -12,10 +12,12 @@ class Element extends BaseElement { | |||
} | |||
|
|||
async waitForEnabled() { | |||
await this.waitForInsert(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these are necessary. The existing assumption is that you are already dealing with an existing element when you call these methods. You are waiting for an element to go from disabled to enabled. If you're element is missing to begin with, the consumer should be tasked with calling waitForInsert
first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like it is better developer ergonomics for consumers to not have to be concerned with that, as well as, fewer wait condition lines in tests.
I'll remove this, but I think we should consider putting it back in to make for a smoother testing experience.
c5f670a
to
0e77136
Compare
When using a function style selector wait conditions fail early if the scoped selector does not yield any results. Instead we should continue to wait/check until the wait condition times out. Additionally in order to retain custom friendly error messages that include the selector that an exception occurred on we fallback to the original wait behavior which includes the custom errors.
before waiting on other conditions like enabled/visible
0e77136
to
d68f9aa
Compare
@@ -41,6 +41,16 @@ class BaseElement extends BasePageObject { | |||
|
|||
async waitForInsert() { | |||
await handleError.call(this, async () => { | |||
if (typeof this._selector === 'function') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
waitForScoped?
if (this.browser && this.browser.options) { | ||
// some of the tests need to validate wait conditions and modify the wait | ||
// timeout so this needs to be reset to avoid leaking context between tests | ||
this.browser.options.waitforTimeout = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should check out isExisting
. It would be better to not assume any hard coded numbers here.
await this.writeFixture('index.html', ` | ||
<input class="foo"> | ||
`); | ||
describe(Element.prototype.waitForEnabled, function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the waiting in these methods was removed, I don't think any of these extra tests are needed anymore.
@@ -56,6 +66,16 @@ class BaseElement extends BasePageObject { | |||
|
|||
async waitForDestroy() { | |||
await handleError.call(this, async () => { | |||
if (typeof this._selector === 'function') { | |||
try { | |||
return await this._browser.waitUntil(async () => !(await this.isExisting())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return value is not used in this method.
|
||
await this.open('index.html'); | ||
|
||
this.page._browser._browser.options.waitforTimeout = 10000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you think 1 second is enough?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
10000 = 10,000 = 10 seconds, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, I'm proposing switching to 1000, and also asking if that would be enough.
.to.eventually.be.fulfilled; | ||
}); | ||
|
||
it('waits for scoped element to be inserted', async function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe putting these in their own describe block would be better? not sure, but then you could set the new timeout in the beforeEach.
When using things like
await this.users.scopeByName('some name').waitForVisible();
the promise immediately rejects because the scoped selector cannot be found.Example error: