Skip to content

Conversation

@ichim-david
Copy link
Member

@ichim-david ichim-david commented Oct 16, 2025

I have an error at
http://localhost:3000/en/sandbox/david-tests.
When trying to visit:
http://localhost:3000/en/sandbox/david-tests/contents

the superagent code crashed which meant I couldn't navigate any other pages anymore.

This pr adds a scoped request handler for unhandled promise rejections during SSR to log and prevent the server from crashing on common HTTP errors. Wrap loadOnServer in a try/catch to handle synchronous errors (e.g. superagent 404) and route them to the existing error handler.
This pull request introduces improvements to the UniversalLink component and its test coverage, as well as adds a comprehensive repository guidelines document. The most significant changes are the robust handling of invalid href values in UniversalLink, expanded unit tests for both UniversalLink and UserSelectWidget, and the addition of clear documentation for repository usage and development.

UniversalLink Improvements

  • Added a guard to UniversalLink to detect and handle cases where href is an array instead of a string, logging an error and rendering nothing to prevent runtime errors.
  • Improved the logic for determining if a link is external or should trigger file display/download, making checks more robust against falsy or malformed url values.

Test Coverage Enhancements

  • Added a comprehensive test suite for UniversalLink in UniversalLink.test.jsx, covering internal/external links, attribute handling, blacklisted URLs, error cases, and invalid href scenarios.
  • Updated Jest snapshots for the new and modified UniversalLink test cases.
  • Expanded tests for UserSelectWidget to cover Redux state integration, prop variations, selection clearing, async behavior, and normalization logic. [1] [2]
  • Added fireEvent import for user interaction simulation in UserSelectWidget tests.

Documentation

  • Added a new AGENTS.md file outlining repository structure, coding standards, dependencies, build/test procedures, and security/configuration guidelines for contributors.

…void crashes

Add a global handler for unhandled promise rejections during SSR to log and
prevent the server from crashing on common HTTP errors. Wrap loadOnServer in a
try/catch to handle synchronous errors (e.g. superagent 404) and route them to
the existing error handler.
@ichim-david ichim-david requested a review from Copilot October 16, 2025 16:00
Copy link
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

This PR adds error handling for unhandled promise rejections and synchronous errors during server-side rendering to prevent server crashes when encountering HTTP errors like 404s.

  • Adds a global unhandledRejection event handler to prevent server crashes on API errors
  • Wraps the loadOnServer call in try-catch to handle synchronous errors from superagent
  • Includes minor code formatting improvements for readability

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

- add original tests & snapshots plus the test for array being based within href
…SSR error handler with detailed logging

- This is to take into consideration the feedback of Github copilot
…void errors when url is undefined

-  If url is falsy and isExternal is false (now guaranteed by your change on line 82), line 87 will throw a TypeError when attempting to call .includes() on a falsy value.
@ichim-david ichim-david requested a review from Copilot October 16, 2025 19:05
Copy link
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 4 out of 4 changed files in this pull request and generated 2 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

…w, structure, build/test, coding & testing, PRs, security)
…rollHeight; update test

- Convert waitForAllContentToLoad to async/await with try/catch to properly wait for iframes, images and Plotly charts before printing
- Use pageDocument.scrollHeight for the final scroll to ensure the correct container is scrolled
- Update test to repeatedly flush timers/promises and assert window.scrollTo includes behavior: 'instant'
- to be able to call individual tests and properly collect coverage
…otly charts, media events, scrolling and timeout/error cases
Add createMockStore, defaultProps and renderWidget test helpers and extend
coverage for UserSelectWidget. New tests cover normalization variants
(normalizeChoices / normalizeSingleSelectOption), rendering with initial
vocabulary state, choice updates, async search interactions (focus/change,
noOptionsMessage, clearing selection), lifecycle behaviors (timeouts/unmount,
terms cache update), disabled/placeholder states and MenuList handling for
large choice sets.
@ichim-david ichim-david requested a review from Copilot October 22, 2025 17:31
Copy link
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 9 out of 9 changed files in this pull request and generated 3 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 453 to 464
fs.existsSync(
path.join(
__dirname,
'node_modules',
'@eeacms',
addonName,
'jest.setup.js',
),
)
? `<rootDir>/node_modules/@eeacms/${addonName}/jest.setup.js`
: `<rootDir>/src/addons/${addonName}/jest.setup.js`,
],
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

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

The nested path.join calls create complex logic. Consider extracting the path construction to a variable for better readability and maintainability.

Suggested change
fs.existsSync(
path.join(
__dirname,
'node_modules',
'@eeacms',
addonName,
'jest.setup.js',
),
)
? `<rootDir>/node_modules/@eeacms/${addonName}/jest.setup.js`
: `<rootDir>/src/addons/${addonName}/jest.setup.js`,
],
(() => {
const nodeModulesSetupPath = path.join(
__dirname,
'node_modules',
'@eeacms',
addonName,
'jest.setup.js',
);
return fs.existsSync(nodeModulesSetupPath)
? `<rootDir>/node_modules/@eeacms/${addonName}/jest.setup.js`
: `<rootDir>/src/addons/${addonName}/jest.setup.js`;
})(),

Copilot uses AI. Check for mistakes.
const s = fs.statSync(p);
statCache.set(p, s);
return s;
} catch {
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

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

The getStatSync function returns null on error but doesn't provide any context about what failed. Consider logging or documenting why the stat call failed for debugging purposes.

Suggested change
} catch {
} catch (err) {
console.error(`[getStatSync] Failed to stat path: ${p}\nError: ${err && err.message ? err.message : err}`);

Copilot uses AI. Check for mistakes.
Comment on lines 159 to 168
function errorHandler(error) {
// Log error details for debugging
const ignoredErrors = [301, 302, 401, 404];
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

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

The ignoredErrors array is defined inside the errorHandler function, causing it to be recreated on every error. Move this constant outside the function for better performance.

Suggested change
function errorHandler(error) {
// Log error details for debugging
const ignoredErrors = [301, 302, 401, 404];
const ignoredErrors = [301, 302, 401, 404];
function errorHandler(error) {
// Log error details for debugging

Copilot uses AI. Check for mistakes.
@ichim-david ichim-david requested a review from avoinea October 23, 2025 14:51
@avoinea
Copy link
Member

avoinea commented Nov 4, 2025

⏺ PR Review Summary

Based on my analysis of the changes in this branch compared to develop, here's my assessment:

✅ SAFE TO MERGE - These are defensive bug fixes with good test coverage


Key Changes

  1. UniversalLink.jsx - Critical Bug Fixes ✅ SAFE

File: src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx

Changes:

  • Array href guard (line 31-36): Returns null and logs error when href is an array instead of string
  • Null-safe external check (line 82): url ? !isInternalURL(url) : false prevents crash on undefined/null URLs
  • Null-safe display-file check (line 86): Guards url.includes('@@display-file') with null check

Safety Assessment:

  • Backward Compatible: ✅ Returns null for invalid inputs (safe fallback)
  • No Breaking Changes: Valid href strings work exactly as before
  • Prevents Crashes: These are defensive guards against runtime errors
  • Good Error Logging: Console errors help debugging
  1. UniversalLink.test.jsx - Comprehensive Test Coverage ✅ EXCELLENT

File: src/customizations/volto/components/manage/UniversalLink/UniversalLink.test.jsx

Test Coverage:

  • Internal/external links ✅
  • Null href handling ✅
  • Array href edge cases (empty array, non-empty array) ✅
  • Display-file URL handling ✅
  • Error scenarios ✅
  • Target/rel attributes ✅
  • Blacklisted routes ✅

Safety Assessment:

  • Comprehensive coverage of edge cases
  • Snapshots ensure no unintended rendering changes
  • Tests validate the defensive guards work correctly
  1. server.jsx - Enhanced SSR Error Handling ✅ SAFE

File: src/customizations/volto/server.jsx

Changes:

  • Moved error handler definition earlier (line 159-195)
  • Added detailed error logging with context (URL, status, stack trace)
  • Wrapped loadOnServer in try-catch for synchronous errors (line 283-394)
  • Better error categorization (ignored vs. logged errors)
  • Improved comments explaining error handling strategy

Safety Assessment:

  • Backward Compatible: ✅ Same error handling flow, just more robust
  • Prevents Process Crashes: Catches synchronous errors from failed API calls
  • Better Debugging: Enhanced logging helps diagnose SSR issues in production
  • No Behavior Change: Still renders error pages and returns proper HTTP status codes
  1. AGENTS.md - Documentation ✅ NON-FUNCTIONAL

New documentation file - no runtime impact.


Potential Issues & Risk Assessment

⚠️ Minor Concerns

  1. UniversalLink returning null
    - Impact: Components expecting a link will render nothing
    - Risk: LOW - This is safer than crashing the page
    - Mitigation: Error is logged to console for debugging
  2. Server error logging verbosity
    - Impact: More console output in production
    - Risk: VERY LOW - Only logs on errors
    - Consideration: Monitor log volume if many 404s occur

✅ No Breaking Changes Detected

  • Valid use cases continue to work exactly as before
  • Changes are purely defensive (guard clauses)
  • Test snapshots confirm rendering stays the same
  • SSR error handling preserves existing flow

Backward Compatibility Analysis

Scenarios Tested:

  1. Valid string href → Works exactly as before ✅
  2. Undefined/null href → Now safely returns null instead of crashing ✅
  3. Array href (bug in data) → Returns null with error log instead of crashing ✅
  4. SSR API failures → Better error handling, same result pages ✅

Migration Impact: NONE

No changes required in consuming code.


Production Safety Checklist

  • ✅ Defensive programming (fail-safe, not fail-hard)
  • ✅ Comprehensive test coverage
  • ✅ No API changes or breaking changes
  • ✅ Error logging for debugging
  • ✅ Handles edge cases gracefully
  • ✅ SSR error handling prevents process crashes
  • ✅ Backward compatible

Final Recommendation

🟢 APPROVED FOR PRODUCTION

Reasoning:

  1. These are critical bug fixes that prevent website crashes when invalid data is encountered
  2. The changes are purely defensive - adding guards where there were none
  3. Excellent test coverage validates the fixes work correctly
  4. Backward compatible - all valid use cases continue to work
  5. The SSR error handling improvements prevent the entire Node process from crashing on API errors

Risk Level: VERY LOW

The changes actually reduce crash risk significantly by:

  • Preventing undefined.includes() errors
  • Catching array hrefs (likely from bad CMS data)
  • Improving SSR error isolation

Suggested Actions Before Deploy:

  1. ✅ Verify tests pass (make test)
  2. ✅ Run in staging environment to confirm SSR logging works as expected
  3. ✅ Monitor error logs after deploy for any invalid href patterns to fix at the data source

This PR is a textbook example of defensive programming and crash prevention. Strong approve for production deployment.

Copy link
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 5 out of 5 changed files in this pull request and generated 9 comments.

Comments suppressed due to low confidence (1)

src/customizations/volto/server.jsx:201

  • If the errorHandler function itself throws an error (e.g., if renderToString fails or res.send throws), there's no safety net to prevent the request from hanging. Consider wrapping the errorHandler's operations in a try-catch block and sending a minimal error response as a last resort to ensure the client always receives a response.
  function errorHandler(error) {
    // Log error details for debugging
    if (!ignoredErrors.includes(error.status)) {
      console.error('[SSR Error Handler]', {
        url: req.url,
        status: error.status,
        message: error.message,
        stack: error.stack,
      });
    } else {
      // Log ignored errors at debug level
      console.log('[SSR Error Handler] HTTP error:', {
        url: req.url,
        status: error.status,
        message: error.message,
      });
    }

    // Render error page
    const errorPage = (
      <Provider store={store} onError={reactIntlErrorHandler}>
        <StaticRouter context={{}} location={req.url}>
          <ErrorPage message={error.message} />
        </StaticRouter>
      </Provider>
    );

    res.set({
      'Cache-Control': 'public, max-age=60, no-transform',
    });

    res
      .status(error.status || 500) // If error happens in Volto code itself error status is undefined
      .send(`<!doctype html> ${renderToString(errorPage)}`);
  }

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

}

const isExternal = !isInternalURL(url);
const isExternal = url ? !isInternalURL(url) : false;
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Good defensive programming to check if url is truthy before calling isInternalURL(url). However, consider what should happen when url is falsy. Currently isExternal will be false, which means the code will attempt to render a Link component with a falsy URL. This could lead to unexpected behavior. Consider adding an early return or additional validation when url is null/undefined/empty.

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +140
config.settings.externalRoutes = [
{
match: {
path: '/external-app',
exact: true,
strict: false,
},
url(payload) {
return payload.location.pathname;
},
},
];
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Modifying config.settings.externalRoutes globally in tests can cause test pollution and flaky tests if tests run in parallel or if the config state persists between tests. Consider storing the original value in a beforeEach hook and restoring it in an afterEach hook, or use a test-specific mock of the config object.

Copilot uses AI. Check for mistakes.
@ichim-david ichim-david marked this pull request as ready for review November 17, 2025 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants