Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 6 additions & 8 deletions efile_app/tests/document-classification-sentinel.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const {
const path = require('path');
const {
getTestConfig,
loginViaLoginPage
loginViaLoginPage,
continueFromUpload
} = require('./test-utils');

const SENTINEL_PDF = path.resolve(__dirname, '../../benchmarking/synthetic/filled_pdfs/flattened/IL-02.pdf');
Expand All @@ -32,12 +33,7 @@ test('real uploaded PDF is extracted and classified against live Tyler choices',
await expect(page.locator('.document-row')).toHaveCount(1, {
timeout: 120000
});
await Promise.all([
page.waitForURL(/\/extraction-review\//, {
timeout: 300000
}),
page.locator('#continue-to-analysis').click(),
]);
await continueFromUpload(page);

await expect(page.getByRole('heading', {
name: /Check what we read from your document/i
Expand All @@ -57,7 +53,9 @@ test('real uploaded PDF is extracted and classified against live Tyler choices',
await expect(page.locator('#case_type_name')).toHaveValue('Dissolution (with children)', {
timeout: 120000
});
await expect(page.locator('#filing_type_name')).toHaveValue('Petition Dissolution of Marriage/Civil Union', {
// Lake County names this filing type "Petition" (checked against the live
// code list on 2026-10-08); it was "Petition Dissolution of Marriage/Civil Union".
await expect(page.locator('#filing_type_name')).toHaveValue('Petition', {
timeout: 120000
});
await expect(page.locator('input[type="radio"][name="existing_case"][value="new"]')).toBeChecked();
Expand Down
37 changes: 10 additions & 27 deletions efile_app/tests/reorganized-filing-matrix.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const path = require('path');
const {
getTestConfig,
loginViaLoginPage,
continueFromUpload,
fillRequiredInputs,
choosePayByAccount,
continueFromExtractionReview,
continueFromDocumentChecklist,
chooseFilingPath,
Expand Down Expand Up @@ -155,21 +158,6 @@ async function selectAfterLoad(page, selector, value) {
await page.locator(selector).selectOption(value);
}

async function fillRequiredInputs(page, values) {
for (const [name, value] of Object.entries(values)) {
const field = page.locator(`[name="${name}"]:visible`).first();
if (await field.count()) await field.fill(value);
}
const required = page.locator('input[required]:visible');
for (let index = 0; index < await required.count(); index += 1) {
const input = required.nth(index);
if (await input.inputValue()) continue;
const type = await input.getAttribute('type');
if (type === 'radio' || type === 'checkbox') continue;
await input.fill(type === 'email' ? 'efile-test@example.com' : type === 'number' ? '1' : 'Test value');
}
}

async function completeParty(page, ordinal) {
const roleRadios = page.locator('input[name="party_type"]');
await expect(roleRadios).not.toHaveCount(0, {
Expand Down Expand Up @@ -319,11 +307,12 @@ async function finishFiling(page, scenario, ordinal) {
await expect(page.locator('#waiver-upload-confirmation')).toBeVisible({
timeout: 120000
});
// A copy added on Fees is checked here before Review is offered.
await page.getByRole('button', {
name: 'This copy looks right'
}).click();
} else {
await page.locator('input[name="paymentIntent"][value="pay"]').check();
await expect(page.locator('input[name="paymentMethod"]')).not.toHaveCount(0, {
timeout: 120000
});
await choosePayByAccount(page);
}
const quoteOutcome = await Promise.race([
page.locator('#errorMessage:not([hidden])').waitFor({
Expand Down Expand Up @@ -389,10 +378,7 @@ async function runNewCase(page, scenario, ordinal) {
await expect(page.locator('.document-row')).toHaveCount(1, {
timeout: 180000
});
await Promise.all([
page.waitForURL(/\/extraction-review\//),
page.locator('#continue-to-analysis').click(),
]);
await continueFromUpload(page);

console.log(`${scenario.label}: selecting case codes`);
await selectGuidedCourt(page, 'illinois', scenario.court);
Expand All @@ -415,10 +401,7 @@ async function runExistingCase(page, scenario, ordinal) {
await expect(page.locator('.document-row')).toHaveCount(1, {
timeout: 180000
});
await Promise.all([
page.waitForURL(/\/extraction-review\//),
page.locator('#continue-to-analysis').click(),
]);
await continueFromUpload(page);

await selectGuidedCourt(page, 'illinois', scenario.court);
await chooseFilingPath(page, 'existing');
Expand Down
42 changes: 12 additions & 30 deletions efile_app/tests/test-optional-services-e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const path = require('path');
const {
getTestConfig,
loginViaLoginPage,
continueFromUpload,
fillRequiredInputs,
choosePayByAccount,
continueFromExtractionReview,
continueFromDocumentChecklist,
chooseFilingPath,
Expand All @@ -21,23 +24,6 @@ async function selectAfterLoad(page, selector, value) {
await page.locator(selector).selectOption(value);
}

async function fillRequiredInputs(page, values) {
for (const [name, value] of Object.entries(values)) {
const field = page.locator(`[name="${name}"]:visible`).first();
if (await field.count()) await field.fill(value);
}
const required = page.locator('input[required]:visible');
for (let index = 0; index < (await required.count()); index += 1) {
const input = required.nth(index);
if (await input.inputValue()) continue;
const type = await input.getAttribute('type');
if (type === 'radio' || type === 'checkbox') continue;
await input.fill(
type === 'email' ? 'efile-test@example.com' : type === 'number' ? '1' : 'Test value'
);
}
}

async function completeParty(page, ordinal) {
const roleRadios = page.locator('input[name="party_type"]');
await expect(roleRadios).not.toHaveCount(0, {
Expand Down Expand Up @@ -95,12 +81,7 @@ test('adding and removing optional services dynamically updates calculated fees
await expect(page.locator('.document-row')).toHaveCount(1, {
timeout: 120000
});
await Promise.all([
page.waitForURL(/\/extraction-review\//, {
timeout: 300000
}),
page.locator('#continue-to-analysis').click(),
]);
await continueFromUpload(page);

console.log('Step 4: Selecting case codes (Adams County - Small Claims)...');
await selectGuidedCourt(page, 'illinois', 'adams');
Expand Down Expand Up @@ -231,8 +212,9 @@ test('adding and removing optional services dynamically updates calculated fees
console.log('Step 9: Verifying fee calculation on Payment page WITH optional service...');
await expect(page).toHaveURL(/\/payment\//);

// Select the active BankAccount payment option
const bankAccount = page.locator('input[name="paymentMethod"][data-type="BankAccount"], input[name="paymentMethod"][value="d44fd7ed-6683-48e1-a670-f7964e5bba4d"]');
// Pay from the active BankAccount payment option
await choosePayByAccount(page);
const bankAccount = page.locator('input[name="paymentMethod"][data-type="BankAccount"]');
await expect(bankAccount.first()).toBeAttached({
timeout: 120000
});
Expand All @@ -259,13 +241,13 @@ test('adding and removing optional services dynamically updates calculated fees
page.locator('#submitButton').click(),
]);

await expect(page.locator('.review-fee-total')).toBeVisible({
await expect(page.locator('#fee-quote-amount')).toContainText('262.25', {
timeout: 120000
});
const reviewTotalWithService = await page.locator('.review-fee-total').innerText();
const reviewTotalWithService = await page.locator('#fee-quote-amount').innerText();
console.log('Review Total WITH Optional Service:\n' + reviewTotalWithService);
expect(reviewTotalWithService).toContain('262.25');
await expect(page.locator('.review-fee-breakdown')).toContainText('Optional Service Fee');
await expect(page.locator('#fee-quote-breakdown')).toContainText('Optional Service Fee');
console.log('✓ Verified: Review page shows $262.25 total and Optional Service Fee breakdown item.');

// Step 11: Edit documents from Review page to UNCHECK the optional service
Expand Down Expand Up @@ -299,10 +281,10 @@ test('adding and removing optional services dynamically updates calculated fees
// offers Submit only once the new total is on screen. No trip to Payment.
console.log('Step 12: Waiting for Review to recalculate the fee WITHOUT optional service...');
await expect(page.locator('#fee-quote')).not.toContainText('262.25');
await expect(page.locator('.review-fee-total')).toContainText('256.25', {
await expect(page.locator('#fee-quote-amount')).toContainText('256.25', {
timeout: 180000
});
await expect(page.locator('.review-fee-breakdown')).not.toContainText('Optional Service Fee');
await expect(page.locator('#fee-quote-breakdown')).not.toContainText('Optional Service Fee');
await expect(page.locator('#fee-quote')).toHaveAttribute('data-state', 'current');
await page.locator('#confirm-filing').check();
await expect(page.locator('#submitButton')).toBeEnabled();
Expand Down
73 changes: 73 additions & 0 deletions efile_app/tests/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,76 @@ async function loginViaLoginPage(page, config = getTestConfig()) {
await waitForLogin(page);
}

/**
* Leave Upload once the first file has been read, through Preview to Confirm
* case. Confirm case sends a filer back while analysis is still running, so
* wait for the analysis first, then confirm the prepared PDFs on Preview.
*/
async function continueFromUpload(page, timeout = 300000) {
await expect(page.locator('.status-pill--analyzing')).toHaveCount(0, {
timeout
});
const next = page.locator('#continue-to-analysis');
await expect(next).toHaveAttribute('href', /.+/, {
timeout
});
await Promise.all([
page.waitForURL(/\/(preview-documents|extraction-review)\//, {
timeout: 120000
}),
next.click(),
]);
if (/\/preview-documents\//.test(page.url())) {
await Promise.all([
page.waitForURL(/\/extraction-review\//, {
timeout: 120000
}),
page.getByRole('button', {
name: 'Continue',
exact: true
}).click(),
]);
}
}

/**
* Fill the named fields, then any other visible required text field. A field
* may be a select (State is), which takes an option value instead of text.
*/
async function fillRequiredInputs(page, values) {
for (const [name, value] of Object.entries(values)) {
const field = page.locator(`[name="${name}"]:visible`).first();
if (!(await field.count())) continue;
if ((await field.evaluate((element) => element.tagName)) === 'SELECT') await field.selectOption(value);
else await field.fill(value);
}
await fillOtherRequiredInputs(page);
}

async function fillOtherRequiredInputs(page) {
const required = page.locator('input[required]:visible');
for (let index = 0; index < (await required.count()); index += 1) {
const input = required.nth(index);
const type = await input.getAttribute('type');
if (type === 'radio' || type === 'checkbox' || (await input.inputValue())) continue;
await input.fill(type === 'email' ? 'efile-test@example.com' : type === 'number' ? '1' : 'Test value');
}
}

/**
* Pay from a saved account on Fees. The intent radios are only wired up once
* the page has loaded the filer's accounts, so wait for that before choosing.
*/
async function choosePayByAccount(page) {
await page.waitForFunction('typeof PaymentPage !== "undefined" && Array.isArray(PaymentPage.accounts)', null, {
timeout: 120000
});
await page.locator('input[name="paymentIntent"][value="pay"]').check();
await expect(page.locator('input[name="paymentMethod"]')).not.toHaveCount(0, {
timeout: 120000
});
}

/**
* Submit the extraction review after answering its conditional questions.
* The filer-side question only appears for case types whose checklist differs
Expand Down Expand Up @@ -234,6 +304,9 @@ module.exports = {
loginUser,
loginViaLogout,
loginViaLoginPage,
continueFromUpload,
fillRequiredInputs,
choosePayByAccount,
continueFromExtractionReview,
continueFromDocumentChecklist,
chooseFilingPath,
Expand Down
Loading