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

Getting "UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'destroy' of null" when testing a component that uses CKEditor #197

Closed
davesag opened this issue Nov 17, 2020 · 20 comments · Fixed by #223

Comments

@davesag
Copy link

davesag commented Nov 17, 2020

We are using the CKEditor 5 React component in a project and having issues running unit tests.

Take this super paired back component

import { CKEditor } from '@ckeditor/ckeditor5-react'
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'

const TextEditor = ({ message, onReady, onChange, onBlur, onFocus }) => {
  return (
    <div style={{ width: '98vw' }}>
      <CKEditor
        editor={ClassicEditor}
        data={message}
        onReady={onReady}
        onChange={onChange}
        onBlur={onBlur}
        onFocus={onFocus}
      />
    </div>
  )
}

and Jest test

import { render } from '@testing-library/react'

import TextEditor from '.'

describe('components > TextEditor', () => {
  const message = 'message'
  const onChange = jest.fn()
  const onFocus = jest.fn()
  const onReady = jest.fn()
  const onBlur = jest.fn()
  let component

  beforeAll(() => {
    component = render(
      <TextEditor
        message={message}
        onChange={onChange}
        onFocus={onFocus}
        onReady={onReady}
        onBlur={onBlur}
      />
    )
  })

  it('rendered without crashing', () => {
    expect(component.asFragment()).toMatchSnapshot()
  })
})

The test passes okay but something within the editor throws an error so we see this in the logs

(node:38593) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'destroy' of null

It's not a show-stopper, and we don't see the issue when actually running our app, but I'd love to know how to properly test the editor in a way that doesn't generate this warning.

@pomek
Copy link
Member

pomek commented Nov 18, 2020

It looks like in some cases, the watchdog instance tries to destroy itself before its created and assigned properly.

_destroyEditor() {
this.watchdog.destroy();
this.watchdog = null;
}

cc: @ma2ciek

@pomek pomek added the type:bug label Nov 18, 2020
@pomek pomek added this to the nice-to-have milestone Nov 18, 2020
@deniapps
Copy link

deniapps commented Dec 6, 2020

Got the same error on a different scenario, where we load CKEditor the second time via NextJS' dynamic import
It works fine with V2.1.0.

@pomek pomek modified the milestones: nice-to-have, iteration 39 Dec 7, 2020
@owboateng
Copy link

I am getting the same error. Please help fix it and it will be much appreciated.

@yanlee26
Copy link

same here.

@Aerophite
Copy link

Same here.

@Aerophite
Copy link

I was able to resolve this by making sure that the component had fully rendered before continuing on in my test. This leads me to believe that the real issue is that the CKEditor is trying to do something after unmount (probably the code that @pomek mentioned above).

@evertonsmenezes
Copy link

evertonsmenezes commented Jan 5, 2021

I was having the same problem, in my case, I was using a "map" and using the index as the map key, causing the error when erase any index that not the last, I changed the key for a unique code and the problem was resolved.

@Reinmar Reinmar modified the milestones: iteration 40, iteration 41 Feb 28, 2021
@pomek pomek assigned pomek and unassigned ma2ciek Mar 8, 2021
@pomek pomek closed this as completed in #223 Mar 8, 2021
pomek added a commit that referenced this issue Mar 8, 2021
Fix: Make sure that the watchdog instance exists before destroying itself. Closes #197.
@pomek
Copy link
Member

pomek commented Mar 8, 2021

@davesag, @deniapps, @evertonsmenezes, the issue has been fixed and released as https://github.com/ckeditor/ckeditor5-react/releases/tag/v3.0.2.

Could I ask you to check once again whether the problem occurs?

@deniapps
Copy link

deniapps commented Mar 8, 2021

@davesag, @deniapps, @evertonsmenezes, the issue has been fixed and released as https://github.com/ckeditor/ckeditor5-react/releases/tag/v3.0.2.

Could I ask you to check once again whether the problem occurs?

Yes. It works for me. Thank you for fixing this!

Just a reminder that starting from v3, we have to use named import (i.e., enclose "CKEditor" in curly braces), like this:

import { CKEditor } from "@ckeditor/ckeditor5-react";

My demo page is running on v3.02 now: https://deniapps.com/playground/ckeditor

@davesag
Copy link
Author

davesag commented Mar 8, 2021

I've upgraded

    "@ckeditor/ckeditor5-build-classic": "^26.0.0",
    "@ckeditor/ckeditor5-react": "^3.0.2",

but when I run my tests I am still seeing

(node:8446) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'destroy' of null

in my test output.

My component is as follows

import { bool, func, string } from 'prop-types'

import { CKEditor } from '@ckeditor/ckeditor5-react'
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'

// ref https://github.com/ckeditor/ckeditor5-react

const TextEditor = ({ hidden, message, onReady, onChange, onBlur, onFocus, ...props }) =>
  hidden ? (
    <div {...props} />
  ) : (
    <div {...props}>
      <CKEditor
        editor={ClassicEditor}
        data={message}
        onReady={onReady}
        onChange={onChange}
        onBlur={onBlur}
        onFocus={onFocus}
      />
    </div>
  )

TextEditor.propTypes = {
  hidden: bool,
  message: string,
  onReady: func,
  onChange: func,
  onBlur: func,
  onFocus: func
}

TextEditor.defaultProps = {
  hidden: false,
  message: '',
  onReady: undefined, // editor => {}
  onBlur: undefined, // (event, editor) => { data = editor.getData() }
  onFocus: undefined, // (event, editor) => { data = editor.getData() }
  onChange: undefined // (event, editor) => { data = editor.getData() }
}

export default TextEditor

and my test is as follows

import { render } from '@testing-library/react'

import TextEditor from '.'

describe('components > TextEditor > TextEditor', () => {
  const message = 'message'
  const onChange = jest.fn()
  const onFocus = jest.fn()
  const onReady = jest.fn()
  const onBlur = jest.fn()
  let component

  beforeAll(() => {
    component = render(
      <TextEditor
        message={message}
        onChange={onChange}
        onFocus={onFocus}
        onReady={onReady}
        onBlur={onBlur}
      />
    )
  })

  it('rendered without crashing', () => {
    expect(component.asFragment()).toMatchSnapshot()
  })
})

@pomek
Copy link
Member

pomek commented Mar 9, 2021

@davesag, I've tried to reproduce the issue using the code you provided but unfortunately, wick no luck. I assumed it passed for me because I use Karma+Webpack as the test runner. Do you use Jest? We already have a few issues that Jest does not work properly with our editor.

I'd like to ask about preparing a minimal part of the application that allows reproducing the issue. It will help us understand what tools are needed to get the error.

@davesag
Copy link
Author

davesag commented Mar 9, 2021

Yep I am using Jest. It's a standard CRA app which uses CRAco to override some of the standard CRA config.

I'll put a very basic example repo together tomorrow morning.

@pomek
Copy link
Member

pomek commented Mar 10, 2021

The issue about support running tests in Jest – #225.

@deniapps
Copy link

@davesag, @deniapps, @evertonsmenezes, the issue has been fixed and released as https://github.com/ckeditor/ckeditor5-react/releases/tag/v3.0.2.
Could I ask you to check once again whether the problem occurs?

Yes. It works for me. Thank you for fixing this!

Just a reminder that starting from v3, we have to use named import (i.e., enclose "CKEditor" in curly braces), like this:

import { CKEditor } from "@ckeditor/ckeditor5-react";

My demo page is running on v3.02 now: https://deniapps.com/playground/ckeditor

Actually, this is still an issue for me. Not on the demo page that I listed above, but in my application. I have CKEditor dynamically loaded in a component and looks like the error is thrown after unmounted/re-mounted.

image

It works fine with the initial load. Here is how I use CKEditor component:

const RichTextEditor = dynamic(() => import("./CKEditor"), { ssr: false });
{props.type === "editor" && (
        <RichTextEditor
          value={props.value}
          onChange={handleEditorChange}
          onUpload={handleUpload}
        />
      )}

FYI: My repos is here

@deniapps
Copy link

@deniapps, are you sure that you use the latest version of the react integration package?

https://github.com/deniapps/nextfeathers/blob/ddd12dc65578a17014348595e5d1ec0a3b983543/next/package-lock.json#L242-L249

Yes. I did. But because V3 causes the issue, I rolled back to V2. When I get a chance, I will create a branch to demo the issue.

@jansonjc
Copy link

@deniapps, did you get any resolution for this? I'm also facing the same issue with a NextJS app. I'm using V3 (3.0.2).

@deniapps
Copy link

@deniapps, did you get any resolution for this? I'm also facing the same issue with a NextJS app. I'm using V3 (3.0.2).

Yes. I fixed this a few days ago. In my case, it's due to some unnecessary page renders. Basically, the form (with CKEditor) was supposed to be rendered when the data is ready, but it was rendered regardless. So it's fixed by something like:

async componentDidMount() {
  // ... get data
  // NOTE: I set pageReady here!!! 
  this.setState({ allOptions: newAllOptions, pageReady: true });
}

// In the page render
render() {
    if (!this.state.pageReady) {
      return <p>Loading</p>;
    }
    return (
       <Form data={this.state.data} /> // the CKEditor is in the Form component
    )
}

Here is the commit for the fixes in my project: deniapps/nextfeathers@23fd112

@gfox1984
Copy link

gfox1984 commented Nov 2, 2021

I'm having the same issue with the latest @ckeditor/ckeditor5-react package (3.0.3 to date) and I'm using Jest too.

With node 14, I would get many warnings in the console, but the test would pass. With node 16, it fails with:

[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "TypeError: Cannot read properties of null (reading 'destroy')".] { code: 'ERR_UNHANDLED_REJECTION' }

@haydenkedit
Copy link

I had the same error and eventually fixed it by adding a delay at the end of my test:

await new Promise((r) => setTimeout(r, 1000));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.