-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
## Summary Fixes #1757 ## Changes proposed - Add e2e tests for search page - Add e2e test (with FE and API) to its own CI job (`ci-frontend-ci.yml`) - invokes shell script to wait until API is loaded
- Loading branch information
Showing
12 changed files
with
421 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
name: Frontend E2E Tests | ||
|
||
on: | ||
workflow_call: | ||
pull_request: | ||
paths: | ||
- frontend/** | ||
- .github/workflows/ci-frontend-e2e.yml | ||
|
||
defaults: | ||
run: | ||
working-directory: ./frontend | ||
|
||
env: | ||
NODE_VERSION: 18 | ||
LOCKFILE_PATH: ./frontend/package-lock.json | ||
PACKAGE_MANAGER: npm | ||
|
||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }} | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
e2e-tests: | ||
name: Run E2E Tests | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Setup Node.js | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ env.NODE_VERSION }} | ||
cache: ${{ env.PACKAGE_MANAGER }} | ||
cache-dependency-path: ${{ env.LOCKFILE_PATH }} | ||
|
||
- run: npm ci | ||
|
||
- name: Install Playwright Browsers | ||
run: npx playwright install --with-deps | ||
|
||
- name: Start API Server for e2e tests | ||
run: | | ||
cd ../api | ||
make init db-seed-local start & | ||
cd ../frontend | ||
# Ensure the API wait script is executable | ||
chmod +x ../api/bin/wait-for-api.sh | ||
../api/bin/wait-for-api.sh | ||
shell: bash | ||
|
||
- name: Run E2E Tests | ||
run: npm run test:e2e | ||
|
||
- uses: actions/upload-artifact@v3 | ||
if: always() | ||
with: | ||
name: playwright-report | ||
path: ./frontend/playwright-report/ | ||
retention-days: 30 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,3 +26,6 @@ coverage.* | |
# VSCode Workspace | ||
*.code-workspace | ||
.vscode | ||
|
||
#e2e | ||
/test-results/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#!/bin/bash | ||
# wait-for-api.sh | ||
|
||
set -e | ||
|
||
# Color formatting for readability | ||
GREEN='\033[0;32m' | ||
RED='\033[0;31m' | ||
NO_COLOR='\033[0m' | ||
|
||
MAX_WAIT_TIME=800 # seconds, adjust as necessary | ||
WAIT_TIME=0 | ||
|
||
echo "Waiting for API server to become ready..." | ||
|
||
# Use curl to check the API server health endpoint | ||
until curl --output /dev/null --silent --head --fail http://localhost:8080/health; | ||
do | ||
printf '.' | ||
sleep 5 | ||
|
||
WAIT_TIME=$(($WAIT_TIME + 5)) | ||
if [ $WAIT_TIME -gt $MAX_WAIT_TIME ] | ||
then | ||
echo -e "${RED}ERROR: API server did not become ready within ${MAX_WAIT_TIME} seconds.${NO_COLOR}" | ||
exit 1 | ||
fi | ||
done | ||
|
||
echo -e "${GREEN}API server is ready after ~${WAIT_TIME} seconds.${NO_COLOR}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import { | ||
clickAccordionWithTitle, | ||
clickMobileNavMenu, | ||
clickSearchNavLink, | ||
expectCheckboxIDIsChecked, | ||
expectSortBy, | ||
expectURLContainsQueryParam, | ||
fillSearchInputAndSubmit, | ||
getMobileMenuButton, | ||
getSearchInput, | ||
hasMobileMenu, | ||
refreshPageWithCurrentURL, | ||
selectSortBy, | ||
toggleCheckboxes, | ||
waitForSearchResultsLoaded, | ||
} from "./searchUtil"; | ||
import { expect, test } from "@playwright/test"; | ||
|
||
test("should navigate from index to search page", async ({ page }) => { | ||
// Start from the index page with feature flag set | ||
await page.goto("/?_ff=showSearchV0:true"); | ||
|
||
// Mobile chrome must first click the menu button | ||
if (await hasMobileMenu(page)) { | ||
const menuButton = getMobileMenuButton(page); | ||
await clickMobileNavMenu(menuButton); | ||
} | ||
|
||
await clickSearchNavLink(page); | ||
|
||
// Verify that the new URL is correct | ||
expectURLContainsQueryParam(page, "status", "forecasted,posted"); | ||
|
||
// Verify the presence of "Search" content on the page | ||
await expect(page.locator("h1")).toContainText( | ||
"Search funding opportunities", | ||
); | ||
|
||
// Verify that the 'forecasted' and 'posted' are checked | ||
await expectCheckboxIDIsChecked(page, "#status-forecasted"); | ||
await expectCheckboxIDIsChecked(page, "#status-posted"); | ||
}); | ||
|
||
test.describe("Search page tests", () => { | ||
test.beforeEach(async ({ page }) => { | ||
// Navigate to the search page with the feature flag set | ||
await page.goto("/search?_ff=showSearchV0:true"); | ||
}); | ||
|
||
test("should return 0 results when searching for obscure term", async ({ | ||
page, | ||
browserName, | ||
}) => { | ||
// TODO (Issue #2005): fix test for webkit | ||
test.skip( | ||
browserName === "webkit", | ||
"Skipping test for WebKit due to a query param issue.", | ||
); | ||
|
||
const searchTerm = "0resultearch"; | ||
|
||
await fillSearchInputAndSubmit(searchTerm, page); | ||
|
||
expectURLContainsQueryParam(page, "query", searchTerm); | ||
|
||
const resultsHeading = page.getByRole("heading", { | ||
name: /0 Opportunities/i, | ||
}); | ||
await expect(resultsHeading).toBeVisible(); | ||
|
||
await expect(page.locator("div.usa-prose h2")).toHaveText( | ||
"Your search did not return any results.", | ||
); | ||
}); | ||
|
||
test("should show and hide loading state", async ({ page, browserName }) => { | ||
// TODO (Issue #2005): fix test for webkit | ||
test.skip( | ||
browserName === "webkit", | ||
"Skipping test for WebKit due to a query param issue.", | ||
); | ||
const searchTerm = "advanced"; | ||
await fillSearchInputAndSubmit(searchTerm, page); | ||
|
||
const loadingIndicator = page.locator("text='Loading results...'"); | ||
await expect(loadingIndicator).toBeVisible(); | ||
await expect(loadingIndicator).toBeHidden(); | ||
|
||
const searchTerm2 = "agency"; | ||
await fillSearchInputAndSubmit(searchTerm2, page); | ||
await expect(loadingIndicator).toBeVisible(); | ||
await expect(loadingIndicator).toBeHidden(); | ||
}); | ||
test("should retain filters in a new tab", async ({ page }) => { | ||
// Set all inputs, then refresh the page. Those same inputs should be | ||
// set from query params. | ||
const searchTerm = "education"; | ||
const statusCheckboxes = { | ||
"status-forecasted": "forecasted", | ||
"status-posted": "posted", | ||
}; | ||
const fundingInstrumentCheckboxes = { | ||
"funding-instrument-cooperative_agreement": "cooperative_agreement", | ||
"funding-instrument-grant": "grant", | ||
}; | ||
|
||
const eligibilityCheckboxes = { | ||
"eligibility-state_governments": "state_governments", | ||
"eligibility-county_governments": "county_governments", | ||
}; | ||
const agencyCheckboxes = { | ||
ARPAH: "ARPAH", | ||
AC: "AC", | ||
}; | ||
const categoryCheckboxes = { | ||
"category-recovery_act": "recovery_act", | ||
"category-agriculture": "agriculture", | ||
}; | ||
|
||
await selectSortBy(page, "agencyDesc"); | ||
|
||
await waitForSearchResultsLoaded(page); | ||
await fillSearchInputAndSubmit(searchTerm, page); | ||
await toggleCheckboxes(page, statusCheckboxes, "status"); | ||
|
||
await clickAccordionWithTitle(page, "Funding instrument"); | ||
await toggleCheckboxes( | ||
page, | ||
fundingInstrumentCheckboxes, | ||
"fundingInstrument", | ||
); | ||
|
||
await clickAccordionWithTitle(page, "Eligibility"); | ||
await toggleCheckboxes(page, eligibilityCheckboxes, "eligibility"); | ||
|
||
await clickAccordionWithTitle(page, "Agency"); | ||
await toggleCheckboxes(page, agencyCheckboxes, "agency"); | ||
|
||
await clickAccordionWithTitle(page, "Category"); | ||
await toggleCheckboxes(page, categoryCheckboxes, "category"); | ||
|
||
/***********************************************************/ | ||
/* Page refreshed should have all the same inputs selected | ||
/***********************************************************/ | ||
|
||
await refreshPageWithCurrentURL(page); | ||
|
||
// Expect search inputs are retained in the new tab | ||
await expectSortBy(page, "agencyDesc"); | ||
const searchInput = getSearchInput(page); | ||
await expect(searchInput).toHaveValue(searchTerm); | ||
|
||
for (const [checkboxID] of Object.entries(statusCheckboxes)) { | ||
await expectCheckboxIDIsChecked(page, `#${checkboxID}`); | ||
} | ||
|
||
for (const [checkboxID] of Object.entries(fundingInstrumentCheckboxes)) { | ||
await expectCheckboxIDIsChecked(page, `#${checkboxID}`); | ||
} | ||
for (const [checkboxID] of Object.entries(eligibilityCheckboxes)) { | ||
await expectCheckboxIDIsChecked(page, `#${checkboxID}`); | ||
} | ||
for (const [checkboxID] of Object.entries(agencyCheckboxes)) { | ||
await expectCheckboxIDIsChecked(page, `#${checkboxID}`); | ||
} | ||
for (const [checkboxID] of Object.entries(categoryCheckboxes)) { | ||
await expectCheckboxIDIsChecked(page, `#${checkboxID}`); | ||
} | ||
}); | ||
}); |
Oops, something went wrong.
85f3e0a
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.
Coverage report for
./frontend
Test suite run success
164 tests passing in 56 suites.
Report generated by 🧪jest coverage report action from 85f3e0a