Skip to content

Expression validation: Keep running when values are undefined or null#4066

Merged
olemartinorg merged 1 commit intomainfrom
bug/expression-validation-on-null
Mar 18, 2026
Merged

Expression validation: Keep running when values are undefined or null#4066
olemartinorg merged 1 commit intomainfrom
bug/expression-validation-on-null

Conversation

@olemartinorg
Copy link
Copy Markdown
Contributor

@olemartinorg olemartinorg commented Mar 18, 2026

Description

In #3735 the expression validation system was refactored and introduced a way to find all the valid/specific paths for a data model binding, i.e. find every row in repeating group structures and outputting all the valid full paths inside. This included a check for null/undefined which seemed smart, but it caused problems as expression validation would simply not run when a value was cleared by the user - so older expression validations would linger. See the referenced thread for a reproduction.

Related Issue(s)

Verification/QA

  • Manual functionality testing
    • I have tested these changes manually
    • Creator of the original issue (or service owner) has been contacted for manual testing (or will be contacted when released in alpha)
    • No testing done/necessary
  • Automated tests
    • Unit test(s) have been added/updated
    • Cypress E2E test(s) have been added/updated
    • No automatic tests are needed here (no functional changes/additions)
    • I want someone to help me make some tests
  • UU/WCAG (follow these guidelines until we have our own)
    • I have tested with a screen reader/keyboard navigation/automated wcag validator
    • No testing done/necessary (no DOM/visual changes)
    • I want someone to help me perform accessibility testing
  • User documentation @ altinn-studio-docs
    • Has been added/updated
    • No functionality has been changed/added, so no documentation is needed
    • I will do that later/have created an issue
  • Support in Altinn Studio
    • Issue(s) created for support in Studio
    • This change/feature does not require any changes to Altinn Studio
  • Sprint board
    • The original issue (or this PR itself) has been added to the Team Apps project and to the current sprint board
    • I don't have permissions to do that, please help me out
  • Labels
    • I have added a kind/* and backport* label to this PR for proper release notes grouping
    • I don't have permissions to add labels, please help me out

Summary by CodeRabbit

  • Tests

    • Added comprehensive test coverage for form field path selection across nested structures, repeating groups, and edge cases with missing values.
  • Chores

    • Refactored internal form data path resolution logic to reduce duplication and centralize decision-making for improved maintainability.

@olemartinorg olemartinorg self-assigned this Mar 18, 2026
@olemartinorg olemartinorg added the kind/bug Something isn't working label Mar 18, 2026
@olemartinorg olemartinorg moved this to 👷 In progress in Team Altinn Studio Mar 18, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

📝 Walkthrough

Walkthrough

A new helper function selectAllPaths is introduced in FormDataWrite.tsx to centralize path-resolution logic for form data fields. This function replaces inline logic previously duplicated in two places. Comprehensive Jest tests are added to cover various scenarios including empty results, nested objects, repeating groups, and edge cases with null/undefined values.

Changes

Cohort / File(s) Summary
Form Data Path Selection — Implementation
src/features/formData/FormDataWrite.tsx
Introduces new exported helper function selectAllPaths that consolidates path-resolution logic. Replaces inline path-resolution decisions in useFreshAllPaths and useDebouncedAllPaths with calls to this helper. Minor adjustment to collectMatchingFieldPaths guard condition for data object checks.
Form Data Path Selection — Tests
src/features/formData/FormDataWrite.test.ts
New Jest test suite covering selectAllPaths function with tests for empty results, raw field returns, nested object path discovery, repeating group handling, nested repeating groups with missing values, and data model edge cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: ensuring expression validation continues when values are cleared (undefined/null), matching the code refactor in FormDataWrite.tsx.
Description check ✅ Passed The description provides context from the original issue, explains the problem caused by the refactor, and covers most verification/QA sections. However, it references a Slack thread instead of a GitHub issue for tracking.

✏️ 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 bug/expression-validation-on-null
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

Migrating from UI to YAML configuration.

Use the @coderabbitai configuration command in a PR comment to get a dump of all your UI settings in YAML format. You can then edit this YAML file and upload it to the root of your repository to configure CodeRabbit programmatically.

@olemartinorg olemartinorg added the backport This PR should be cherry-picked onto older release branches label Mar 18, 2026
Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (2)
src/features/formData/FormDataWrite.tsx (1)

704-725: Use emptyArray instead of [] for consistent referential stability.

Line 718 returns a new array literal [], while line 707 correctly returns emptyArray. The new array literal creates a fresh reference on each call, which could cause unnecessary re-renders in React components relying on referential equality.

♻️ Proposed fix
     const formData = v.dataModels[reference.dataType]?.debouncedCurrentData;
     if (!formData) {
-      return [];
+      return emptyArray;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/formData/FormDataWrite.tsx` around lines 704 - 725, The function
selectAllPaths returns a fresh empty array literal in the branch where formData
is falsy; replace that literal with the shared emptyArray constant to preserve
referential stability (same pattern used earlier in the function) — update the
return in selectAllPaths (the branch that currently returns []) to return
emptyArray; no other behavior changes needed, and this keeps components using
FormDataContext and functions like collectMatchingFieldPaths from triggering
unnecessary re-renders.
src/features/formData/FormDataWrite.test.ts (1)

8-18: Type assertion is pragmatic for test mocks, but consider a type-safe alternative.

The as FormDataContext cast at line 17 bypasses type checking. While this is a common pattern for unit tests and avoids verbose full mocks, it could mask type errors if FormDataContext changes. Consider using a partial type or Pick<> to explicitly declare the subset being mocked.

♻️ Optional: Type-safe mock approach
+type PartialFormDataContext = Pick<FormDataContext, 'dataModels'>;
+
 function makeContext(formData?: object): FormDataContext {
-  return {
+  const partial: PartialFormDataContext = {
     dataModels: formData
       ? {
           [dataType]: {
             debouncedCurrentData: formData,
           },
         }
       : {},
-  } as FormDataContext;
+  };
+  return partial as FormDataContext;
 }

As per coding guidelines: "Avoid using any or type casting (as type) in TypeScript".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/formData/FormDataWrite.test.ts` around lines 8 - 18, The test
helper makeContext currently force-casts the object to FormDataContext (avoid
using "as" here); change the signature to return a narrower, type-safe shape
such as Partial<FormDataContext> or Pick<FormDataContext, "dataModels"> and
construct the object with a matching type (set dataModels with [dataType] and
debouncedCurrentData) so no top-level type assertion is needed; update callers
of makeContext in tests if necessary to accept the narrower return type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/features/formData/FormDataWrite.test.ts`:
- Around line 8-18: The test helper makeContext currently force-casts the object
to FormDataContext (avoid using "as" here); change the signature to return a
narrower, type-safe shape such as Partial<FormDataContext> or
Pick<FormDataContext, "dataModels"> and construct the object with a matching
type (set dataModels with [dataType] and debouncedCurrentData) so no top-level
type assertion is needed; update callers of makeContext in tests if necessary to
accept the narrower return type.

In `@src/features/formData/FormDataWrite.tsx`:
- Around line 704-725: The function selectAllPaths returns a fresh empty array
literal in the branch where formData is falsy; replace that literal with the
shared emptyArray constant to preserve referential stability (same pattern used
earlier in the function) — update the return in selectAllPaths (the branch that
currently returns []) to return emptyArray; no other behavior changes needed,
and this keeps components using FormDataContext and functions like
collectMatchingFieldPaths from triggering unnecessary re-renders.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: df8b0b79-9bf5-4553-b171-7138fd8bf4f8

📥 Commits

Reviewing files that changed from the base of the PR and between 79e1c08 and 6d00d62.

📒 Files selected for processing (2)
  • src/features/formData/FormDataWrite.test.ts
  • src/features/formData/FormDataWrite.tsx

@sonarqubecloud
Copy link
Copy Markdown

@olemartinorg olemartinorg changed the title Expression validation: Keep running when values are undefined or null Adding unit test + making sure even null/undefined values are returned as valid paths Mar 18, 2026
@olemartinorg olemartinorg merged commit 0fca32f into main Mar 18, 2026
16 of 18 checks passed
@olemartinorg olemartinorg deleted the bug/expression-validation-on-null branch March 18, 2026 12:07
@github-project-automation github-project-automation bot moved this from 👷 In progress to ✅ Done in Team Altinn Studio Mar 18, 2026
github-actions bot pushed a commit that referenced this pull request Mar 18, 2026
…d as valid paths (#4066)

Co-authored-by: Ole Martin Handeland <git@olemartin.org>
@github-actions
Copy link
Copy Markdown
Contributor

Automatic backport successful!

A backport PR has been automatically created for the release/v4.26 release branch.

The release branch release/v4.26 already existed and was updated.

The cherry-pick was clean with no conflicts. Please review the backport PR when it appears.

@olemartinorg olemartinorg changed the title Adding unit test + making sure even null/undefined values are returned as valid paths Expression validation: Keep running when values are undefined or null Mar 18, 2026
olemartinorg added a commit that referenced this pull request Mar 18, 2026
…d as valid paths (#4066)

Co-authored-by: Ole Martin Handeland <git@olemartin.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport This PR should be cherry-picked onto older release branches kind/bug Something isn't working

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants