Skip to content
Merged
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
60 changes: 58 additions & 2 deletions e2e/workflow.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,67 @@ test.describe.serial("Workflow Sample - Self-Service UI walk-through", () => {
for (const [name, value] of Object.entries(fields)) {
const input = page.locator(`#processContent [name="${name}"]`).first();
await input.waitFor({ state: "visible", timeout: 30000 });
await input.fill(value);
// ValidatorsManager (forgerock-ui-commons) binds its policy
// re-evaluation handlers via jQuery to keyup/change/blur. A plain
// Playwright fill() sets `value` programmatically and never fires
// keyup, so async server-side validators (`unique`,
// `no-internal-user-conflict` on userName, `valid-email-address-format`
// on mail) never get a chance to flip data-validation-status from
// its post-render "error" state to "ok" -- which keeps
// validatorsManager.formValidated($form) returning false inside
// StartProcessView.formSubmit and the workflow start request is
// never POSTed. pressSequentially emits real key events so the
// jQuery-bound validators fire on every character.
await input.click();
await input.fill("");
await input.pressSequentially(value, { delay: 5 });
// Trigger blur via jQuery so it reaches handlers registered with
// $(el).on('blur', ...) (native el.dispatchEvent('blur') doesn't
// bubble and isn't observed by jQuery's delegated listeners on
// some forgerock-ui widget bindings).
await input.evaluate((el) => {
if (window.jQuery) {
window.jQuery(el).trigger("change").trigger("blur");
} else {
el.dispatchEvent(new Event("change", { bubbles: true }));
el.dispatchEvent(new Event("blur", { bubbles: true }));
}
});
}

await page.locator('input[name="startProcessButton"]').first().click();
// Re-run the form-wide validator pass. validateAllFields walks every
// [data-validator] input and re-fires its policy chain, which is
// necessary to (re)issue the /policy/validateObject POST with the
// now-populated form state -- without this the userName "unique"
// check stays stuck in its initial "REQUIRED" failure.
await page.evaluate(() => new Promise((resolve) => {
if (typeof window.require !== "function" || !window.jQuery) {
resolve();
return;
}
window.require(
["org/forgerock/commons/ui/common/main/ValidatorsManager"],
(vm) => {
try {
vm.validateAllFields(window.jQuery("#contractorOnBoardForm"));
} catch (e) { /* fall through to timeout-based wait */ }
setTimeout(resolve, 3000);
},
() => resolve()
);
}));

// With every field validated successfully, ValidatorsManager removes
// the cosmetic `disabled` attribute. Wait for it; this is now a real
// signal that StartProcessView.formSubmit will accept the click.
const startBtn = page.locator('input[name="startProcessButton"]').first();
await expect(startBtn).toBeEnabled({ timeout: 60000 });
await startBtn.click();
await page.waitForLoadState("networkidle");

// On successful start, StartProcessView empties #processDetails (see
// hideDetails / refreshTasksMenu flow) so the Start button is gone.
await expect(startBtn).toHaveCount(0, { timeout: 30000 });
await assertNoErrors(page);
});

Expand Down
Loading