Search code examples
reactjstypescripttinymcecypress

@foreachbe/cypress-tinymce TinyMCE package for Cypress is always returning undefined for editors instance with TypeScript


I am building a React JS application using TypeScript. In my application, I am using TinyMCE editor in a form. I am also writing integration tests using Cypress in TypeScript. I am now having trouble setting the TinyMCE editor value in the Cypress using TypeScript. I am using this library to manipulate TinyMCE editor in Cypress, https://github.com/ForeachOS/cypress-tinymce.

This is my React JS TinyMCE editor code.

import React, { FC } from 'react'
import { Editor } from '@tinymce/tinymce-react'

interface RichEditorFieldProps {
    id: string
    name?: string
    value?: string
    onChange: (fieldName: string, value: string) => void
}

const RichEditorField: FC<RichEditorFieldProps> = (
    props: RichEditorFieldProps
) => {
    const getFieldName = () => {
        return props.name ? props.name : props.id
    }

    return (
        <div cy-data-id={props.id} className={'rich-editor-field-container'}>
            <Editor
                id={props.id}
                apiKey={'zjf0nctg4gmjj0a5u5uxm7pcrs0ka4hwcuxzskhxh5hauugf'}
                value={props.value ?? ''}
                onEditorChange={(content) => {
                    props.onChange(getFieldName(), content)
                }}
            />
        </div>
    )
}

I am rendering that component as follows:

<FormRichEditorField
                            value={formik.values.specification}
                            label={'Specification'}
                            error={
                                formik.touched.specification &&
                                formik.errors.specification
                            }
                            id={'specification'}
                            onChange={formik.setFieldValue}
                        />

What I want to highlight is that the id of the editor is specification.

To write the test for that in Cypress, I installed the package I mentioned, https://github.com/ForeachOS/cypress-tinymce.

After I installed the package and import it into support/index.ts file, I cannot just call the function as follows:

cy.setTinyMceContent('specification', 'This is the new content');

I have to add the following code to make it compatible with TypeScript:

declare namespace Cypress {
    interface Chainable<Subject = any> {
        setTinyMceContent(tinyMceId: string, content: any): void
    }
}

Then I set the value of the TinyMCE editor field as follow in my Cypress test:

cy.setTinyMceContent('specification', 'This is testing')

When I run the test, I am getting the following error:

Cannot read properties of undefined (reading 'specification')

enter image description here

What is wrong with my code and how can I fix it?


Solution

  • The package cypress-tinymce looks well out of date. There's no editors property on the latest ver 6 tinyMCE.

    The following is the best I have so far. There are a couple of places where you need to wait for the editor to spin up.

    The 2nd waits for current text content, which isn't good as it's too specific. I haven't found a better way to do this yet.

    Updated from @WaiYanHein comment

    /// <reference types="cypress" />
    
    Cypress.Commands.add('setTinyMceContent', (tinyMceId: string, content: any) => {
      
      cy.window().should('have.property', 'tinymce')   // wait for tinyMCE
    
      cy.wait(1000).then(() => {   // wait for editor to be ready
    
        const win = cy.state('window')
        const editor = win.tinymce.EditorManager.get() 
          .filter((editor: any) => editor.id === fieldId)[0] 
    
        editor2.setContent(content, { format: 'text' });
      })
    })
    
    it('adds some text to tinyMCE', () => {
    
      cy.visit('http://localhost:3001')
    
      cy.setTinyMceContent('specification', 'This is the new content')
    
      cy.window().then(win => {
        cy.wrap(win.tinymce.activeEditor.getContent({ format: 'text'}))
          .should('eq', 'This is the new content')                      // ✅ passes
      })
    });
    

    The React app is controlling the content, you may see the text change to "This is the new content" then revert to "Some text".

    I think this is caused by the onChange() event firing which re-renders the component and sets it back to the App's text.

    Because of that, it may be better to control the React app through app actions rather than approach the tinyMCE API directly. Depends on the goals of your testing.