Skip to content

Add JS library for client-side validation for Blazor SSR#66420

Draft
oroztocil wants to merge 7 commits intomainfrom
oroztocil/validation-client-side-js
Draft

Add JS library for client-side validation for Blazor SSR#66420
oroztocil wants to merge 7 commits intomainfrom
oroztocil/validation-client-side-js

Conversation

@oroztocil
Copy link
Copy Markdown
Member

@oroztocil oroztocil commented Apr 22, 2026

Description

This PR adds a zero-dependency client-side form validation JS library for Blazor SSR forms. The companion PR with .NET-side changes (rendering data-val-* attributes from Blazor components) will follow separately.

Contributes to #51040
Companion PR to #66441

What this enables

Blazor SSR forms can get instant, in-browser validation feedback without a server round-trip, matching the experience that interactive Blazor and MVC apps provide today. The .NET model remains the single source of truth for validation rules — the server renders data-val-* HTML attributes, and this JS library reads them and enforces validation client-side.

How it works

flowchart LR
    subgraph Server["Server (ASP.NET Core)"]
        Model["<b>Model</b><br/>[Required]<br/>[Range]<br/>etc."]
        Render["<b>SSR Rendering</b><br/>(Blazor / MVC)"]
        Model --> Render
    end

    subgraph HTML["HTML Document"]
        Attrs["data-val-required='...'<br/>data-val-range-min='1'<br/>etc."]
    end

    subgraph Client["JS Validation Library"]
        Scan["<b>DomScanner</b><br/>parses rules"]
        Engine["<b>ValidationEngine</b><br/>runs validators"]
        Display["<b>ErrorDisplay</b><br/>CSS + ARIA + messages"]
        Scan --> Engine --> Display
    end

    Render -->|"generates<br/>data-val-* attributes"| Attrs
    Attrs -->|"querySelectorAll<br/>[data-val=true]"| Scan
    Display -->|"updates DOM"| HTML
Loading
  1. Server renders the form with data-val-* HTML attributes derived from .NET validation attributes on the model (e.g., [Required]data-val-required="The Name field is required.").
  2. JS library scans the DOM for elements with data-val="true", parses their data-val-* attributes into structured validation rules, and attaches event listeners.
  3. User interacts with the form — the library validates fields on change (and on input after the first error or submit), running the matching validator function for each rule.
  4. Errors are displayed via CSS classes, ARIA attributes, message element text, and the validation summary. The browser's Constraint Validation API (setCustomValidity) is also updated, driving :valid/:invalid CSS pseudo-classes.
  5. Form submission is intercepted — if any field is invalid, preventDefault + stopPropagation blocks the submit (including Blazor enhanced navigation). A validationcomplete event is dispatched with the validation result.
  6. On enhanced navigation, the library re-scans to discover new fields and clean up stale ones.

Delivery

The library can be built in two ways from the same codebase:

Bundle Size (brotli) How it's used
Embedded in blazor.web.js ≤ 3.5 KB delta Lazy-initialized only when data-val elements are detected
Standalone aspnet-core-validation.js 3.5 KB Drop-in script for apps that do not boot blazor.web.js

Key features

Lazy initializationBlazor.formValidation is only created when querySelector('[data-val="true"]') finds validatable fields. Interactive-only apps pay zero cost (no listeners added).

Blazor enhanced navigation compatibility — the library listens in capture phase (before enhanced nav's bubble phase). Invalid submissions are blocked via stopPropagation(). Additionally, when enhanced navigation replaces the DOM, re-scan is triggered via enhancedload event to update validation rules automatically.

Built-in validators matching standard .NET ValidationAttributes:
required · length · range · regex · email · url · phone · creditcard · equalto · fileextensions

UX: Lazy validation, eager recovery — fields are validated on change by default (configurable). After the first error (or first submit), validation switches to input (every keystroke) for immediate feedback as the user corrects errors.

Accessibilityaria-invalid, aria-describedby, role="alert", aria-live="assertive" are managed automatically.

CSS framework integration via two mechanisms:

  1. Constraint Validation API (zero-config) — setCustomValidity() drives :valid/:invalid pseudo-classes that Bootstrap (was-validated), Tailwind (invalid: variant), and other modern frameworks use natively.
  2. Configurable CSS class names — override defaults via Blazor.start():
Blazor.start({
  ssr: {
    formValidation: {
      cssClasses: {
        inputError: 'is-invalid',
        inputValid: 'is-valid',
        messageError: 'invalid-feedback',
        messageValid: 'valid-feedback'
      }
    }
  }
});

Space-separated values are supported for utility class based frameworks such as Tailwind.

Custom validators supported via public API:

Blazor.formValidation.addValidator('zipcode', (ctx) => {
  if (!ctx.value) return true;
  return /^\d{5}(-\d{4})?$/.test(ctx.value);
});
Blazor.formValidation.scanRules();

validationcomplete event with programmatic error access:

form.addEventListener('validationcomplete', (e) => {
  // e.detail.valid: boolean
  // e.detail.errors: Map<string, string> (field name → error message)
});

Future: Async validation extension

The design intentionally supports adding async/deferred validators (e.g., remote endpoint validation) without breaking changes. The Validator return type can be widened to include Promise<ValidationResult>, and a pending validation tracking with a deferred re-submitting mechanism can be added to run async validators. This can be added in a later PR if wanted.

Solution details

graph TD
    subgraph Entry["Entry Points"]
        Boot["<b>Boot.Web.ts</b><br/>(Blazor)"]
        Index["<b>index.ts</b><br/>(Standalone)"]
    end

    Factory["createValidationService()"]
    Boot --> Factory
    Index --> Factory

    subgraph Service["<b>ValidationService</b> (public API)"]
        direction TB
        API["addValidator()<br/>scanRules()<br/>validateField()<br/>validateForm()"]
    end
    Factory --> Service

    subgraph Internals["Internal Components"]
        Registry["<b>ValidatorRegistry</b><br/>name to validator fn"]
        Engine["<b>ValidationEngine</b><br/>per-form + per-element state<br/>runs validators, coordinates UI"]
        Scanner["<b>DomScanner</b><br/>discovery + reconciliation<br/>fingerprint-based change detection"]
        Events["<b>EventManager</b><br/>submit/reset interception<br/>lazy validation, eager recovery"]
        Display["<b>ErrorDisplay</b><br/>CSS classes<br/>ARIA attributes<br/>message elements<br/>summary"]
    end

    Service --> Registry
    Service --> Engine
    Service --> Scanner
    Service --> Events
    Engine --> Display

    subgraph Validators["Built-in Validators"]
        V["required / length / range / regex<br/>email / url / phone / creditcard<br/>equalto / fileextensions"]
    end

    Registry --> Validators
Loading

HTML Attribute Protocol

The library reads validation rules from data-val-* attributes on form elements. This is an established attribute protocol used by ASP.NET for client-side validation. Reusing it means the same server-side attribute generation works for both Blazor SSR and existing MVC/Razor Pages apps.

Element attributes

<!-- Enable validation on a field -->
<input name="Email" type="email"
       data-val="true"
       data-val-required="The Email field is required."
       data-val-email="Please enter a valid email address." />
  • data-val="true" - marks the element as validatable. The library discovers these via querySelectorAll.
  • data-val-{rule}="{message}" - defines a validation rule with its error message.
  • data-val-{rule}-{param}="{value}" - provides parameters for the rule (e.g., data-val-range-min="1").
  • data-val-event="{events}" - overrides the default validation trigger (e.g., "submit" for submit-only, or "input change" for custom events).

Validation message

<!-- Error message display (linked by name) -->
<span data-valmsg-for="Email" data-valmsg-replace="true"></span>
  • data-valmsg-for="{name}" - links a message element to a field by name attribute.
  • data-valmsg-replace="true|false" - controls whether the message element's text content is replaced (default true) or preserved.
  • data-valmsg-summary="true" - identifies the validation summary container.

Validation summary

<div data-valmsg-summary="true" class="validation-summary-valid">
  <ul></ul>
</div>

The summary shows a deduplicated list of all error messages for the form. It toggles between validation-summary-valid (hidden) and validation-summary-errors (visible) CSS classes.

Components

Core Types

type ValidatableElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;

type ValidationContext = {
  value: string | null | undefined;
  element: ValidatableElement;
  params: Record<string, string>;
}

type ValidationResult = boolean | string;
// true = valid, false = invalid (use rule's error message), string = invalid (use this message)

type Validator = (context: ValidationContext) => ValidationResult;

interface ValidationService {
  addValidator(name: string, validator: Validator): void;
  scanRules(elementOrSelector?: ParentNode | string): void;
  validateField(element: ValidatableElement): boolean;
  validateForm(form: HTMLFormElement): boolean;
}

interface ValidationOptions {
  cssClasses?: Partial<CssClassNames>;
}
  • Validator is a pure function: receives field value + params, returns pass/fail.
  • ValidationService is the public API exposed to developers via Blazor.formValidation or window.__aspnetValidation.

ValidatorRegistry

A Map<string, ValidatorEntry> that maps rule names (e.g., "required", "range") to validator functions. The set/get API is used both internally (for built-in validators) and externally (via addValidator). set has overriding semantics — calling it with an existing name replaces the validator, allowing developers to customize built-in behavior (e.g., replace the email regex).

ValidationEngine

The central coordinator. Manages per-form and per-element state, runs validators, and updates the UI.

Element state tracks:

  • rules — parsed ValidationRule[] from data-val-* attributes. Immutable after registration; attribute changes trigger unregister + re-register via DomScanner's fingerprint comparison.
  • triggerEvents"default", "submit", or custom event types
  • fingerprint — hash of all data-val* attributes for change detection during re-scan
  • currentError — the active error message (if any)
  • hasBeenInvalid — enables eager recovery after first error
  • listenerControllerAbortController for cleaning up event listeners when the element is unregistered or the DOM changes

Form state tracks:

  • trackedElementsSet<ValidatableElement> of all validated fields in the form
  • hasBeenSubmitted — enables input-level validation after first submit

Key methods:

  • validateElement(element) — runs all validators for the element, marks valid/invalid, returns boolean.
  • validateForm(form) — validates all tracked elements, updates summary, focuses first invalid field. Returns Map<string, string> (field name → error message) internally; the public ValidationService.validateForm wraps this as a boolean.
  • resetForm(form) — clears all validation state and error display.
  • registerElement / unregisterElement — manages element lifecycle, including ARIA initialization and listener cleanup.

Validation loop (validateElementInternal):

for each rule on the element:
  look up validator by rule name
  call validator({ value, element, params })
  if result indicates failure → return error message immediately (fail-fast)
return '' (all passed)

The loop uses fail-fast (one error per field) for two reasons: (1) the browser's Constraint Validation API (setCustomValidity()) accepts only a single error message per element, and (2) showing one error at a time is the standard web form UX — the user fixes it, then sees the next issue.

The engine calls setCustomValidity() on every validated element, which drives the browser's :valid/:invalid CSS pseudo-classes. This is the primary integration mechanism for CSS frameworks — Bootstrap, Tailwind, Pico, and others can read validation state without any library-specific class names.

DomScanner

Discovers validatable elements in the DOM and registers them with the engine.

scan(root?) (called internally by scanRules()) performs two phases:

  1. Reconcile — iterates tracked elements and unregisters any that are no longer connected, hidden, disabled, or missing data-val="true". This is essential for Blazor enhanced navigation, which replaces the entire DOM — stale element references must be cleaned up before discovering new ones.

  2. Discover — queries input[data-val="true"], select[data-val="true"], textarea[data-val="true"] within the root. For each candidate:

    • Skips hidden, disabled, and type="hidden" elements (hidden fields shouldn't show validation errors)
    • Computes a fingerprint from all data-val* attributes. This avoids unnecessary unregister/re-register on re-scan when attributes haven't changed — important for enhanced navigation where the DOM is replaced but the form structure is identical.
    • If already tracked with different fingerprint → unregister + re-register (server sent updated rules)
    • Parses rules, registers with engine, attaches input listeners
    • Sets novalidate on the form to suppress native browser validation bubbles (we use setCustomValidity to drive :valid/:invalid pseudo-classes, but we don't want the browser's built-in tooltip UI)

parseRules(element) reads all data-val-* attributes and groups them by rule name:

  • data-val-range="Error."{ ruleName: 'range', errorMessage: 'Error.', params: {} }
  • data-val-range-min="10" → adds params.min = '10' to the range rule

EventManager

Manages DOM event listeners for validation triggers and form submission interception.

Per-field listeners (attachInputListeners):

  • If data-val-event specifies custom events → listen to those, no gating.
  • If data-val-event="submit" → no per-field listeners (validate only on submit).
  • Default behavior ("lazy validation, eager recovery"):
    • Always validate on change (fires on blur-commit for text inputs, immediately for checkboxes/selects).
    • After the form has been submitted OR after the field has been invalid: also validate on input (every keystroke). This avoids the "everything is red immediately" problem while providing responsive feedback as the user corrects errors.

All per-field listeners use AbortController signals so they are automatically cleaned up when the element is unregistered (e.g., DOM replaced by enhanced navigation).

Form-level listeners (attachFormInterceptors):

Both listeners use { capture: true } on document. This is critical for Blazor compatibility: Blazor's enhanced navigation listens in the bubble phase, so our capture-phase handler fires first. When validation fails, stopPropagation() prevents the submit event from reaching enhanced navigation — the form stays on the page with errors instead of being submitted via fetch.

  • submit handler:

    • Respects formnovalidate on the submitter element (HTML spec).
    • Only intercepts tracked forms (forms with data-val elements). Untracked forms pass through untouched.
    • Sets hasBeenSubmitted = true (enables input-level validation for eager recovery).
    • Calls engine.validateForm(form).
    • If invalid: preventDefault() + stopPropagation() (blocks both native and enhanced submission).
    • Dispatches validationcomplete custom event with { detail: { valid, errors } }.
  • reset handler:

    • Uses setTimeout to clear validation state after the browser resets field values. This is necessary because the reset event fires before the browser clears the form — without the delay, we'd clear validation state and then immediately see stale values.

Custom event: validationcomplete

Dispatched on the form element after validation completes. Bubbles to document. The detail contains:

  • valid — boolean indicating whether the form passed validation.
  • errorsMap<string, string> mapping field names to their error messages (one error per field, empty map when valid). This provides programmatic access to validation results for developers who need custom error rendering (toasts, modals, framework-specific components) without scraping the DOM.
form.addEventListener('validationcomplete', (e) => {
  if (!e.detail.valid) {
    // Programmatic access to validation errors
    for (const [fieldName, message] of e.detail.errors) {
      showToast(`${fieldName}: ${message}`);
    }
  }
});

ErrorDisplay

Manages visual feedback: CSS classes, message element content, ARIA attributes, and validation summary.

CSS class names are configurable via the CssClassNames interface:

interface CssClassNames {
  inputError: string;    // default: 'input-validation-error'
  inputValid: string;    // default: 'input-validation-valid'
  messageError: string;  // default: 'field-validation-error'
  messageValid: string;  // default: 'field-validation-valid'
  summaryError: string;  // default: 'validation-summary-errors'
  summaryValid: string;  // default: 'validation-summary-valid'
}

The defaults match the CSS class names established by ASP.NET's existing client validation ecosystem, ensuring backward compatibility. Override via ValidationOptions.cssClasses (passed to createValidationService() or configured in Blazor.start()).

Space-separated class names are supported — addClasses/removeClasses helpers split on spaces before calling classList.add/remove.The split enables Tailwind-style multi-class values like 'border-red-500 ring-1 ring-red-500'.

ARIA management:

  • On error: sets aria-invalid="true" and aria-describedby pointing to the message element (auto-generates an id on the message element if one is not already present).
  • On valid: removes both attributes.
  • Message elements are initialized with role="alert" and aria-live="assertive" for screen reader announcements. These are set at scan time, not on every validation cycle.

Server-rendered message cleanup:
Blazor SSR renders one <div class="validation-message"> per error, but only the first has data-valmsg-for. When client validation runs, it needs to remove these server-rendered siblings to avoid showing duplicate messages. The cleanup walks sibling elements after the message element and removes any with validation-message class that lack data-valmsg-for.

Validation summary:

  • Finds [data-valmsg-summary] within the form.
  • Creates/populates a <ul> with <li> elements for each unique error message.
  • Toggles summaryError/summaryValid CSS classes.

Factory: createValidationService(options?)

The single entry point for both Blazor and standalone. It wires together all internal components:

  1. Creates a ValidatorRegistry and registers all 10 built-in validators
  2. Creates ErrorDisplay with optional CSS class overrides from ValidationOptions
  3. Creates ValidationEngine (coordinator) and DomScanner (discovery)
  4. Creates EventManager and attaches document-level submit/reset interceptors
  5. Performs the initial DOM scan to discover existing validatable fields
  6. Returns a ValidationService object with the public API

Testing

  • 215 Jest unit tests covering all 10 validators (edge cases, empty values, boundary conditions) and DOM rule parsing.
  • Playwright integration tests covering end-to-end scenarios: form submission, field-level validation, dynamic content, radio groups, custom validators, formnovalidate, validation summary, server-rendered message cleanup.

Playwright tests were added because they test the actual HTML-JS interaction against real browser DOM without the overhead of full Blazor E2E tests (no server needed, sub-second execution). Currently, these run only locally via npm run test:integration with a lightweight fixture server. If we keep these tests, they should be added into the CI pipeline in a follow up PR.

@github-actions github-actions Bot added the area-blazor Includes: Blazor, Razor Components label Apr 22, 2026
@oroztocil oroztocil force-pushed the oroztocil/validation-client-side-js branch from 3093d1d to f15952a Compare April 22, 2026 18:01
@oroztocil oroztocil requested a review from Copilot April 22, 2026 18:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a zero-dependency client-side validation library for Blazor SSR forms (and a standalone bundle) that reads data-val-* attributes, runs built-in validators, and updates the DOM with CSS/ARIA/error summary behavior—along with Jest + Playwright coverage and test fixtures.

Changes:

  • Introduces a new validation runtime (scanner/engine/eventing/display) plus 10 built-in validators and a public API for custom validators.
  • Hooks lazy initialization into blazor.web.js (SSR start options + enhanced navigation rescan) and adds a standalone aspnet-core-validation Rollup entry.
  • Adds Jest unit tests, Playwright integration tests, and HTML fixtures with a minimal fixture server.

Reviewed changes

Copilot reviewed 41 out of 41 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Components/Web.JS/test/Validation/serve-fixtures.mjs Local fixture + bundle server used by Playwright integration tests
src/Components/Web.JS/test/Validation/integration/validation.spec.ts Playwright integration tests for core validation behaviors
src/Components/Web.JS/test/Validation/integration/validation-scenarios.spec.ts Playwright scenario coverage (timing, radios, dynamic content, etc.)
src/Components/Web.JS/test/Validation/fixtures/basic-validation.html Basic fixture covering required/length/summary/reset
src/Components/Web.JS/test/Validation/fixtures/all-validators.html Fixture for exercising the full built-in validator set
src/Components/Web.JS/test/Validation/fixtures/custom-validator.html Fixture for custom validator registration behavior
src/Components/Web.JS/test/Validation/fixtures/dynamic-content.html Fixture for DOM changes + scanRules()
src/Components/Web.JS/test/Validation/fixtures/formnovalidate.html Fixture for formnovalidate + hidden/disabled skipping
src/Components/Web.JS/test/Validation/fixtures/multiple-forms.html Fixture for multiple independent forms on a page
src/Components/Web.JS/test/Validation/fixtures/no-validation.html Fixture for pages without data-val usage
src/Components/Web.JS/test/Validation/fixtures/radio-group.html Fixture for radio-group required validation
src/Components/Web.JS/test/Validation/fixtures/server-rendered-messages.html Fixture simulating SSR-rendered sibling message cleanup
src/Components/Web.JS/test/Validation/fixtures/timing.html Fixture for lazy-validation/eager-recovery timing rules
src/Components/Web.JS/test/Validation/fixtures/aria-advanced.html Fixture for ARIA behaviors + data-valmsg-replace=false
src/Components/Web.JS/test/Validation/DomScanner.test.ts Jest tests for data-val-* rule parsing
src/Components/Web.JS/test/Validation/CoreValidators.test.ts Jest tests for built-in validators’ semantics
src/Components/Web.JS/src/Validation/index.ts Standalone bundle entry exposing window.__aspnetValidation
src/Components/Web.JS/src/Validation/ValidationTypes.ts Public types for validators, options, and service API
src/Components/Web.JS/src/Validation/ValidationService.ts Factory wiring registry/engine/scanner/events + initial scan
src/Components/Web.JS/src/Validation/ValidationEngine.ts Per-form/element tracking + validation execution + summary update
src/Components/Web.JS/src/Validation/DomScanner.ts Discovery/reconcile + fingerprinting + rule parsing
src/Components/Web.JS/src/Validation/DomUtils.ts DOM helpers (form lookup, msg elements, skip logic)
src/Components/Web.JS/src/Validation/EventManager.ts Document-level submit/reset interception + per-field listeners
src/Components/Web.JS/src/Validation/ErrorDisplay.ts CSS class toggling, message updates, ARIA, summary rendering
src/Components/Web.JS/src/Validation/CoreValidators.ts Registration for the built-in validators
src/Components/Web.JS/src/Validation/Validators/Required.ts Required validator (text/checkbox/radio/select/textarea)
src/Components/Web.JS/src/Validation/Validators/StringLength.ts Length/minlength/maxlength validator
src/Components/Web.JS/src/Validation/Validators/Range.ts Numeric range validator
src/Components/Web.JS/src/Validation/Validators/Regex.ts Regex full-match validator
src/Components/Web.JS/src/Validation/Validators/Email.ts Email format validator
src/Components/Web.JS/src/Validation/Validators/Url.ts URL format validator
src/Components/Web.JS/src/Validation/Validators/Phone.ts Phone number validator
src/Components/Web.JS/src/Validation/Validators/CreditCard.ts Credit card (Luhn) validator
src/Components/Web.JS/src/Validation/Validators/EqualTo.ts Compare/equal-to validator
src/Components/Web.JS/src/Validation/Validators/FileExtensions.ts File extension allow-list validator
src/Components/Web.JS/src/Platform/SsrStartOptions.ts Adds SSR start options for validation configuration
src/Components/Web.JS/src/GlobalExports.ts Exposes Blazor.formValidation on the global API surface
src/Components/Web.JS/src/Boot.Web.ts Lazy init + enhanced navigation rescan integration
src/Components/Web.JS/rollup.config.mjs Adds Rollup entry for aspnet-core-validation bundle
src/Components/Web.JS/playwright.config.ts Playwright config for integration test directory + webServer
src/Components/Web.JS/package.json Adds Playwright dependency + test:integration script

Comment thread src/Components/Web.JS/src/Validation/ErrorDisplay.ts
Comment thread src/Components/Web.JS/src/Validation/ValidationEngine.ts
Comment thread src/Components/Web.JS/test/Validation/serve-fixtures.mjs Outdated
Comment thread src/Components/Web.JS/src/Validation/DomScanner.ts
Comment thread src/Components/Web.JS/src/Validation/EventManager.ts Outdated
Comment thread src/Components/Web.JS/src/Validation/DomUtils.ts
Comment thread src/Components/Web.JS/src/Validation/ErrorDisplay.ts Outdated
@oroztocil oroztocil requested a review from Copilot April 23, 2026 14:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 41 out of 41 changed files in this pull request and generated 8 comments.

Comment thread src/Components/Web.JS/src/Validation/DomScanner.ts Outdated
Comment thread src/Components/Web.JS/src/Validation/ErrorDisplay.ts Outdated
Comment thread src/Components/Web.JS/test/Validation/serve-fixtures.mjs
Comment thread src/Components/Web.JS/src/Validation/Validators/Regex.ts Outdated
Comment thread src/Components/Web.JS/src/Validation/Validators/FileExtensions.ts Outdated
Comment thread src/Components/Web.JS/src/Validation/ValidationTypes.ts Outdated
Comment thread src/Components/Web.JS/src/Validation/ValidationEngine.ts Outdated
oroztocil and others added 2 commits April 23, 2026 19:23
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 41 out of 41 changed files in this pull request and generated 3 comments.

Comment thread src/Components/Web.JS/src/Validation/DomScanner.ts
Comment thread src/Components/Web.JS/src/Validation/ValidationEngine.ts
Comment thread src/Components/Web.JS/src/Validation/ValidationEngine.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 41 out of 41 changed files in this pull request and generated 2 comments.

Comment on lines +92 to +93
await page.locator('#default-field').press('Control+a');
await page.locator('#default-field').press('Backspace');
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

locator.press('Control+a') is platform-specific and will fail on macOS where select-all is Meta+A. Use a platform-agnostic way to clear the field (e.g., locator.fill('')) so the test behaves consistently across OSes.

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +68
await page.locator('#default-field').press('Control+a');
await page.locator('#default-field').press('Backspace');
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

locator.press('Control+a') is platform-specific and will fail on macOS where select-all is Meta+A. To keep these Playwright tests cross-platform, clear the input using page.fill(selector, '')/locator.fill('') (or another platform-agnostic approach) instead of relying on the select-all shortcut.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants