Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ea7334d
Split staging E2E workflow: sharded no-auth, single-worker auth; upda…
Bhavna-Ramachandran Mar 10, 2026
30e8f2a
E2E: tag-based auth split, robust form nav, artifact/report improvements
Bhavna-Ramachandran Mar 11, 2026
e77dd1a
Fix lint errors and update overall auth E2E workflow
Bhavna-Ramachandran Mar 11, 2026
d6c1217
E2E: tag-based auth split, robust form nav, artifact/report improvements
Bhavna-Ramachandran Mar 11, 2026
60bb632
Fix lint errors and update overall auth E2E workflow
Bhavna-Ramachandran Mar 11, 2026
69bd321
Updated with already logged in guard and resolved lint errors
Bhavna-Ramachandran Mar 11, 2026
b64b674
Add @auth tag to the new SFLLL form test
Bhavna-Ramachandran Mar 11, 2026
bc63d76
Resolve flakiness: remove redundant networkidle wait in refreshPageWi…
Bhavna-Ramachandran Mar 11, 2026
7483cc8
Add waiting for stable app state
Bhavna-Ramachandran Mar 12, 2026
2a18616
Fix: stabilize login redirect and org dropdown loading on staging
Bhavna-Ramachandran Mar 12, 2026
42857c2
Resolve lint errors
Bhavna-Ramachandran Mar 12, 2026
dce8a22
Restore workflow files to main branch versions
Bhavna-Ramachandran Mar 13, 2026
35faa3c
Merge branch 'main' into BR-8942-E2E-Staging-Run
Bhavna-Ramachandran Mar 13, 2026
ee4972c
Address review comments and made updates post discussion
Bhavna-Ramachandran Mar 13, 2026
38be1d0
Address review comments and clean up annotations
Bhavna-Ramachandran Mar 13, 2026
6e1ee99
Update skip auth tests on non chrome browsers
Bhavna-Ramachandran Mar 16, 2026
31b90c0
fix: resolve lint errors in e2e tests and utils
Bhavna-Ramachandran Mar 16, 2026
1440a6f
Update test.beforeEach to avoid empty object pattern lint error
Bhavna-Ramachandran Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/actions/e2e/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ inputs:
node_options:
description: "options to pass to node"
default: "--dns-result-order=ipv4first"
workers:
description: "number of workers to use for test run"
default: ""
outputs:
artifact-id:
description: "artifact ids for uploaded test reports"
Expand Down Expand Up @@ -87,14 +90,14 @@ runs:
TOTAL_SHARDS: ${{ inputs.total_shards }}
CURRENT_SHARD: ${{ inputs.current_shard }}
PLAYWRIGHT_TARGET_ENV: ${{ inputs.target }}
PLAYWRIGHT_WORKERS: ${{ inputs.workers }}
STAGING_TEST_USER_EMAIL: ${{ inputs.staging_test_user_email }}
STAGING_TEST_USER_PASSWORD: ${{ inputs.staging_test_user_password }}
STAGING_TEST_USER_MFA_KEY: ${{ inputs.staging_test_user_mfa_key }}
run: |
npm run test:e2e
shell: bash


- name: Run e2e tests for ${{ inputs.playwright_tags }} (Shard ${{ inputs.current_shard }}/${{ inputs.total_shards }})
if: ${{ inputs.playwright_tags != '' }}
working-directory: ./frontend
Expand All @@ -103,6 +106,7 @@ runs:
TOTAL_SHARDS: ${{ inputs.total_shards }}
CURRENT_SHARD: ${{ inputs.current_shard }}
PLAYWRIGHT_TARGET_ENV: ${{ inputs.target }}
PLAYWRIGHT_WORKERS: ${{ inputs.workers }}
STAGING_TEST_USER_EMAIL: ${{ inputs.staging_test_user_email }}
STAGING_TEST_USER_PASSWORD: ${{ inputs.staging_test_user_password }}
STAGING_TEST_USER_MFA_KEY: ${{ inputs.staging_test_user_mfa_key }}
Expand All @@ -111,7 +115,7 @@ runs:
shell: bash

