Skip to content

fix: enforce required validation on APM phone field - #273

Open
roshan-gorasia-cko wants to merge 2 commits into
masterfrom
fix/apm-phone-required-validation
Open

fix: enforce required validation on APM phone field#273
roshan-gorasia-cko wants to merge 2 commits into
masterfrom
fix/apm-phone-required-validation

Conversation

@roshan-gorasia-cko

@roshan-gorasia-cko roshan-gorasia-cko commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Fixes a defect in the embedded APM (`client.apm.authorization` / `apm.tokenization`) phone field where the "required" validation stops firing after interaction.

The bug

If a required phone number is entered and then removed, the "required" validation stops firing and the field can be submitted empty.

The phone field is an object, and its digits live under different keys depending on origin: the Phone component emits `{ dialing_code, number }` after the shopper interacts, while the initial/prefilled seed uses a legacy `value` key. After a shopper types a number and clears it, the value is `{ number: "" }`, but `validateField` only read the `value` key — so it saw a non-empty object and the required check passed.

The fix

  • `getComparableFieldValue()` (`src/apm/utils.ts`) normalises a field value to its scalar, reading the canonical `number` first and falling back to the legacy `value`; used in `validateField` (`src/apm/views/utils/form.ts`).
  • The `Phone` component now emits `oninput` when the number is cleared back into the dialing-code prefix (`src/apm/elements/phone.ts`), so a full clear propagates to the form instead of leaving a stale value.

Tests

  • Adds Vitest regression coverage for the required rule in both value shapes, including the cleared-`number` case that reproduced the bug.
  • Runs on the existing test harness; `yarn test` is already wired into CI.

@roshan-gorasia-cko roshan-gorasia-cko changed the title fix: enforce required validation on APM phone field fix: APM phone field — required validation + { dialing_code, number } alignment Jul 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes inconsistent APM phone field value shapes by standardizing on { dialing_code, number }, ensuring required validation works after the user clears the input, and preventing prefills from being submitted under the legacy value key. It also introduces a Vitest-based test harness to exercise the real (namespace-style) TypeScript sources without shipping test code in the SDK bundle.

Changes:

  • Add getComparableFieldValue() and normalizePhoneValue() utilities to normalize phone values for validation and submission.
  • Update form validation, phone element behavior, and NextSteps seeding/prefill to use the canonical { dialing_code, number } shape.
  • Add Vitest + Node test support and CI wiring, with regression tests covering normalization and required validation.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
yarn.lock Adds Vitest and its transitive dependencies to the lockfile.
vitest.config.ts Configures Vitest to run Node-based TypeScript tests under test/**/*.test.ts.
test/support/loadNamespace.ts Provides a harness to transpile and execute namespace-style SDK sources for testing.
test/apm/phone-value-shape.test.ts Adds coverage for normalizePhoneValue() coercion into { dialing_code, number }.
test/apm/phone-validation.test.ts Adds regression coverage for required validation across value vs number shapes.
src/apm/views/utils/form.ts Switches validation to normalize values via getComparableFieldValue().
src/apm/views/NextSteps.ts Normalizes phone seed/prefill values via normalizePhoneValue().
src/apm/utils.ts Introduces utilities for comparable validation values and canonical phone normalization.
src/apm/types.ts Updates InitialData.phone_number typing to prefer number and keep value as deprecated alias.
src/apm/elements/phone.ts Aligns Phone element value shape to { dialing_code, number } and propagates clears for validation.
package.json Adds vitest and test scripts; includes tests in verify.
eslint.config.mjs Marks tests and Vitest config as Node-global (not browser-global).
.github/workflows/build-lint-test.yml Runs yarn test in CI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/apm/utils.ts Outdated
Comment thread src/apm/types.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/apm/views/utils/form.ts:33

  • The required validation still checks typeof value === "undefined" instead of the normalised actualValue. If a phone value comes through as an object with { number: undefined } (or { value: undefined }), getComparableFieldValue() returns undefined but the required rule will not fire, allowing an empty required phone to pass.
    switch (true) {
      case validation.required &&
        (typeof value === "undefined" || (typeof actualValue === "string" && actualValue.length === 0)): {
        return "Missing required value"
      }

Comment thread src/apm/elements/phone.ts
Comment thread src/apm/elements/phone.ts
Comment thread test/support/loadNamespace.ts
The APM phone form value has two shapes: the Phone component emits the
number under `number` once the shopper interacts, while the initial /
prefilled seed carries it under the legacy `value` key. validateField only
read `value`, so after the shopper typed a number and removed it the value
was `{ number: "" }` — an object without a `value` key — which the required
check treated as a non-empty value and passed. Required validation
effectively disappeared after any interaction.

- add getComparableFieldValue() in utils.ts to normalise a field value
  (canonical `number`, falling back to legacy `value`) and use it in
  validateField
- emit oninput when the number is cleared back into the dialing-code prefix
  in phone.ts, so a full clear propagates to the form instead of leaving a
  stale value
- add Vitest regression coverage on the existing test harness

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@roshan-gorasia-cko
roshan-gorasia-cko force-pushed the fix/apm-phone-required-validation branch from 9a2bd67 to b850415 Compare July 28, 2026 15:11
@roshan-gorasia-cko roshan-gorasia-cko changed the title fix: APM phone field — required validation + { dialing_code, number } alignment fix: enforce required validation on APM phone field Jul 28, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@roshan-gorasia-cko
roshan-gorasia-cko marked this pull request as ready for review July 28, 2026 15:39
Comment on lines +57 to +63
function isMissingRequired(value: unknown): boolean {
const actualValue = getComparableFieldValue(value)
return (
typeof value === "undefined" ||
(typeof actualValue === "string" && actualValue.length === 0)
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Not really sure we need this function. We create it in the tests and then test it?
If we want to test getComparableFieldValue(), we could just make explicit assertions on its return values explicitly instead? 🤔

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.

3 participants