Skip to content

[CDX-407] Update packages and jsdom version#218

Merged
esezen merged 7 commits intomainfrom
cdx-407-plp-ui-review-and-merge-open-dependabot-prs
Apr 10, 2026
Merged

[CDX-407] Update packages and jsdom version#218
esezen merged 7 commits intomainfrom
cdx-407-plp-ui-review-and-merge-open-dependabot-prs

Conversation

@esezen
Copy link
Copy Markdown
Contributor

@esezen esezen commented Mar 17, 2026

Ran npm audit fix to get rid of security vulnerabilities. I had to upgrade jsdom to version 30 which is a breaking change with major differences.

  • Upgrade jest-environment-jsdom from ^29.7.0 to ^30.3.0 (pulls in jsdom@26)
  • Fix all 18 test files that relied on Object.defineProperty(window, 'location', ...) which is no longer allowed in jsdom 26
  • Add a custom jsdom environment to patch jsdom's navigation behavior

Why do we need a custom jsdom environment?

jsdom 26 does not support window.location.href = '...' — it throws "Not implemented: navigation (except hash changes)". This is a long-standing jsdom limitation, but in previous versions our tests worked around it by replacing window.location entirely with a plain URL object (where .href is just a writable property, not a navigation trigger).

With jsdom 26 locking down window.location, we can no longer swap it out. Our source code (urlHelpers.ts, shopifyDefaults.ts) intentionally assigns to window.location.href to navigate, and multiple tests exercise that code path. Without the patch, those tests would fail with the "Not implemented: navigation" error.

Let me know if there is an easier approach to this 🤔

Copilot AI review requested due to automatic review settings March 17, 2026 19:59
@esezen esezen requested a review from a team March 17, 2026 20:01
@constructor-claude-bedrock

This comment was marked as outdated.

This comment was marked as outdated.

constructor-claude-bedrock[bot]

This comment was marked as outdated.

constructor-claude-bedrock[bot]

This comment was marked as outdated.

constructor-claude-bedrock[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@Alexey-Pavlov Alexey-Pavlov left a comment

Choose a reason for hiding this comment

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

Looks good, thank you! 🙏
I didn't like that we needed to make this patch at all, but after investigating, I found out that this is the way 🥲 Well, maybe migration to something like happy-dom would help, but it's even more changes than we have right now
Left just one minor comment

Comment thread spec/jest-env-jsdom.js Outdated
Comment on lines +6 to +11
const LocationImpl =
require('jsdom/lib/jsdom/living/window/Location-impl.js').implementation;
LocationImpl.prototype._locationObjectNavigate = function (url) {
this._relevantDocument._URL = url;
};

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.

@esezen I think Copilot overcomplicated things there, but maybe it's still worth adding try/catch with something like

throw new Error(
    'Failed to patch jsdom LocationImpl. The internal jsdom API may have changed. ' +
      `Original error: ${error.message}`,
  );

WDYT?

Comment thread spec/components/CioPlp/CioPlp.test.jsx Outdated
spy.mockImplementation(() => {});
});

afterAll(() => {
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.

Since window.location is set in beforeEach, maybe it makes sense to use afterEach here and in other places with the same setup? Some test files already use afterEach (e.g., useFilter, useBrowseResults), so it would also be more consistent

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@esezen esezen requested a review from a team as a code owner April 10, 2026 13:11
@esezen esezen requested a review from Alexey-Pavlov April 10, 2026 13:12
Copy link
Copy Markdown

@constructor-claude-bedrock constructor-claude-bedrock bot left a comment

Choose a reason for hiding this comment

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

Code Review

This PR upgrades jest-environment-jsdom from v29 to v30 (which pulls in jsdom@26) and migrates all affected tests away from Object.defineProperty(window, 'location', ...) by instead directly assigning to window.location, enabled via a custom jsdom environment that monkey-patches LocationImpl._locationObjectNavigate.

Inline comments: 6 discussions added

Overall Assessment: ⚠️ Needs Work

Comment thread spec/jest-env-jsdom.js
try {
const LocationImpl =
require('jsdom/lib/jsdom/living/window/Location-impl.js').implementation;
LocationImpl.prototype._locationObjectNavigate = function (url) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important Issue: This patch reaches deep into jsdom's private internals (jsdom/lib/jsdom/living/window/Location-impl.js) which are not part of the public API and are subject to change without notice in any jsdom patch or minor release. If jsdom renames or restructures _locationObjectNavigate, the error thrown in the catch block will surface and break the entire test suite with a cryptic failure.

Consider adding a comment that references the jsdom version range this patch was tested against, and ideally document the specific jsdom issue/PR that may eventually make this workaround unnecessary. This will help future maintainers know when the patch can be removed.

Also, since the function signature changed (navigation used to take a URL string in some jsdom versions), it is worth asserting the argument type in a note or adding a check:

LocationImpl.prototype._locationObjectNavigate = function (url) {
  // Patched for jsdom >=26 where window.location.href = '...' triggers navigation.
  // Tested with jsdom@26.x. Remove when jsdom natively supports href assignment in tests.
  this._relevantDocument._URL = url;
};

Comment thread spec/jest-env-jsdom.js
);
}

module.exports = JSDOMEnvironment;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The module exports JSDOMEnvironment but never actually creates a subclass — the patch is applied side-effectfully at require-time to the shared LocationImpl prototype. This means the custom environment file is really just a side-effect loader, not a proper Jest environment extension.

This is fragile because module caching in Node means the patch runs exactly once regardless of how many test suites load this file. While that is generally fine here, a more explicit approach would be to create an actual subclass and apply the patch inside setup() or the constructor, making the intent clearer:

class PatchedJSDOMEnvironment extends JSDOMEnvironment {
  // patch applied at module-level above; subclass exists only for documentation clarity
}
module.exports = PatchedJSDOMEnvironment;

This also makes it obvious to future developers that this is a Jest environment class, not just a configuration file.

writable: true,
configurable: true,
});
window.location = mockUrl;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important Issue: Assigning window.location = mockUrl (a plain string) replaces the entire window.location object with a string. Subsequent reads of window.location.pathname, window.location.href, window.location.origin etc. in production code (e.g., shopifyDefaults.ts:42 uses window.location.origin) will operate on a String primitive's properties — all of which are undefined. This makes the test unreliable.

The previous approach used Object.defineProperty to replace the location with a URL-like object; the new approach should instead assign only window.location.href (enabled by the jsdom patch) while leaving the Location object intact:

beforeEach(() => {
  window.location.href = mockUrl;
});

afterEach(() => {
  window.location.href = originalWindowLocation.href;
});

Object.defineProperty(window, 'location', {
value: new URL('https://example.com'),
});
window.location = 'https://example.com';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important Issue: Same as above — window.location is being assigned a plain string throughout this and other test files (useCioPlp.test.tsx, useSearchResults.test.js, useBrowseResults.test.js, useFilter.test.js, usePagination.test.js, CioPlp.test.jsx). This replaces the real Location object with a String primitive.

In particular, useRequestConfigs.test.tsx:151 then calls new URL(window.location.href) — if window.location is a string, window.location.href is undefined, causing new URL(undefined) to throw a TypeError. This entire pattern should use window.location.href = '...' instead of window.location = '...' for consistency and correctness with the patched jsdom environment.

Comment thread package.json
"husky": "^8.0.0",
"jest": "^28.1.3",
"jest-environment-jsdom": "^29.7.0",
"jest-environment-jsdom": "^30.3.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The jest version in devDependencies is still pinned to ^28.1.3 while jest-environment-jsdom is being upgraded to ^30.3.0. These are from different major versions of Jest. While it appears to work (because jest-environment-jsdom 30 happens to be compatible with the peer dependency range of Jest 28's @jest/environment), this version mismatch is surprising and potentially fragile across npm installs or version resolutions.

Consider aligning the jest and jest-environment-jsdom major versions (i.e., upgrading jest to ^30.x as well, or pinning jest-environment-jsdom to a ^29.x version that works with jsdom 26), and documenting the intentional mismatch if it is kept as-is.

Copy link
Copy Markdown
Contributor

@Alexey-Pavlov Alexey-Pavlov left a comment

Choose a reason for hiding this comment

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

LGTM!

@esezen esezen merged commit b6276f0 into main Apr 10, 2026
9 of 10 checks passed
@esezen esezen deleted the cdx-407-plp-ui-review-and-merge-open-dependabot-prs branch April 10, 2026 13:47
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