Skip to content

test(davinci-client): e2e tests for agreements, single checkbox, rich text#638

Open
ancheetah wants to merge 1 commit into
mainfrom
SDKS-5000-form-fields-e2e
Open

test(davinci-client): e2e tests for agreements, single checkbox, rich text#638
ancheetah wants to merge 1 commit into
mainfrom
SDKS-5000-form-fields-e2e

Conversation

@ancheetah
Copy link
Copy Markdown
Collaborator

@ancheetah ancheetah commented May 20, 2026

JIRA Ticket

https://pingidentity.atlassian.net/browse/SDKS-5000

Description

Extends e2e coverage for agreements, rich text, and single checkbox fields; fixes SingleCheckboxField type to match the actual server payload. Moves Form Fields Test flow to new web tenant.

Key Changes

  • SingleCheckboxField type — flattened validation.errorMessage into a top-level errorMessage?: string to match the server shape. Updated collector.utils.ts and regenerated API reports.
  • booleanComponent — wired in Validator<ValidatedBooleanCollector> so the checkbox runs client-side validation and renders inline errors on change.
  • server-configs.ts — swapped client ID to a PingOne environment that includes the updated form fields flow.
  • form-fields.test.ts — added assertions for rich text link rendering, agreement visibility, single checkbox default state, and required-field error on uncheck.

Summary by CodeRabbit

  • New Features

    • Inline validation for single checkbox fields with visible error messages.
  • Bug Fixes

    • More reliable form validation and error display when toggling checkboxes.
  • Tests

    • End-to-end tests expanded to cover richer content rendering (links, headings, agreement text) and checkbox validation behavior.
  • Chores

    • Repository ignore rules updated to exclude additional local config paths.

Review Change Stack

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 20, 2026

⚠️ No Changeset found

Latest commit: 89d04ee

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

SingleCheckboxField error representation moved from nested validation.errorMessage to a top-level errorMessage; booleanComponent now accepts and runs a validator, renders/removes collector-scoped error elements on validation results, types and tests were updated, and API/type-report artifacts were regenerated.

Changes

Checkbox Validation Refactoring

Layer / File(s) Summary
SingleCheckboxField type contract and API updates
packages/davinci-client/src/lib/davinci.types.ts, packages/davinci-client/api-report/*
SingleCheckboxField error representation simplified from validation?: { errorMessage: string } to errorMessage?: string; API Extractor/type report artifacts updated (union ordering and RTK Query error unions normalized).
Boolean component validation and text validator typing
e2e/davinci-app/components/boolean.ts, e2e/davinci-app/components/text.ts
booleanComponent gains a validator parameter, derives collectorKey for scoped IDs, validates on change, calls updater(checked), logs updater errors, and conditionally inserts/removes a ${collectorKey}-error element. textComponent validator typing narrowed to Validator<ValidatedTextCollector>.
Collector validation message lookup
packages/davinci-client/src/lib/collector.utils.ts, packages/davinci-client/src/lib/davinci.types.ts
ValidatedBooleanCollector required rule now reads field.errorMessage (top-level) with fallback "Value cannot be empty".
App integration and server config
e2e/davinci-app/main.ts, e2e/davinci-app/server-configs.ts
Main app now passes davinciClient.validate(collector) into booleanComponent; server tenant config updated for the form-fields test tenant.
E2E test coverage for checkbox validation
e2e/davinci-suites/src/form-fields.test.ts
Playwright tests adjusted to new clientId and expanded to assert rich-text/link content, agreement text, single-checkbox default and validation toggling, and updated expected formData payload including single-checkbox-field.
Git configuration update
.gitignore
Added .polaris and .playwright-mcp to .gitignore.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • ForgeRock/ping-javascript-sdk#629: Provides earlier single-checkbox/ValidatedBooleanCollector implementation that this PR adjusts to use top-level errorMessage and to render validation errors in the boolean component.
  • ForgeRock/ping-javascript-sdk#413: Related collector validation work (required/validatePhoneNumber validators and validator handling) that intersects with how validator results are surfaced and consumed.

Suggested reviewers

  • cerebrl
  • ryanbas21

"🐰
A checkbox checked with careful art,
Errors now live where they truly start,
No nested maze to pull apart,
Top-level message plays its part,
Hooray — forms and tests depart!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding e2e tests for agreements, single checkbox, and rich text fields.
Description check ✅ Passed The description includes JIRA ticket, comprehensive description of changes, and key changes section covering all major modifications.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SDKS-5000-form-fields-e2e

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ancheetah ancheetah requested review from cerebrl and ryanbas21 May 20, 2026 16:17
@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud Bot commented May 20, 2026

View your CI Pipeline Execution ↗ for commit 89d04ee


☁️ Nx Cloud last updated this comment at 2026-05-20 17:27:32 UTC

Copy link
Copy Markdown
Contributor

@nx-cloud nx-cloud Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nx Cloud is proposing a fix for your failed CI:

We updated the test fixture in node.reducer.test.ts to use the new flattened errorMessage field on SingleCheckboxField, which fixes the assertion failure caused by the PR's intentional type change. The implementation in collector.utils.ts was already reading field.errorMessage directly, but the test was still supplying the old nested validation: { errorMessage: '...' } shape, causing the message to fall back to 'Value cannot be empty'.

Tip

We verified this fix by re-running @forgerock/davinci-client:test.

diff --git a/packages/davinci-client/src/lib/node.reducer.test.ts b/packages/davinci-client/src/lib/node.reducer.test.ts
index 3db2cbd..75d66e7 100644
--- a/packages/davinci-client/src/lib/node.reducer.test.ts
+++ b/packages/davinci-client/src/lib/node.reducer.test.ts
@@ -1699,7 +1699,7 @@ describe('The node collector reducer with ValidatedBooleanCollector', () => {
             key: 'accept-terms',
             label: 'Accept Terms',
             required: true,
-            validation: { errorMessage: 'You must accept the terms' },
+            errorMessage: 'You must accept the terms',
           },
         ],
         formData: {},

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally 2XKE-yVlk

Apply fix locally with your editor ↗   View interactive diff ↗



🎓 Learn more about Self-Healing CI on nx.dev

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/davinci-app/components/boolean.ts`:
- Around line 51-64: The checkbox handler currently only calls updater when
validation passes, causing collector state to lag when validation fails; always
invoke updater((event.target as HTMLInputElement).checked) (the updater call in
this block) regardless of the validation result so the collector state stays in
sync, then run the existing validation logic that creates/removes the
`${collectorKey}-error` element based on result; keep the existing error logging
path (checking 'error' in the updater return) and ensure you do not
remove/create error DOM nodes prematurely (use updater first, then handle
Array.isArray(result) and error DOM updates using result, collectorKey, formEl).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59f71200-eefc-4560-b9b1-eea5176a5604

📥 Commits

Reviewing files that changed from the base of the PR and between 9f93d5e and 128e211.

📒 Files selected for processing (10)
  • .gitignore
  • e2e/davinci-app/components/boolean.ts
  • e2e/davinci-app/components/text.ts
  • e2e/davinci-app/main.ts
  • e2e/davinci-app/server-configs.ts
  • e2e/davinci-suites/src/form-fields.test.ts
  • packages/davinci-client/api-report/davinci-client.api.md
  • packages/davinci-client/api-report/davinci-client.types.api.md
  • packages/davinci-client/src/lib/collector.utils.ts
  • packages/davinci-client/src/lib/davinci.types.ts

Comment thread e2e/davinci-app/components/boolean.ts
@ancheetah ancheetah force-pushed the SDKS-5000-form-fields-e2e branch from 128e211 to 89d04ee Compare May 20, 2026 17:23
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
e2e/davinci-suites/src/form-fields.test.ts (1)

92-94: ⚡ Quick win

Use consistent navigation pattern with asyncEvents helper.

The second test uses page.goto() with a hardcoded URL, while the first test uses the asyncEvents helper with a relative URL. Using the helper provides consistent event handling and avoids hardcoding the port/protocol/host.

♻️ Refactor to use asyncEvents helper
 test('should render form validation fields', async ({ page }) => {
-  await page.goto('http://localhost:5829/?clientId=e4ef2896-8d90-4abd-bf0f-7b8034995927');
+  const { navigate } = asyncEvents(page);
+  await navigate('/?clientId=e4ef2896-8d90-4abd-bf0f-7b8034995927');
 
   await expect(page.getByText('Select Form Fields Test Form')).toBeVisible();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/davinci-suites/src/form-fields.test.ts` around lines 92 - 94, The test
"should render form validation fields" currently calls page.goto with a
hardcoded full URL; update it to use the existing asyncEvents helper and a
relative path like the other test to keep event handling consistent and avoid
embedding host/port. Replace the
page.goto('http://localhost:5829/?clientId=...') call inside the test body with
the asyncEvents call used elsewhere (refer to the asyncEvents helper and the
test name "should render form validation fields") so the test navigates to
'/?clientId=e4ef2896-8d90-4abd-bf0f-7b8034995927' via asyncEvents instead of a
hardcoded URL.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/davinci-app/components/boolean.ts`:
- Line 25: The code constructs DOM selectors using the raw collectorKey (const
collectorKey = collector.output.key) and then passes it to
document.querySelector for id/class selectors, which breaks when keys contain
CSS-special characters (e.g., dots); update each querySelector call that uses
collectorKey (the selectors around where collectorKey is interpolated) to wrap
the key with CSS.escape(collectorKey) when building id or class selectors so the
selectors are valid for any key value.

---

Nitpick comments:
In `@e2e/davinci-suites/src/form-fields.test.ts`:
- Around line 92-94: The test "should render form validation fields" currently
calls page.goto with a hardcoded full URL; update it to use the existing
asyncEvents helper and a relative path like the other test to keep event
handling consistent and avoid embedding host/port. Replace the
page.goto('http://localhost:5829/?clientId=...') call inside the test body with
the asyncEvents call used elsewhere (refer to the asyncEvents helper and the
test name "should render form validation fields") so the test navigates to
'/?clientId=e4ef2896-8d90-4abd-bf0f-7b8034995927' via asyncEvents instead of a
hardcoded URL.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c29145f7-cabe-4a6a-b6d0-2690b2d520dc

📥 Commits

Reviewing files that changed from the base of the PR and between 128e211 and 89d04ee.

📒 Files selected for processing (11)
  • .gitignore
  • e2e/davinci-app/components/boolean.ts
  • e2e/davinci-app/components/text.ts
  • e2e/davinci-app/main.ts
  • e2e/davinci-app/server-configs.ts
  • e2e/davinci-suites/src/form-fields.test.ts
  • packages/davinci-client/api-report/davinci-client.api.md
  • packages/davinci-client/api-report/davinci-client.types.api.md
  • packages/davinci-client/src/lib/collector.utils.ts
  • packages/davinci-client/src/lib/davinci.types.ts
  • packages/davinci-client/src/lib/node.reducer.test.ts
✅ Files skipped from review due to trivial changes (1)
  • .gitignore

validator: Validator<ValidatedBooleanCollector>,
) {
// Create a container for the checkboxes
const collectorKey = collector.output.key;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

❓ Verification inconclusive

Script executed:

find . -type f -name "boolean.ts" -path "*davinci-app*"

Repository: ForgeRock/ping-javascript-sdk


Repository: ForgeRock/ping-javascript-sdk
Exit code: 0

stdout:

./e2e/davinci-app/components/boolean.ts


Script executed:

cat -n ./e2e/davinci-app/components/boolean.ts

Repository: ForgeRock/ping-javascript-sdk


Repository: ForgeRock/ping-javascript-sdk
Exit code: 0

stdout:

     1	/*
     2	 * Copyright (c) 2026 Ping Identity Corporation. All rights reserved.
     3	 *
     4	 * This software may be modified and distributed under the terms
     5	 * of the MIT license. See the LICENSE file for details.
     6	 */
     7	import type {
     8	  ValidatedBooleanCollector,
     9	  Updater,
    10	  Validator,
    11	} from '`@forgerock/davinci-client/types`';
    12	
    13	/**
    14	 * Creates a single checkbox and attaches it to the form
    15	 * `@param` {HTMLFormElement} formEl - The form element to attach the checkbox to
    16	 * `@param` {ValidatedBooleanCollector} collector - Contains the configuration
    17	 * `@param` {Updater} updater - Function to call when selection changes
    18	 */
    19	export default function booleanComponent(
    20	  formEl: HTMLFormElement,
    21	  collector: ValidatedBooleanCollector,
    22	  updater: Updater<ValidatedBooleanCollector>,
    23	  validator: Validator<ValidatedBooleanCollector>,
    24	) {
    25	  const collectorKey = collector.output.key;
    26	
    27	  // Create a container for the checkbox
    28	  const containerDiv = document.createElement('div');
    29	  containerDiv.className = 'single-checkbox-container';
    30	
    31	  // Create a single checkbox
    32	  const wrapper = document.createElement('div');
    33	  wrapper.className = 'checkbox-wrapper';
    34	
    35	  const checkbox = document.createElement('input');
    36	  checkbox.type = 'checkbox';
    37	  checkbox.id = collectorKey;
    38	  checkbox.name = collectorKey || 'single-checkbox-field';
    39	  checkbox.checked = collector.output.value;
    40	  checkbox.value = 'checked';
    41	
    42	  const label = document.createElement('label');
    43	  label.htmlFor = checkbox.id;
    44	  label.textContent = collector.output.label;
    45	
    46	  // Add event listener to handle single-select behavior
    47	  checkbox.addEventListener('change', (event) => {
    48	    const checked = (event.target as HTMLInputElement).checked;
    49	    const result = validator(checked);
    50	    const errorEl = formEl?.querySelector(`.${collectorKey}-error`);
    51	
    52	    // Keep collector state aligned with the current UI value
    53	    const updateError = updater(checked);
    54	    if (updateError && 'error' in updateError) {
    55	      console.error(updateError.error.message);
    56	    }
    57	
    58	    // Validate the input
    59	    if (Array.isArray(result) && result.length && !errorEl) {
    60	      const errorEl = document.createElement('div');
    61	      errorEl.className = `${collectorKey}-error`;
    62	      errorEl.innerText = result.join(', ');
    63	      formEl?.querySelector(`#${collectorKey}`)?.after(errorEl);
    64	    } else {
    65	      formEl.querySelector(`.${collectorKey}-error`)?.remove();
    66	    }
    67	  });
    68	
    69	  wrapper.appendChild(checkbox);
    70	  wrapper.appendChild(label);
    71	  containerDiv.appendChild(wrapper);
    72	
    73	  // Append the container to the form
    74	  formEl.appendChild(containerDiv);
    75	}