- name: Debug logging on failure
if: ${{ failure() && inputs.api_logs == 'true' }}
if: ${{ failure() && inputs.api_logs == 'true' }}
working-directory: ./frontend
run: |
cd ../api
Expand Down
10 changes: 0 additions & 10 deletions .github/workflows/e2e-create-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,31 @@ on:
artifact-ids:
description: "comma separated string of test report artifact ids to download"
type: string

env:
NODE_VERSION: 24
LOCKFILE_PATH: ./frontend/package-lock.json
PACKAGE_MANAGER: npm
NODE_OPTIONS: --dns-result-order=ipv4first

defaults:
run:
working-directory: ./frontend

jobs:
e2e-create-report:
continue-on-error: true
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: ${{ env.PACKAGE_MANAGER }}
cache-dependency-path: ${{ env.LOCKFILE_PATH }}

- name: Install Playwright Dependencies
run: |
npm ci
npx playwright install --with-deps

- name: Download All Blob Reports
uses: actions/download-artifact@v7
with:
Expand All @@ -47,20 +41,16 @@ jobs:
run-id: ${{ inputs.run_id }}
pattern: blob-report-shard-*
merge-multiple: true

- name: Verify Downloaded Artifacts
run: |
echo "Contents of all-blob-reports after download:"
ls -R all-blob-reports*

- name: Merge Blob Reports into HTML
run: npx playwright merge-reports --reporter html ./all-blob-reports

- name: Verify Downloaded Artifacts
run: |
echo "Contents of all-blob-reports after download:"
ls -R playwright*

- name: Upload Merged HTML Report
uses: actions/upload-artifact@v6
with:
Expand Down
17 changes: 3 additions & 14 deletions .github/workflows/e2e-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,29 @@ on:
description: "pipe separated list of @tags denoting groups of tests to run"
required: false
type: string

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
e2e-tests-deployed:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
total_shards: [4]

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set target
run: if [[ -z "${{ inputs.target }}" ]]; then echo "defaulted_target=staging" >> "$GITHUB_ENV"; else echo "defaulted_target=${{ inputs.target }}" >> "$GITHUB_ENV"; fi

- name: Run E2E tests
uses: ./.github/actions/e2e
with:
version: ${{ inputs.version || github.ref }}
target: ${{ env.defaulted_target }}
total_shards: ${{ matrix.total_shards }}
current_shard: ${{ matrix.shard }}
total_shards: 1
current_shard: 1
workers: 1
playwright_tags: ${{ inputs.playwright_tags }}
staging_test_user_email: ${{ secrets.STAGING_TEST_USER_EMAIL }}
staging_test_user_password: ${{ secrets.STAGING_TEST_USER_PASSWORD }}
staging_test_user_mfa_key: ${{ secrets.STAGING_TEST_USER_MFA_KEY }}

create-report:
name: Create Merged Test Report
if: ${{ !cancelled() }}
Expand All @@ -61,7 +51,6 @@ jobs:
secrets: inherit
with:
run_id: ${{ github.run_id }}

send-slack-notification:
if: failure()
needs: [create-report]
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/application/ApplicationFormsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ const FormLink = ({
{formName && (
<Link
className="text-bold"
// Added stable test attributes for reliable E2E targeting of form links.
// Enables Playwright to select the correct link regardless of text or layout changes.
data-testid="application-form-link"
data-form-id={formId}
href={`/applications/${applicationId}/form/${appFormId}`}
>
{formName}
Expand Down
12 changes: 11 additions & 1 deletion frontend/tests/e2e/apply/failure-path-sf424b.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { openForm } from "tests/e2e/utils/forms/form-navigation-utils";
import { saveForm } from "tests/e2e/utils/forms/save-form-utils";
import { verifyFormStatusAfterSave } from "tests/e2e/utils/forms/verify-form-status-utils";

const { testOrgLabel } = playwrightEnv;
const { testOrgLabel, targetEnv } = playwrightEnv;
const OPPORTUNITY_ID = "f7a1c2b3-4d5e-6789-8abc-1234567890ab"; // TEST-APPLY-ORG-IND-ON01
const OPPORTUNITY_URL = `/opportunity/${OPPORTUNITY_ID}`;

Expand All @@ -24,6 +24,16 @@ const sf424bErrors = [
},
];

// Skip non-Chrome browsers in staging
test.beforeEach(({ page: _ }, testInfo) => {
if (targetEnv === "staging") {
test.skip(
testInfo.project.name !== "Chrome",
"Staging MFA login is limited to Chrome to avoid OTP rate-limiting",
);
}
});

test("SF-424B error validation - required fields and inline errors", async ({
page,
context,
Expand Down
14 changes: 12 additions & 2 deletions frontend/tests/e2e/apply/fill-sflll-form.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,22 @@ import { fillForm } from "tests/e2e/utils/forms/general-forms-filling";
import {
clearPageState,
ensurePageClosed,
} from "tests/e2e/utils/lifecycle-helpers";
} from "tests/e2e/utils/lifecycle-utils";

const { baseUrl, testOrgLabel, opportunityId } = playwrightEnv;
const { baseUrl, testOrgLabel, opportunityId, targetEnv } = playwrightEnv;
const OPPORTUNITY_URL = `/opportunity/${opportunityId}`;

test.describe("fill SF-LLL Form", () => {
// Skip non-Chrome browsers in staging
test.beforeEach(({ page: _ }, testInfo) => {
if (targetEnv === "staging") {
test.skip(
testInfo.project.name !== "Chrome",
"Staging MFA login is limited to Chrome to avoid OTP rate-limiting",
);
}
});

test.beforeEach(async ({ page, context }, testInfo) => {
const isMobile = testInfo.project.name.match(/[Mm]obile/);
await authenticateE2eUser(page, context, !!isMobile);
Expand Down
59 changes: 34 additions & 25 deletions frontend/tests/e2e/apply/happy-path-application-submission.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,20 @@ import { selectFormInclusionOption } from "tests/e2e/utils/forms/select-form-inc
import { verifyFormStatusAfterSave } from "tests/e2e/utils/forms/verify-form-status-utils";
import { submitApplicationAndVerify } from "tests/e2e/utils/submit-application-utils";

const { testOrgLabel } = playwrightEnv;
const { testOrgLabel, targetEnv } = playwrightEnv;
const OPPORTUNITY_ID = "f7a1c2b3-4d5e-6789-8abc-1234567890ab"; // TEST-APPLY-ORG-IND-ON01
const OPPORTUNITY_URL = `/opportunity/${OPPORTUNITY_ID}`;

// Skip non-Chrome browsers in staging
test.beforeEach(({ page: _ }, testInfo) => {
if (targetEnv === "staging") {
test.skip(
testInfo.project.name !== "Chrome",
"Staging MFA login is limited to Chrome to avoid OTP rate-limiting",
);
}
});

test("Application submission happy path - application with required SF424B and unsubmitted conditional SFLLL", async ({
page,
context,
Expand All @@ -35,33 +45,32 @@ test("Application submission happy path - application with required SF424B and u
await createApplication(page, OPPORTUNITY_URL, testOrgLabel);
const applicationUrl = page.url();

if (await openForm(page, SF424B_FORM_MATCHER)) {
// Fill SF-424B form fields using helper
await fillSf424bForm(page, "TESTER", testOrgLabel);
if (!(await openForm(page, SF424B_FORM_MATCHER))) {
throw new Error(
"Could not find or open SF-424B form link on the application forms page",
);
}

// Fill SF-424B form fields using helper
await fillSf424bForm(page, "TESTER", testOrgLabel);

// Save the form using helper
await saveForm(page);
// Save the form using helper
await saveForm(page);

// Verify form status after save
await verifyFormStatusAfterSave(
page,
"complete",
"SF-424B",
applicationUrl,
);
// Verify form status after save
await verifyFormStatusAfterSave(page, "complete", "SF-424B", applicationUrl);

// Extra wait for page to fully render forms table after navigation
await page.waitForTimeout(10000);
// Extra wait for page to fully render forms table after navigation
await page.waitForTimeout(10000);

// Select 'No' for including SF-LLL form in submission
await selectFormInclusionOption(
page,
"Disclosure of Lobbying Activities (SF-LLL)",
"No",
);
// Select 'No' for including SF-LLL form in submission
await selectFormInclusionOption(
page,
"Disclosure of Lobbying Activities (SF-LLL)",
"No",
);

// Submit the application and verify success
await submitApplicationAndVerify(page);
// Application ID is now available in appId variable for further use if needed
}
// Submit the application and verify success
await submitApplicationAndVerify(page);
// Application ID is now available in appId variable for further use if needed
});
Loading
Loading