Skip to content
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

Merged
merged 5 commits into from Feb 20, 2024

Conversation

WunderBart
Copy link
Contributor

@WunderBart WunderBart commented Feb 16, 2024

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:

  1. cd plugins/woocommerce-blocks
  2. pnpm env:start
  3. pnpm test:e2e:side-effects -g "should show only products that match the filter"

@WunderBart
Copy link
Contributor Author

WunderBart commented Feb 16, 2024

@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

@woocommercebot woocommercebot requested a review from a team February 16, 2024 10:49
Comment on lines -71 to +83
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' );
Copy link
Contributor Author

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. 😄

Copy link
Contributor

github-actions bot commented Feb 16, 2024

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:
https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions

Comment on lines -77 to -79
await page.waitForResponse( ( response ) =>
response.url().includes( blockData.endpointAPI )
);
Copy link
Contributor Author

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.

Copy link
Contributor Author

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 😄

Comment on lines -94 to +99
expect( products ).toHaveLength( 1 );
await expect( allProducts ).toHaveCount( 1 );
Copy link
Contributor Author

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' );
Copy link
Contributor Author

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

Copy link
Contributor

github-actions bot commented Feb 16, 2024

Test Results Summary

Commit SHA: 33e4245

Test 🧪Passed ✅Failed 🚨Broken 🚧Skipped ⏭️Unknown ❔Total 📊Duration ⏱️
API Tests25900202610m 36s
E2E Tests306001903257m 13s

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.
@WunderBart WunderBart added the focus: e2e tests Issues related to e2e tests label Feb 16, 2024
@WunderBart WunderBart self-assigned this Feb 16, 2024
@github-actions github-actions bot added plugin: woocommerce Issues related to the WooCommerce Core plugin. and removed focus: e2e tests Issues related to e2e tests labels Feb 16, 2024
Copy link
Member

@dinhtungdu dinhtungdu left a 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!

Comment on lines +68 to +77
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.
} );
Copy link
Member

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?

Copy link
Contributor Author

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:

  1. Initialization: The price filter input field is rendered on the UI.
  2. Interaction: The automation framework Playwright identifies the input field as enabled and inputs a value of $10.
  3. Concurrent Processing: Concurrently, the application begins to fetch filter data, during which it programmatically disables the input field.
  4. 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.
  5. 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:

  1. Initialization: The price filter input field is rendered on the UI.
  2. Data Pre-fetching: The application initiates the data fetching process for filter criteria and disables the input field.
  3. 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.
  4. 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.
  5. Successful Interaction: Playwright, detecting the re-enabled state of the input field, proceeds to set the filter value to $10.
  6. 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.

Copy link
Member

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:

  1. Initialization: The price filter input field is rendered on the UI.
  2. Delayed data fetching: the input field is still enabled.
  3. Timed-out waiting for disabled input: PW keeps waiting for a disabled input and timed out after three seconds.
  4. Interaction: After three seconds, The automation framework Playwright identifies the input field as enabled and inputs a value of $10.
  5. Concurrent Processing: Concurrently, the application begins to fetch filter data, during which it programmatically disables the input field.
  6. 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.
  7. 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.

Copy link
Contributor Author

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.

Copy link
Member

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

Copy link
Member

@dinhtungdu dinhtungdu left a 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!

@WunderBart WunderBart merged commit 82d1df5 into trunk Feb 20, 2024
67 checks passed
@WunderBart WunderBart deleted the tweak/stabilize-flaky-price-filter-e2e branch February 20, 2024 15:30
@github-actions github-actions bot added this to the 8.7.0 milestone Feb 20, 2024
@github-actions github-actions bot added the needs: analysis Indicates if the PR requires a PR testing scrub session. label Feb 20, 2024
@alvarothomas alvarothomas added status: analysis complete Indicates if a PR has been analysed by Solaris and removed needs: analysis Indicates if the PR requires a PR testing scrub session. labels Feb 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plugin: woocommerce Issues related to the WooCommerce Core plugin. status: analysis complete Indicates if a PR has been analysed by Solaris
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants