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
[E2E] Stabilize a flaky Price Filter test #44690
Conversation
@lanej0, I've just noticed you recently updated that test as well in #44440. Let me know if this solution makes sense to you. It seems to be addressing the direct cause of the flakiness. Also, would it make sense to report the weird filter behavior since the All Products block is going to be deprecated? cc: @gigitux |
await frontendUtils.selectTextInput( maxPriceInput ); | ||
await maxPriceInput.fill( '$10', { | ||
// eslint-disable-next-line playwright/no-force-option | ||
force: true, | ||
} ); | ||
await maxPriceInput.dblclick(); | ||
await maxPriceInput.fill( '$10' ); |
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.
Double click seems to be working just as well and is likely closer to how a user would select the value. 😄
Hi @lanej0, @gigitux, @dinhtungdu, @kmanijak, @woocommerce/woo-fse Apart from reviewing the code changes, please make sure to review the testing instructions as well. You can follow this guide to find out what good testing instructions should look like: |
await page.waitForResponse( ( response ) => | ||
response.url().includes( blockData.endpointAPI ) | ||
); |
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.
It's generally a bad practice to call and await waitForResponse/Request
simultaneously since the response/request might already be over when this is called.
The Waiting for event guide section has some good examples of using that API correctly.
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.
Also, this is not necessary for the test to succeed so I removed it 😄
expect( products ).toHaveLength( 1 ); | ||
await expect( allProducts ).toHaveCount( 1 ); |
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.
For locators, we can use the dedicated toHaveCount
assertion.
force: true, | ||
} ); | ||
await maxPriceInput.dblclick(); | ||
await maxPriceInput.fill( '$10' ); | ||
await maxPriceInput.press( 'Tab' ); |
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 feels weird, not from the test but from the feature implementation perspective. Hitting Tab changes the focus to another element and loses the filter from the sight, so is that how it's intended to work? Why not update on value change or Enter? Not sure who to mention here, actually 😛 cc: @gigitux
Test Results SummaryCommit SHA: 33e4245
To view the full API test report, click here. To view the full E2E test report, click here. To view all test reports, visit the WooCommerce Test Reports Dashboard. |
This is usually a bad practice as we're allowing the tests to start before the DOM is loaded, which can occasionally time-out events like locators waiting for their respective elements to appear.
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.
Besides the comment about timeout, this is looking good to me. Thank you for working on this!
await page | ||
.getByRole( 'textbox', { | ||
name: 'Filter products by maximum price', | ||
disabled: true, | ||
} ) | ||
.waitFor( { timeout: 3000 } ) | ||
.catch( () => { | ||
// Do not throw in case Playwright doesn't make it in time for the | ||
// initial (pre-request) render. | ||
} ); |
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.
So, initially, we had flaky tests because we got the price input field, but it became disabled at the time we tried to change the price.
With this code, we try to get a disabled input first, for three seconds. Then we wait and get the active one.
But if there is an issue with the test environment, after three seconds, the input becomes disabled again, will we get the original flakiness again?
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.
So, initially, we had flaky tests because we got the price input field, but it became disabled at the time we tried to change the price.
Not exactly. I'll try to explain using some example scenarios:
Failure Example Due to Race Condition:
- Initialization: The price filter input field is rendered on the UI.
- Interaction: The automation framework Playwright identifies the input field as enabled and inputs a value of $10.
- Concurrent Processing: Concurrently, the application begins to fetch filter data, during which it programmatically disables the input field.
- Data Integration: Upon completion of data retrieval, the application logic resets the price filter to a value of $90 based on the fetched data and re-enables the input field.
- Assertion Failure: The test assertion fails as the expected value of $10 is overwritten by the reset operation, resulting in a value of $90.
Success Example Despite Race Condition:
- Initialization: The price filter input field is rendered on the UI.
- Data Pre-fetching: The application initiates the data fetching process for filter criteria and disables the input field.
- Deferred Interaction: Playwright attempts to input a value of $10 but finds the input field disabled. It enters a wait state until the field becomes interactive.
- Data Integration and Reactivation: Once data fetching concludes, the application resets the price filter value based on the retrieved data, defaulting it to $90, and subsequently re-enables the input field.
- Successful Interaction: Playwright, detecting the re-enabled state of the input field, proceeds to set the filter value to $10.
- Assertion Success: The test passes as Playwright successfully sets the expected value post-data fetching process, aligning with the test conditions.
But if there is an issue with the test environment, after three seconds, the input becomes disabled again, will we get the original flakiness again?
Fortunately, a disabled input is not an issue for Playwright as it will enter a waiting state until the input is enabled.
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 still have another question 😄 .
Just theoretically, if there is an issue that delays the data fetching to more than three seconds after load:
- Initialization: The price filter input field is rendered on the UI.
- Delayed data fetching: the input field is still enabled.
- Timed-out waiting for disabled input: PW keeps waiting for a disabled input and timed out after three seconds.
- Interaction: After three seconds, The automation framework Playwright identifies the input field as enabled and inputs a value of $10.
- Concurrent Processing: Concurrently, the application begins to fetch filter data, during which it programmatically disables the input field.
- Data Integration: Upon completion of data retrieval, the application logic resets the price filter to a value of $90 based on the fetched data and re-enables the input field.
- Assertion Failure: The test assertion fails as the expected value of $10 is overwritten by the reset operation, resulting in a value of $90.
Is this a valid assumption?
I can see this may not even be possible and your updates here will solve most of the flakiness we have. So please consider my comment here is just for learning and curiosity. The code change in this PR is LGTM.
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.
Is this a valid assumption?
Playwright will time out and fail the test if the data fetching is delayed to over 3 seconds since the filter input has become visible in the DOM (which is when Playwright starts the disabled
locator timer). Therefore, nothing after step no. 3 from the sequence you provided would happen.
I can see this may not even be possible and your updates here will solve most of the flakiness we have.
I do think that this is an edge-case scenario at most! 😄 While my fix addresses the flakiness, the actual fix should be provided in the app logic instead. The input should be rendered disabled, and only enabled once the data is initialized. That way we wouldn't need any extra handling in the test. Does that make sense?
So please consider my comment here is just for learning and curiosity. The code change in this PR is LGTM.
Thanks for diving into this PR and sharing your thoughts! 😊 I totally get where you're coming from with your questions. Glad to hear the changes look good to you. Happy to answer any more questions or ideas.
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.
Thanks for your reply! It's clear now
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.
As discussed in the review, this is LGTM!
Changes proposed in this Pull Request:
Stabilize a flaky Price Filter test by addressing the unstable nature of the filter input (see inline comments).
How to test the changes in this Pull Request:
The test should pass in CI. To test locally:
cd plugins/woocommerce-blocks
pnpm env:start
pnpm test:e2e:side-effects -g "should show only products that match the filter"