Script executed:

# Search for how collector.output.key is generated or what format it follows
rg "collector\.output\.key" -B 3 -A 3 --type ts --type js

Repository: ForgeRock/ping-javascript-sdk


Repository: ForgeRock/ping-javascript-sdk
Exit code: 0

stdout:

e2e/davinci-app/main.ts-          collector, // This is the plain object of the collector
e2e/davinci-app/main.ts-          davinciClient.flow({
e2e/davinci-app/main.ts-            // Returns a function to call the flow from within component
e2e/davinci-app/main.ts:            action: collector.output.key,
e2e/davinci-app/main.ts-          }),
e2e/davinci-app/main.ts-          renderForm, // Ignore this; it's just for re-rendering the form
e2e/davinci-app/main.ts-        );
--
e2e/davinci-app/components/boolean.ts-  updater: Updater<ValidatedBooleanCollector>,
e2e/davinci-app/components/boolean.ts-  validator: Validator<ValidatedBooleanCollector>,
e2e/davinci-app/components/boolean.ts-) {
e2e/davinci-app/components/boolean.ts:  const collectorKey = collector.output.key;
e2e/davinci-app/components/boolean.ts-
e2e/davinci-app/components/boolean.ts-  // Create a container for the checkbox
e2e/davinci-app/components/boolean.ts-  const containerDiv = document.createElement('div');
--
e2e/davinci-app/components/fido.ts-  if (collector.type === 'FidoRegistrationCollector') {
e2e/davinci-app/components/fido.ts-    const button = document.createElement('button');
e2e/davinci-app/components/fido.ts-    button.type = 'button';
e2e/davinci-app/components/fido.ts:    button.value = collector.output.key;
e2e/davinci-app/components/fido.ts-    button.innerHTML = 'FIDO Register';
e2e/davinci-app/components/fido.ts-    formEl.appendChild(button);
e2e/davinci-app/components/fido.ts-
--
e2e/davinci-app/components/fido.ts-  } else if (collector.type === 'FidoAuthenticationCollector') {
e2e/davinci-app/components/fido.ts-    const button = document.createElement('button');
e2e/davinci-app/components/fido.ts-    button.type = 'button';
e2e/davinci-app/components/fido.ts:    button.value = collector.output.key;
e2e/davinci-app/components/fido.ts-    button.innerHTML = 'FIDO Authenticate';
e2e/davinci-app/components/fido.ts-    formEl.appendChild(button);
e2e/davinci-app/components/fido.ts-
--
e2e/davinci-app/components/password.ts-  updater: Updater<PasswordCollector | ValidatedPasswordCollector>,
e2e/davinci-app/components/password.ts-  validator?: Validator,
e2e/davinci-app/components/password.ts-) {
e2e/davinci-app/components/password.ts:  const collectorKey = dotToCamelCase(collector.output.key);
e2e/davinci-app/components/password.ts-  const label = document.createElement('label');
e2e/davinci-app/components/password.ts-  const input = document.createElement('input');
e2e/davinci-app/components/password.ts-
--
e2e/davinci-app/components/polling.ts-) {
e2e/davinci-app/components/polling.ts-  const button = document.createElement('button');
e2e/davinci-app/components/polling.ts-  button.type = 'button';
e2e/davinci-app/components/polling.ts:  button.value = collector.output.key;
e2e/davinci-app/components/polling.ts-  button.innerHTML = 'Start polling';
e2e/davinci-app/components/polling.ts-  formEl.appendChild(button);
e2e/davinci-app/components/polling.ts-
--
e2e/davinci-app/components/multi-value.ts-
e2e/davinci-app/components/multi-value.ts-    const checkbox = document.createElement('input');
e2e/davinci-app/components/multi-value.ts-    checkbox.type = 'checkbox';
e2e/davinci-app/components/multi-value.ts:    checkbox.id = `${collector.output.key}-${index}`;
e2e/davinci-app/components/multi-value.ts:    checkbox.name = collector.output.key || 'checkbox-field';
e2e/davinci-app/components/multi-value.ts-    checkbox.value = option.value;
e2e/davinci-app/components/multi-value.ts-
e2e/davinci-app/components/multi-value.ts-    const label = document.createElement('label');
--
e2e/davinci-app/components/text.ts-  updater: Updater<TextCollector | ValidatedTextCollector>,
e2e/davinci-app/components/text.ts-  validator: Validator<ValidatedTextCollector>,
e2e/davinci-app/components/text.ts-) {
e2e/davinci-app/components/text.ts:  const collectorKey = dotToCamelCase(collector.output.key);
e2e/davinci-app/components/text.ts-  const label = document.createElement('label');
e2e/davinci-app/components/text.ts-  const input = document.createElement('input');
e2e/davinci-app/components/text.ts-
--
e2e/davinci-app/components/single-value.ts-  // Create the label element
e2e/davinci-app/components/single-value.ts-  const labelEl = document.createElement('label');
e2e/davinci-app/components/single-value.ts-  labelEl.textContent = collector.output.label || 'Select an option';
e2e/davinci-app/components/single-value.ts:  labelEl.setAttribute('for', collector.output.key || 'dropdown-field');
e2e/davinci-app/components/single-value.ts-  labelEl.className = 'dropdown-label';
e2e/davinci-app/components/single-value.ts:  labelEl.setAttribute('for', collector.output.key || 'dropdown-field');
e2e/davinci-app/components/single-value.ts-
e2e/davinci-app/components/single-value.ts-  // Create the select element
e2e/davinci-app/components/single-value.ts-  const selectEl = document.createElement('select');
e2e/davinci-app/components/single-value.ts:  selectEl.name = collector.output.key || 'dropdown-field';
e2e/davinci-app/components/single-value.ts:  selectEl.id = collector.output.key || 'dropdown-field';
e2e/davinci-app/components/single-value.ts-
e2e/davinci-app/components/single-value.ts-  // Add all options from the data
e2e/davinci-app/components/single-value.ts-  for (const option of collector.output.options) {


Script executed:

# Search for dotToCamelCase definition and usage
rg "dotToCamelCase" -B 2 -A 5 --type ts --type js

Repository: ForgeRock/ping-javascript-sdk


Repository: ForgeRock/ping-javascript-sdk
Exit code: 0

stdout:

e2e/journey-app/helper.ts- * of the MIT license. See the LICENSE file for details.
e2e/journey-app/helper.ts- */
e2e/journey-app/helper.ts:export function dotToCamelCase(str: string) {
e2e/journey-app/helper.ts-  return str
e2e/journey-app/helper.ts-    .split('.')
e2e/journey-app/helper.ts-    .map((part: string, index: number) =>
e2e/journey-app/helper.ts-      index === 0 ? part.toLowerCase() : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(),
e2e/journey-app/helper.ts-    )
--
e2e/davinci-app/helper.ts- * of the MIT license. See the LICENSE file for details.
e2e/davinci-app/helper.ts- */
e2e/davinci-app/helper.ts:export function dotToCamelCase(str: string) {
e2e/davinci-app/helper.ts-  return str
e2e/davinci-app/helper.ts-    .split('.')
e2e/davinci-app/helper.ts-    .map((part: string, index: number) =>
e2e/davinci-app/helper.ts-      index === 0 ? part.toLowerCase() : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(),
e2e/davinci-app/helper.ts-    )
--
e2e/davinci-app/components/text.ts-  Validator,
e2e/davinci-app/components/text.ts-} from '`@forgerock/davinci-client/types`';
e2e/davinci-app/components/text.ts:import { dotToCamelCase } from '../helper.js';
e2e/davinci-app/components/text.ts-
e2e/davinci-app/components/text.ts-export default function textComponent(
e2e/davinci-app/components/text.ts-  formEl: HTMLFormElement,
e2e/davinci-app/components/text.ts-  collector: TextCollector | ValidatedTextCollector,
e2e/davinci-app/components/text.ts-  updater: Updater<TextCollector | ValidatedTextCollector>,
e2e/davinci-app/components/text.ts-  validator: Validator<ValidatedTextCollector>,
e2e/davinci-app/components/text.ts-) {
e2e/davinci-app/components/text.ts:  const collectorKey = dotToCamelCase(collector.output.key);
e2e/davinci-app/components/text.ts-  const label = document.createElement('label');
e2e/davinci-app/components/text.ts-  const input = document.createElement('input');
e2e/davinci-app/components/text.ts-
e2e/davinci-app/components/text.ts-  label.htmlFor = collectorKey;
e2e/davinci-app/components/text.ts-  label.innerText = collector.output.label;
--
e2e/davinci-app/components/password.ts-  Validator,
e2e/davinci-app/components/password.ts-} from '`@forgerock/davinci-client/types`';
e2e/davinci-app/components/password.ts:import { dotToCamelCase } from '../helper.js';
e2e/davinci-app/components/password.ts-
e2e/davinci-app/components/password.ts-const UPPERCASE_RE = /^[A-Z]+$/;
e2e/davinci-app/components/password.ts-const LOWERCASE_RE = /^[a-z]+$/;
e2e/davinci-app/components/password.ts-const DIGIT_RE = /^[0-9]+$/;
e2e/davinci-app/components/password.ts-
--
e2e/davinci-app/components/password.ts-  validator?: Validator,
e2e/davinci-app/components/password.ts-) {
e2e/davinci-app/components/password.ts:  const collectorKey = dotToCamelCase(collector.output.key);
e2e/davinci-app/components/password.ts-  const label = document.createElement('label');
e2e/davinci-app/components/password.ts-  const input = document.createElement('input');
e2e/davinci-app/components/password.ts-
e2e/davinci-app/components/password.ts-  label.htmlFor = collectorKey;
e2e/davinci-app/components/password.ts-  label.innerText = collector.output.label;


Script executed:

# Verify CSS.escape is available and check for any other selector uses
rg "querySelector" e2e/davinci-app/components/boolean.ts -n

Repository: ForgeRock/ping-javascript-sdk


Repository: ForgeRock/ping-javascript-sdk
Exit code: 0

stdout:

50:    const errorEl = formEl?.querySelector(`.${collectorKey}-error`);
63:      formEl?.querySelector(`#${collectorKey}`)?.after(errorEl);
65:      formEl.querySelector(`.${collectorKey}-error`)?.remove();


Escape collectorKey in querySelector selectors to handle keys with CSS-special characters.

Keys containing dots (e.g., from dotToCamelCase in other components) will break unescaped selectors. Use CSS.escape() for all querySelector id and class selectors at lines 50, 63, and 65.

🔧 Proposed fix
  const collectorKey = collector.output.key;
+  const selectorKey = CSS.escape(collectorKey);
@@
-    const errorEl = formEl?.querySelector(`.${collectorKey}-error`);
+    const errorEl = formEl?.querySelector(`.${selectorKey}-error`);
@@
-      formEl?.querySelector(`#${collectorKey}`)?.after(errorEl);
+      formEl?.querySelector(`#${selectorKey}`)?.after(errorEl);
@@
-      formEl.querySelector(`.${collectorKey}-error`)?.remove();
+      formEl.querySelector(`.${selectorKey}-error`)?.remove();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/davinci-app/components/boolean.ts` at line 25, The code constructs DOM
selectors using the raw collectorKey (const collectorKey = collector.output.key)
and then passes it to document.querySelector for id/class selectors, which
breaks when keys contain CSS-special characters (e.g., dots); update each
querySelector call that uses collectorKey (the selectors around where
collectorKey is interpolated) to wrap the key with CSS.escape(collectorKey) when
building id or class selectors so the selectors are valid for any key value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant