Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: structuredClone is not defined #3363

Open
tbertrand7 opened this issue May 2, 2022 · 22 comments · May be fixed by #3521
Open

[Bug]: structuredClone is not defined #3363

tbertrand7 opened this issue May 2, 2022 · 22 comments · May be fixed by #3521
Labels

Comments

@tbertrand7
Copy link

tbertrand7 commented May 2, 2022

Basic info:

  • Node.js version: 18.0.0
  • jsdom version: 19.0.0

Minimal reproduction case

Running a jest test that calls the structuredClone global using the jsdom environment

structuredClone();

I first opened this issue in the jest repo and was instructed to open it here.

@jathanasiou
Copy link

This is still not working and it affects React app tests as well.

@TD-DO
Copy link

TD-DO commented Aug 19, 2022

global.structuredClone = val => {
  return JSON.parse(JSON.stringify(val))
}

is what worked for me.
But is this going to be fixed?

@AideTechBot
Copy link

AideTechBot commented Aug 22, 2022

I can vouch that this is still an issue using jest-environment-jsdom v28.1.3

@amrutraj
Copy link

As an alternative, you could mock structuredClone in your test file:

global.structuredClone = jest.fn(val => {
    return JSON.parse(JSON.stringify(val));
});

This won't work for copying functions as values of the keys. For the rest, no problem.

@domenic domenic added the feature label Oct 1, 2022
@mftw
Copy link

mftw commented Oct 26, 2022

As an alternative, you could mock structuredClone in your test file:

global.structuredClone = jest.fn(val => {
    return JSON.parse(JSON.stringify(val));
});

This won't work for copying functions as values of the keys. For the rest, no problem.

functions aren't supported by the structured clone algo anyway. https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types

@oleksandr-dukhovnyy
Copy link

oleksandr-dukhovnyy commented Nov 11, 2022

global.structuredClone = jest.fn(val => {
return JSON.parse(JSON.stringify(val));
});

This method is good for objects that can be converted to JSON. This doesn't work for regular expressions, for example.


A smarter solution seems to be installing a polyfill.

  1. Installing core-js (it is clear that if it is already installed, you do not need to install it again)
npm install core-js --save-dev

2. In the test file, add the line

import 'core-js/modules/web.structured-clone';
// ...
// tests...
  1. In the test file, add the line
import 'core-js/stable/structured-clone';
// ...
// tests...

This is suitable if you are using the jsdom environment ("testEnvironment": "jsdom"). For other cases, the exact method may be slightly different, but the basic meaning is the same. This is the simplest one I've found. You can also try setting up Babel to automate this.

@domenic
Copy link
Member

domenic commented Nov 11, 2022

https://twitter.com/slicknet/status/782274190451671040

@realtimetodie
Copy link

@Aleksandr-JS-Developer No, this is wrong. The core-js polyfill of the structuredClone() method is incomplete in comparison to the native Node.js implementation.

Instead, Node.js has its own built-in structuredClone() method since version 17.0.0. The method can be reflected in the VM context, as introduced in PR #3459.

@zloirock
Copy link

The core-js polyfill of the structuredClone() method is incomplete in comparison to the native Node.js implementation.

@diceride really? -) It reuses native implementation if it's available and fills the missing cases.

@zloirock
Copy link

zloirock commented Nov 11, 2022

@Aleksandr-JS-Developer

import 'core-js/modules/web.structured-clone';

Use import 'core-js/stable/structured-clone'; since this module has some dependencies.

@oleksandr-dukhovnyy
Copy link

@Aleksandr-JS-Developer No, this is wrong. The core-js polyfill of the structuredClone() method is incomplete in comparison to the native Node.js implementation.

So what exactly are the differences?

Instead, Node.js has its own built-in structuredClone() method since version 17.0.0. The method can be reflected in the VM context, as introduced in PR #3459.

First, not everyone can simply upgrade Node to two major versions at once.
Second, the changes in your PR just make jsdom incompatible with Node < 17. It won't suit me.

@GeorgianStan
Copy link

GeorgianStan commented Jan 18, 2023

structuredClone() has polyfills that can be used.

The solution I chose was:

  1. Install ungap/structured-clone
    npm i -D @ungap/structured-clone @types/ungap__structured-clone

  2. Update jest.config.ts and import the library and make it available as structuredClone:

import * as structuredClone from '@ungap/structured-clone'; // can be written as import { structuredClone } from '@ungap/structured-clone';  if esModuleInterop flag is used


export default {
  ....
  globals: {
    structuredClone: structuredClone.default, // 'structuredClone' is a default export so I access it via .default
  },
};

@eatsjobs
Copy link

eatsjobs commented Mar 14, 2023

Any news on this topic? Unfortunately we have a case where we need the original one and not the polyfilled one

structuredCloned(new Proxy({}))  

gives an error and we've a case where we want to test it.

I just tested with

  • jest-environment-jsdom: ^29.5.0
  • jest: ^29.5.0

and the issue still persist

@tkrotoff
Copy link

tkrotoff commented Mar 14, 2023

How to force Jest/jsdom to use Node.js structuredClone implementation (requires Node.js >= 17.0.0):

// FixJSDOMEnvironment.ts

import JSDOMEnvironment from 'jest-environment-jsdom';

// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string
export default class FixJSDOMEnvironment extends JSDOMEnvironment {
  constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
    super(...args);

    // FIXME https://github.com/jsdom/jsdom/issues/3363
    this.global.structuredClone = structuredClone;
  }
}
// jest.config.js

/** @type {import('jest').Config} */
const config = {
  testEnvironment: './FixJSDOMEnvironment.ts',

  ...
}

module.exports = config;

See also #1724 (comment), #1721 (comment)

@eatsjobs
Copy link

@tkrotoff Thanks!

@GeorgianStan
Copy link

@tkrotoff is this something that we manually need to add or it will be handled by jsdom in a future update?

@patelnets

This comment was marked as spam.

@urbenlegend
Copy link

urbenlegend commented Jun 5, 2023

How to force Jest/jsdom to use Node.js structuredClone implementation (requires Node.js >= 17.0.0):

// FixJSDOMEnvironment.ts

import JSDOMEnvironment from 'jest-environment-jsdom';

// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string
export default class FixJSDOMEnvironment extends JSDOMEnvironment {
  constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
    super(...args);

    // FIXME https://github.com/jsdom/jsdom/issues/3363
    this.global.structuredClone = structuredClone;
  }
}
// jest.config.js

/** @type {import('jest').Config} */
const config = {
  testEnvironment: './FixJSDOMEnvironment.ts',

  ...
}

module.exports = config;

See also #1724 (comment), #1721 (comment)

@tkrotoff Is there a way to do this without ejecting in create-react-app? I have a few React components that are calling structuredClone and they're erroring out when I try to render them in my unit tests.

@tkrotoff
Copy link

tkrotoff commented Jun 5, 2023

@urbenlegend I don't know I don't use create-react-app

@sw-tracker
Copy link

testEnvironment: './FixJSDOMEnvironment.ts',

were you able to work this out without ejecting?

brianrodri added a commit to brianrodri/obsidian-bujo that referenced this issue Sep 24, 2023
MGibson1 added a commit to bitwarden/clients that referenced this issue Nov 28, 2023
This is a deficiency in jsdom --
jsdom/jsdom#3363 -- structured clone is well
supported.
MGibson1 added a commit to bitwarden/clients that referenced this issue Nov 29, 2023
* Handle switch messaging

TODO: handle loading state for account switcher

* Async updates required for state

* Fallback to email for current account avatar

* Await un-awaited promises

* Remove unnecessary Prune

Prune was getting confused in browser and deleting memory in browser on
account switch. This method isn't needed since logout already removes
memory data, which is the condition for pruning

* Fix temp password in browser

* Use direct memory access until data is serializable

Safari uses a different message object extraction than firefox/chrome
and is removing `UInt8Array`s. Until all data passed into StorageService
is guaranteed serializable, we need to use direct access in state
service

* Reload badge and context menu on switch

* Gracefully switch account as they log out.

* Maintain location on account switch

* Remove unused state definitions

* Prefer null for state

undefined can be misinterpreted to indicate a value has not been set.

* Hack: structured clone in memory storage

We are currently getting dead objects on account switch due to updating
the object in the foreground state service. However, the storage service
is owned by the background. This structured clone hack ensures that all
objects stored in memory are owned by the appropriate context

* Null check nullable values

active account can be null, so we should include null safety in the
equality

* Correct background->foreground switch command

* Already providing background memory storage

* Handle connection and clipboard on switch account

* Prefer strict equal

* Ensure structuredClone is available to jsdom

This is a deficiency in jsdom --
jsdom/jsdom#3363 -- structured clone is well
supported.

* Fixup types in faker class
jlf0dev pushed a commit to bitwarden/clients that referenced this issue Nov 30, 2023
* Handle switch messaging

TODO: handle loading state for account switcher

* Async updates required for state

* Fallback to email for current account avatar

* Await un-awaited promises

* Remove unnecessary Prune

Prune was getting confused in browser and deleting memory in browser on
account switch. This method isn't needed since logout already removes
memory data, which is the condition for pruning

* Fix temp password in browser

* Use direct memory access until data is serializable

Safari uses a different message object extraction than firefox/chrome
and is removing `UInt8Array`s. Until all data passed into StorageService
is guaranteed serializable, we need to use direct access in state
service

* Reload badge and context menu on switch

* Gracefully switch account as they log out.

* Maintain location on account switch

* Remove unused state definitions

* Prefer null for state

undefined can be misinterpreted to indicate a value has not been set.

* Hack: structured clone in memory storage

We are currently getting dead objects on account switch due to updating
the object in the foreground state service. However, the storage service
is owned by the background. This structured clone hack ensures that all
objects stored in memory are owned by the appropriate context

* Null check nullable values

active account can be null, so we should include null safety in the
equality

* Correct background->foreground switch command

* Already providing background memory storage

* Handle connection and clipboard on switch account

* Prefer strict equal

* Ensure structuredClone is available to jsdom

This is a deficiency in jsdom --
jsdom/jsdom#3363 -- structured clone is well
supported.

* Fixup types in faker class
rr-bw added a commit to bitwarden/clients that referenced this issue Dec 6, 2023
* Handle switch messaging

TODO: handle loading state for account switcher

* Async updates required for state

* Fallback to email for current account avatar

* Await un-awaited promises

* Remove unnecessary Prune

Prune was getting confused in browser and deleting memory in browser on
account switch. This method isn't needed since logout already removes
memory data, which is the condition for pruning

* Fix temp password in browser

* Use direct memory access until data is serializable

Safari uses a different message object extraction than firefox/chrome
and is removing `UInt8Array`s. Until all data passed into StorageService
is guaranteed serializable, we need to use direct access in state
service

* Reload badge and context menu on switch

* Gracefully switch account as they log out.

* Maintain location on account switch

* Remove unused state definitions

* Prefer null for state

undefined can be misinterpreted to indicate a value has not been set.

* Hack: structured clone in memory storage

We are currently getting dead objects on account switch due to updating
the object in the foreground state service. However, the storage service
is owned by the background. This structured clone hack ensures that all
objects stored in memory are owned by the appropriate context

* Null check nullable values

active account can be null, so we should include null safety in the
equality

* Correct background->foreground switch command

* Already providing background memory storage

* Handle connection and clipboard on switch account

* Prefer strict equal

* Ensure structuredClone is available to jsdom

This is a deficiency in jsdom --
jsdom/jsdom#3363 -- structured clone is well
supported.

* Fixup types in faker class

* add avatar and simple navigation to header

* add options buttons

* add app-header to necessary pages

* add back button and adjust avatar sizes

* add helper text when account limit reached

* convert magic number to constant

* add clarifying comment

* adjust homepage header styles

* navigate to previousp page upon avatar click when already on '/account-switcher'

* move account UI to own component

* add i18n

* show correct auth status

* add aria-hidden to icons

* use listbox role

* add screen reader accessibility to account component

* more SR a11y updates to account component

* add hover and focus states to avatar

* refactor hover and focus states for avatar

* add screen reader text for avatar

* add slide-down animation on account switcher close

* remove comment

* setup account component story

* add all stories

* move navigation call to account component

* implement account lock

* add button hover effect

* implement account logout

* implement lockAll accounts functionality

* replace 'any' with custom type

* add account switcher button to /home login page

* use <main> tag (enables scrolling)

* change temp server filler name

* temporarily remove server arg from account story

* don't show avatar on /home if no accounts, and don't show 'lock'/'logout' buttons if no currentAccount

* add translation and a11y to /home avatar

* add correct server url to account component

* add 'server' to AccountOption type

* Enabled account switching client-side feature flag.

* add slide-in transition to /account-switcher page

* change capitalization to reflect figma design

* make screen reader read active account email, address more capitalization

* fix web avatar misalignment

* make avatar color based on user settings and in sync with other clients

* make property private

* change accountOptions to availableAccounts for clarity

* refactor to remove 'else' template ref

* remove empty scss rule

* use tailwind instead of scss

* rename isSelected to isActive

* add 'isButton' to /home page avatar

* move files to services folder

* update import

* Remove duplicate active account button

* Move no account button to current-account component

* Always complete logging out

Fixes PM-4866

* make screenreader read off email, not name

* refactor avatar for button case

* Do not next object updates

StateService's init was calling `updateState` at multiple layers,
once overall and then again for each account updated. Because
we were not maintaining a single state object through the
process, it was ending up in a consistent, but incomplete state.
Fixed by returning the updated state everywhere. This very well
may not be all the bugs associated with this

* Treat null switch account as no active user

* Listen for switchAccountFinish before routing (#6950)

* adjust avatar style when wrapped in a button

* show alt text for favicon setting

* move stories to browser

* Send Finish Message on null

* Dynamically set active user when locking all

This is required because some user lock states are not recoverable after
process reload (those with logout timeout). This waits until reload is
occurring, then sets the next user appropriately

* Move Finished Message to Finally Block

Fix tests

* Drop problematic key migration

Fixes PM-4933. This was an instance of foreground/background collision
when writing state. We have several other fallbacks of clearing
these deprecated keys.

* Prefer location to homebrew router service

* Initialize account disk cache from background

Uses the `isRecoveredSession` bool to re-initialize foreground caches
from a background message. This avoids a lengthy first-read for
foregrounds

* PM-4865 - Browser Acct Switcher - only show lock btn for lockable accounts (#6991)

* Lock of active account first, when locking multiple.

Fixes PM-4996

* Fix linter

* Hide lock now for locked users (#7020)

* Hide lock now for locked users

* Prefer disabling button to removing

* Add tooltip to TDE without unlock method

* Load all auth states on state init (#7027)

This is a temporary fix until the owning services can update state
themselves. It uses the presence of an auto key to surmise unlocked
state on init. This is safe since it's run only once on extension start.

* Ps/pm 5004/add load to account switcher (#7032)

* Add load spinner to account switcher

* Remove ul list icons

* Properly size account switcher in popout

* [PM-5005] Prevent Double Navigation (#7035)

* Delete Overriden Method

* Add Lock Transition

* truncate email and server name

* remove account.stories.ts (will add in separate PR)

* Do not switch user at reload if no user is active

* fix prettier issues

---------

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
Co-authored-by: Todd Martin <tmartin@bitwarden.com>
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
Co-authored-by: bnagawiecki <107435978+bnagawiecki@users.noreply.github.com>
@olof-nord

This comment was marked as duplicate.

@gangsthub
Copy link

gangsthub commented Feb 1, 2024

How to force Jest/jsdom to use Node.js structuredClone implementation (requires Node.js >= 17.0.0):

// FixJSDOMEnvironment.ts

import JSDOMEnvironment from 'jest-environment-jsdom';

// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string
export default class FixJSDOMEnvironment extends JSDOMEnvironment {
  constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
    super(...args);

    // FIXME https://github.com/jsdom/jsdom/issues/3363
    this.global.structuredClone = structuredClone;
  }
}
// jest.config.js

/** @type {import('jest').Config} */
const config = {
  testEnvironment: './FixJSDOMEnvironment.ts',

  ...
}

module.exports = config;

See also #1724 (comment), #1721 (comment)

In case anybody needs it, I found out how to do the same in vitest:

// environment.ts

import type { Environment } from 'vitest'
import { builtinEnvironments } from 'vitest/environments'

export default <Environment>{
  name: 'custom_jsdom',
  transformMode: 'ssr',
  setup: (global, options) => {
    builtinEnvironments.jsdom.setup(global, options)

    // eslint-disable-next-line no-param-reassign
    global.structuredClone = structuredClone

    return {
      teardown() {
        // called after all tests with this env have been run
      },
    }
  },
}

And in the vitest.config.ts file:

export default mergeConfig(
  viteConfig,
  defineConfig({
    // ...
    test: {
      environment: './path/to/environment.ts',
      // ...
    },
  }),
)

Docs: https://vitest.dev/guide/environment#custom-environment

github-merge-queue bot pushed a commit to tldraw/tldraw that referenced this issue Mar 18, 2024
…3166)

Currently, we only use native `structuredClone` in the browser, falling
back to `JSON.parse(JSON.stringify(...))` elsewhere, despite Node
supporting `structuredClone` [since
v17](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone)
and Cloudflare Workers supporting it [since
2022](https://blog.cloudflare.com/standards-compliant-workers-api/).
This PR adjusts our shim to use the native `structuredClone` on all
platforms, if available.

Additionally, `jsdom` doesn't implement `structuredClone`, a bug [open
since 2022](jsdom/jsdom#3363). This PR patches
`jsdom` environment in all packages/apps that use it for tests.

Also includes a driveby removal of `deepCopy`, a function that is
strictly inferior to `structuredClone`.

### Change Type

<!-- ❗ Please select a 'Scope' label ❗️ -->

- [x] `sdk` — Changes the tldraw SDK
- [x] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff

<!-- ❗ Please select a 'Type' label ❗️ -->

- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [x] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know


### Test Plan

1. A smoke test would be enough

- [ ] Unit Tests
- [x] End to end tests
LukasMasuch added a commit to streamlit/streamlit that referenced this issue May 4, 2024
## Describe your changes

This PR updates all vega-related frontend dependencies and applies some
refactoring by moving all the arrow-data handling logic into a dedicated
`arrowUtils` file.

This is mainly done because `vega-embed` cannot be tested with jest
because of an issue with jsdom:
jsdom/jsdom#3363

Unfortunately, we cannot update the `vega-lite` dependency to the latest
version since it breaks our stacked area charts in version >=5.14.1.
Related issue: vega/vega-lite#9337

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet