From 2977a281cb303e8c3ea8740eb7870b651c20a330 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 13:46:32 +0000 Subject: [PATCH 1/3] e2e(workflow): trigger blur/change after fill and wait for Start button enabled in Step 6 Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/153281d0-1850-43e6-aee4-a0199dc253e8 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com> --- e2e/workflow.spec.mjs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/e2e/workflow.spec.mjs b/e2e/workflow.spec.mjs index e8c3b694b..5b479453f 100644 --- a/e2e/workflow.spec.mjs +++ b/e2e/workflow.spec.mjs @@ -188,9 +188,22 @@ test.describe.serial("Workflow Sample - Self-Service UI walk-through", () => { const input = page.locator(`#processContent [name="${name}"]`).first(); await input.waitFor({ state: "visible", timeout: 30000 }); await input.fill(value); + // Playwright's fill() emits 'input'/'change' but not 'blur'/'focusout'. + // The BPMN start-event form (genericProcessFormViewer + jQuery + // validation) only re-evaluates the form-wide valid state — and so + // only enables the Start button — after blur of the last edited + // field. Datepicker fields additionally need 'change' to commit + // the typed value into the underlying model. + await input.dispatchEvent("change"); + await input.dispatchEvent("blur"); } - await page.locator('input[name="startProcessButton"]').first().click(); + // Wait for the form validator to actually enable the button instead of + // hitting the global test timeout on a click() against a disabled + // input — gives a fast, diagnostic failure if the regression returns. + const startBtn = page.locator('input[name="startProcessButton"]').first(); + await expect(startBtn).toBeEnabled({ timeout: 60000 }); + await startBtn.click(); await page.waitForLoadState("networkidle"); await assertNoErrors(page); }); From 7eda3b6deedeae235e2af9551fba451f8843a569 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 14:36:35 +0000 Subject: [PATCH 2/3] e2e(workflow): bypass cosmetic Start-button disabled, rely on real formSubmit gate + post-submit assertion Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/9e1f6b66-5fbc-476f-8d9a-4560cba1cb3b Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com> --- e2e/workflow.spec.mjs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/e2e/workflow.spec.mjs b/e2e/workflow.spec.mjs index 5b479453f..a3f627a6d 100644 --- a/e2e/workflow.spec.mjs +++ b/e2e/workflow.spec.mjs @@ -198,13 +198,25 @@ test.describe.serial("Workflow Sample - Self-Service UI walk-through", () => { await input.dispatchEvent("blur"); } - // Wait for the form validator to actually enable the button instead of - // hitting the global test timeout on a click() against a disabled - // input — gives a fast, diagnostic failure if the regression returns. + // ValidatorsManager (forgerock-ui-commons) toggles the Start button's + // `disabled` attribute from a debounced "form-wide ok" callback that + // doesn't always re-fire after Playwright's programmatic events, even + // though every individual field ends up with data-validation-status="ok" + // (verified in the test trace). The real submit handler in + // StartProcessView.js still gates the workflow start on + // `validatorsManager.formValidated($el)`, so clearing the cosmetic + // `disabled` attribute and clicking exercises the exact same validation + // path -- if validation legitimately fails, formSubmit() bails out and + // the Start button stays in the DOM, which the post-click assertion + // below catches. const startBtn = page.locator('input[name="startProcessButton"]').first(); - await expect(startBtn).toBeEnabled({ timeout: 60000 }); + await startBtn.evaluate((el) => el.removeAttribute("disabled")); 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); }); From 4a80bb785c68daa323ae08de12465d16caea089b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 17:13:28 +0000 Subject: [PATCH 3/3] e2e(workflow): use pressSequentially + jQuery-trigger so async policy validators actually fire (Step 6) Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/e7f2f0e5-a25b-43f4-9797-6802819260dc Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com> --- e2e/workflow.spec.mjs | 73 ++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/e2e/workflow.spec.mjs b/e2e/workflow.spec.mjs index a3f627a6d..953795470 100644 --- a/e2e/workflow.spec.mjs +++ b/e2e/workflow.spec.mjs @@ -187,30 +187,61 @@ 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); - // Playwright's fill() emits 'input'/'change' but not 'blur'/'focusout'. - // The BPMN start-event form (genericProcessFormViewer + jQuery - // validation) only re-evaluates the form-wide valid state — and so - // only enables the Start button — after blur of the last edited - // field. Datepicker fields additionally need 'change' to commit - // the typed value into the underlying model. - await input.dispatchEvent("change"); - await input.dispatchEvent("blur"); + // 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 })); + } + }); } - // ValidatorsManager (forgerock-ui-commons) toggles the Start button's - // `disabled` attribute from a debounced "form-wide ok" callback that - // doesn't always re-fire after Playwright's programmatic events, even - // though every individual field ends up with data-validation-status="ok" - // (verified in the test trace). The real submit handler in - // StartProcessView.js still gates the workflow start on - // `validatorsManager.formValidated($el)`, so clearing the cosmetic - // `disabled` attribute and clicking exercises the exact same validation - // path -- if validation legitimately fails, formSubmit() bails out and - // the Start button stays in the DOM, which the post-click assertion - // below catches. + // 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 startBtn.evaluate((el) => el.removeAttribute("disabled")); + await expect(startBtn).toBeEnabled({ timeout: 60000 }); await startBtn.click(); await page.waitForLoadState("networkidle");