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

[1545] Add E2E Tests for Browse Bills Page #1569

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

HuanFengYeh
Copy link

@HuanFengYeh HuanFengYeh commented Jun 12, 2024

Summary

Issue: #1545
This PR adds Playwright end-to-end (E2E) testing for the Browse Bills page, including:

Finding bills via text search
Sorting bills by various criteria
Filtering bills based on different attributes

My Playwright version 1.43.1 and yarn version is 1.22.19

Checklist

Playwright test covering the Browse Bills page, including:
Able to find Bills via Text Search - finished
Able to sort Bills as expected - finished
Able to filter Bills as expected- finished

Screenshots

Screenshot 2024-06-12 at 9 08 32 AM

Known issues

1.The sorting function for "sort by relevance" is not implemented as I do not have access to the relevance score needed to test this functionality.
2.Only a couple of pages were tested due to time constraints, as testing every page would take too long.
3.For the filtering function, the tests mostly focus on ensuring that the labels are showing correctly. The content itself wasn't thoroughly checked since this would require looking into the bill page in detail, which is extremely time-consuming.

Steps to test/reproduce

To run the e2e tests with the Playwright UI, run yarn test:e2e.

Copy link

vercel bot commented Jun 12, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
maple-dev ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 22, 2024 8:04am

Copy link
Collaborator

@Mephistic Mephistic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic start!

This is the first big E2E test that will be setting an example for others going forward, so I think it's worth drilling into the details here to make sure we're setting other devs up for success going forward.

@seatuna Would love your thoughts on this. This is likely also relevant to your tests for the Browse Testimonies page, so we may be able to de-duplicate effort here.

}
]

const WriteContent = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the translation work, but please make the translation changes and the new test two separate PRs.

* Function to get a random word from a predefined list.
* @returns A random word from the list.
*/
const getRandomWord = (): string => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like picking random words to search might be more complex than what we need here - it could lead to test flappiness. I would pick one of these words that consistently turns up results and just use that throughout. If/when we start making test-specific data fixtures, this would also be easier to swap out.

Our main goal here is to ensure that search works at all - not examine the specifics of how the search works (which can change with Typesense config/versions).

Copy link
Author

@HuanFengYeh HuanFengYeh Jun 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could definitely make things more complicated and lead to inconsistent test results. I'll stick with a word for our tests.

) => {
await page.waitForFunction(initialResultCount => {
const currentResultCount = document.querySelector(
".ResultCount__ResultContainer-sc-3931e200-0"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC classes like .ResultCount__ResultContainer-sc-3931e200-0 are generated by styled components and can potentially change between builds or with trivial formatting changes - could we use a more resilient locator here (and the other places that use these classes)?

Copy link
Author

@HuanFengYeh HuanFengYeh Jun 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct that classes like .ResultCount__ResultContainer-sc-3931e200-0 are generated by styled components and can indeed change between builds or due to trivial formatting changes, leading to unstable locators.

I am considering two alternatives and would appreciate your input on which method we should proceed with:

1.Add New Data Attribute: Adding a custom data attribute to the elements we want to target. This approach would make our locators highly stable and independent of styling changes.

2.Using Utility Classes: Utilizing existing utility classes like .flex-grow-1 and .m-1. This method offers some stability but still relies on styling changes to some extent.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the results count text, I used page.getByText("Results").first(). If you want the text you're searching for to be more specific, you can use regex to target multiple words that will show up in that string like "showing" and "results" (this would definitely complicate it but make it more accurate) ex. page.getByText(\Results\). Just searching "Results" and selecting the first one worked out for the Testimony page.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, adding a data-test-id is also a very good idea and would guarantee you're finding the correct element


await page.goto(firstBillLink)

const readmorebtn = page.locator(".Summary__StyledButton-sc-791f19-3")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: camelCase for variable names

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for point out, Corrected !

* @param page - The Playwright page object.
* @param searchTerm - The search term to validate in the bill content.
*/
const checkFirstBill = async (page: Page, searchTerm: string) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could we call this something like checkFirstBillResultMatchesSearchTerm to make it more clear what it's actually checking?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After chewing on this a bit, I'm not convinced we want to be mirroring the search logic in this test - given the scope of what we're trying to catch here, there are two things we could be checking here:
1.) That, should we get any search results for a term, we can follow the search result's link to the corresponding bill page.
2.) That, should we provide a specific search criteria, we find a specific bill that matches that criteria.

IMO (1) is all we need to start here. (2) is trickier (even if we set up test fixtures, we would need to avoid confounding bills and that would be a continual problem).

I don't want us to get in the business of mirroring business logic in the tests - I'm willing to rely on the Typesense tests to verify that certain search terms/facets will result in relevant results. The point of these tests should be to verify that the bones of the site work (e.g. whatever bills we get back from the search results for a term are visitable, not that the search results are relevant). Open to discussion here, but want to avoid overcomplicating this ticket or introducing fragility into the test stream.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To align with these points, I suggest we concentrate on verifying the basic functionality that the search results are visitable and ensure that search results are clickable and lead to the corresponding bill pages, confirming that navigation works as expected.

* Function to click a random number of times on the "next page" button.
* @param page - The Playwright page object.
*/
const clickNextPageRandomTimes = async (page: Page) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned above, but I feel like we should avoid randomness in tests where possible - if we can navigate forward once and back once, I'd be satisified - we get diminishing returns vis-a-vis verifying correctness with each additional page we check.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we should aim for consistent and reliable tests. I will avoid randomness and focusing on a single navigation forward and back.

await performSearch(page, searchTerm)

const noResultsMessage = await page.textContent(
".NoResults__Container-sc-bf043801-0 .text-center"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOC, are these the locators that Playwright is generating via VS Code / whatever IDE integration you're using (e.g. https://playwright.dev/docs/getting-started-vscode#picking-a-locator)? More curious for the implications for other tests going forward than for this PR, but still curious. We should feel free to add data-testids where necessary if it supports the testing effort.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I retrieved the locators by inspecting the HTML code using Google Chrome's Developer Tools. The locators are based on the frontend HTML and CSS classes that are rendered by the browser. These can be generated by frontend frameworks like React and styled with CSS or CSS-in-JS libraries.

It will definitely make the test more reliable by adding data-testid attributes to key elements in our application to create more stable locators. This will help us ensure that our tests remain robust against UI changes.


// Array of sorting test configurations
const sortingTests: SortingTest[] = [
// {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: If we're intentionally leaving this out (which I believe we are IIRC), we should leave a comment that this is intentionally excluded.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment to the code!

* @returns The extracted value as a number.
*/
const getAttributeValue = async (
item: ElementHandle<Element>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like Playwright wants us to move away from ElementHandles to using locators (https://playwright.dev/docs/api/class-elementhandle#element-handle-query-selector-all) - could we update this logic to handle that change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't aware that Playwright is moving away from ElementHandles in favor of using locators. It's good to know about this update!

I agree that we should switch our logic to use locators as recommended. I'll start updating the relevant parts of our codebase to handle this change.

* @param filterCategory - The selector of the filter category.
* @returns The selector for a randomly chosen filter item.
*/
const getRandomFilterItemSelector = async (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid randomness here as well for consistency - I'd just pick an arbitrary filter option for each list and use that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OKAY

@seatuna
Copy link
Contributor

seatuna commented Jun 21, 2024

This is a great start! Some general comments I have:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants