element which will be replaced by CKEditor.
+ *
+ * @return {JSX.Element}
+ */
render() {
return (
@@ -86,12 +102,14 @@ export default class CKEditor extends React.Component {
/**
* Initializes the editor by creating a proper watchdog and initializing it with the editor's configuration.
+ *
+ * @private
*/
_initializeEditor() {
if ( this.context instanceof ContextWatchdog ) {
this.watchdog = new EditorWatchdogAdapter( this.context );
} else {
- this.watchdog = new EditorWatchdog( this.editor );
+ this.watchdog = new CKEditor._EditorWatchdog( this.props.editor );
}
this.watchdog.setCreator( ( el, config ) => this._createEditor( el, config ) );
@@ -107,6 +125,7 @@ export default class CKEditor extends React.Component {
/**
* Creates an editor from the element and configuration.
*
+ * @private
* @param {HTMLElement} element The source element.
* @param {Object} config CKEditor 5 editor configuration.
* @returns {Promise}
@@ -157,6 +176,8 @@ export default class CKEditor extends React.Component {
/**
* Destroys the editor by destroying the watchdog.
+ *
+ * @private
*/
_destroyEditor() {
// It may happen during the tests that the watchdog instance is not assigned before destroying itself. See: #197.
@@ -172,7 +193,8 @@ export default class CKEditor extends React.Component {
/**
* Returns true when the editor should be updated.
*
- * @param {*} nextProps React's properties.
+ * @private
+ * @param {Object} nextProps React's properties.
* @returns {Boolean}
*/
_shouldUpdateEditor( nextProps ) {
@@ -192,6 +214,12 @@ export default class CKEditor extends React.Component {
return true;
}
+ /**
+ * Returns the editor configuration.
+ *
+ * @private
+ * @return {Object}
+ */
_getConfig() {
if ( this.props.data && this.props.config.initialData ) {
console.warn(
@@ -277,12 +305,7 @@ class EditorWatchdogAdapter {
* An editor instance.
*/
get editor() {
- // TODO - try/catch should not be necessary as `getItem` could return `null` instead of throwing errors.
- try {
- return this._contextWatchdog.getItem( this._id );
- } catch ( err ) {
- return null;
- }
+ return this._contextWatchdog.getItem( this._id );
}
}
@@ -313,3 +336,7 @@ CKEditor.defaultProps = {
config: {},
onError: ( error, details ) => console.error( error, details )
};
+
+// Store the API in the static property to easily overwrite it in tests.
+// Too bad dependency injection does not work in Webpack + ES 6 (const) + Babel.
+CKEditor._EditorWatchdog = EditorWatchdog;
diff --git a/tests/_utils-tests/context.js b/tests/_utils-tests/context.js
new file mode 100644
index 0000000..75ff4fd
--- /dev/null
+++ b/tests/_utils-tests/context.js
@@ -0,0 +1,40 @@
+/**
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Context from '../_utils/context';
+
+describe( 'Context', () => {
+ describe( 'constructor()', () => {
+ it( 'saves the config', () => {
+ const config = { foo: 'bar' };
+ const context = new Context( config );
+
+ expect( context.config ).to.equal( config );
+ } );
+ } );
+
+ describe( 'destroy()', () => {
+ it( 'should return a promise that resolves properly', () => {
+ return Context.create()
+ .then( context => {
+ const promise = context.destroy();
+
+ expect( promise ).to.be.an.instanceof( Promise );
+
+ return promise;
+ } );
+ } );
+ } );
+
+ describe( 'create()', () => {
+ it( 'should return a promise that resolves properly', () => {
+ const promise = Context.create();
+
+ expect( promise ).to.be.an.instanceof( Promise );
+
+ return promise;
+ } );
+ } );
+} );
diff --git a/tests/_utils-tests/turnoffdefaulterrorcatching.js b/tests/_utils-tests/turnoffdefaulterrorcatching.js
index 7d1c9bd..16d5210 100644
--- a/tests/_utils-tests/turnoffdefaulterrorcatching.js
+++ b/tests/_utils-tests/turnoffdefaulterrorcatching.js
@@ -1,19 +1,22 @@
/**
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ * For licensing, see LICENSE.md.
*/
/* global window */
-/**
- * Turns off the default error catching
- * so Mocha won't complain about errors caused by the called function.
- */
-export default async function turnOffDefaultErrorCatching( fn ) {
- const originalOnError = window.onerror;
- window.onerror = null;
+import turnOffDefaultErrorCatching from '../_utils/turnoffdefaulterrorcatching';
+
+describe( 'turnOffDefaultErrorCatching()', () => {
+ it( 'should catch the error', () => {
+ const onErrorStub = sinon.stub( window, 'onerror' );
+
+ turnOffDefaultErrorCatching( () => {
+ window.onerror( 'Foo', null, 0 );
+ } );
- await fn();
+ onErrorStub.restore();
- window.onerror = originalOnError;
-}
+ expect( onErrorStub.called ).to.equal( false );
+ } );
+} );
diff --git a/tests/_utils/context.js b/tests/_utils/context.js
index f312724..23cf822 100644
--- a/tests/_utils/context.js
+++ b/tests/_utils/context.js
@@ -1,13 +1,22 @@
-
/**
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
+/**
+ * Mock of class that representing the Context feature.
+ *
+ * @see: https://ckeditor.com/docs/ckeditor5/latest/api/module_core_context-Context.html
+ */
export default class ContextMock {
+ constructor( config ) {
+ this.config = config;
+ }
+
static create( config ) {
return Promise.resolve( new ContextMock( config ) );
}
+
static destroy() {
return Promise.resolve();
}
diff --git a/tests/_utils/turnoffdefaulterrorcatching.js b/tests/_utils/turnoffdefaulterrorcatching.js
new file mode 100644
index 0000000..fd2e555
--- /dev/null
+++ b/tests/_utils/turnoffdefaulterrorcatching.js
@@ -0,0 +1,19 @@
+/**
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* global window */
+
+/**
+ * Turns off the default error catching
+ * so Mocha won't complain about errors caused by the called function.
+ */
+export default async function turnOffDefaultErrorCatching( fn ) {
+ const originalOnError = window.onerror;
+ window.onerror = () => {};
+
+ await fn();
+
+ window.onerror = originalOnError;
+}
diff --git a/tests/ckeditor-classiceditor.jsx b/tests/ckeditor-classiceditor.jsx
deleted file mode 100644
index be9c458..0000000
--- a/tests/ckeditor-classiceditor.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* global ClassicEditor */
-
-import React from 'react';
-import 'react-dom';
-import { configure, mount } from 'enzyme';
-import Adapter from 'enzyme-adapter-react-16';
-
-import { CKEditor } from '../dist/ckeditor';
-
-configure( { adapter: new Adapter() } );
-
-describe( 'CKEditor Component + ClassicEditor Build', () => {
- let wrapper;
-
- afterEach( () => {
- if ( wrapper ) {
- wrapper.unmount();
- }
- } );
-
- it( 'should initialize the ClassicEditor properly', async () => {
- await new Promise( res => {
- wrapper = mount(
);
- } );
-
- const component = wrapper.instance();
-
- expect( component.editor ).to.not.be.null;
- expect( component.editor.element ).to.not.be.null;
- } );
-} );
diff --git a/tests/ckeditor.jsx b/tests/ckeditor.jsx
index b5c6615..882ac4b 100644
--- a/tests/ckeditor.jsx
+++ b/tests/ckeditor.jsx
@@ -11,11 +11,11 @@ import Adapter from 'enzyme-adapter-react-16';
import Editor from './_utils/editor';
import CKEditor from '../src/ckeditor.jsx';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
-import turnOffDefaultErrorCatching from './_utils-tests/turnoffdefaulterrorcatching';
+import turnOffDefaultErrorCatching from './_utils/turnoffdefaulterrorcatching';
configure( { adapter: new Adapter() } );
-describe( 'CKEditor Component', () => {
+describe( '
Component', () => {
let wrapper;
beforeEach( () => {
@@ -87,61 +87,53 @@ describe( 'CKEditor Component', () => {
);
} );
- it( 'sets initial data if was specified (using the "config" property with the `initialData` key)', done => {
+ it( 'sets initial data if was specified (using the "config" property with the `initialData` key)', async () => {
sinon.stub( Editor, 'create' ).resolves( new Editor() );
- wrapper = mount( Hello CKEditor 5!'
- } } /> );
-
- setTimeout( () => {
- expect( Editor.create.firstCall.args[ 1 ].initialData ).to.equal(
- 'Hello CKEditor 5!
'
- );
-
- done();
+ await new Promise( res => {
+ wrapper = mount( Hello CKEditor 5!' } } onReady={ res } /> );
} );
+
+ expect( Editor.create.firstCall.args[ 1 ].initialData ).to.equal(
+ 'Hello CKEditor 5!
'
+ );
} );
- it( 'shows a warning if used "data" and "config.initialData" at the same time', done => {
+ it( 'shows a warning if used "data" and "config.initialData" at the same time', async () => {
const consoleWarnStub = sinon.stub( console, 'warn' );
- wrapper = mount( Bar'
- } } /> );
+ await new Promise( res => {
+ const data = 'Foo
';
- setTimeout( () => {
- // We must restore "console.warn" before assertions in order to see warnings if they were logged.
- consoleWarnStub.restore();
+ wrapper = mount( );
+ } );
- expect( consoleWarnStub.calledOnce ).to.be.true;
- expect( consoleWarnStub.firstCall.args[ 0 ] ).to.equal(
- 'Editor data should be provided either using `config.initialData` or `data` properties. ' +
- 'The config property is over the data value and the first one will be used when specified both.'
- );
+ // We must restore "console.warn" before assertions in order to see warnings if they were logged.
+ consoleWarnStub.restore();
- done();
- } );
+ expect( consoleWarnStub.calledOnce ).to.be.true;
+ expect( consoleWarnStub.firstCall.args[ 0 ] ).to.equal(
+ 'Editor data should be provided either using `config.initialData` or `data` properties. ' +
+ 'The config property is over the data value and the first one will be used when specified both.'
+ );
} );
- it( 'uses "config.initialData" over "data" when specified both', done => {
+ it( 'uses "config.initialData" over "data" when specified both', async () => {
const consoleWarnStub = sinon.stub( console, 'warn' );
sinon.stub( Editor, 'create' ).resolves( new Editor() );
- wrapper = mount( Bar'
- } } /> );
-
- setTimeout( () => {
- // We must restore "console.warn" before assertions in order to see warnings if they were logged.
- consoleWarnStub.restore();
+ await new Promise( res => {
+ wrapper = mount( Bar'
+ } } onReady={ res } /> );
+ } );
- expect( Editor.create.firstCall.args[ 1 ].initialData ).to.equal(
- 'Bar
'
- );
+ // We must restore "console.warn" before assertions in order to see warnings if they were logged.
+ consoleWarnStub.restore();
- done();
- } );
+ expect( Editor.create.firstCall.args[ 1 ].initialData ).to.equal(
+ 'Bar
'
+ );
} );
it( 'when setting initial data, it must not use "Editor.setData()"', async () => {
@@ -191,6 +183,29 @@ describe( 'CKEditor Component', () => {
expect( consoleErrorStub.firstCall.args[ 1 ].phase ).to.equal( 'initialization' );
expect( consoleErrorStub.firstCall.args[ 1 ].willEditorRestart ).to.equal( false );
} );
+
+ it( 'passes the specified editor class to the watchdog feature', async () => {
+ const EditorWatchdog = CKEditor._EditorWatchdog;
+ const constructorSpy = sinon.spy();
+
+ class CustomEditorWatchdog extends EditorWatchdog {
+ constructor( ...args ) {
+ super( ...args );
+ constructorSpy( ...args );
+ }
+ }
+
+ CKEditor._EditorWatchdog = CustomEditorWatchdog;
+
+ await new Promise( res => {
+ wrapper = mount( );
+ } );
+
+ expect( constructorSpy.called ).to.equal( true );
+ expect( constructorSpy.firstCall.args[ 0 ] ).to.equal( Editor );
+
+ CKEditor._EditorWatchdog = EditorWatchdog;
+ } );
} );
describe( 'properties', () => {
@@ -279,140 +294,128 @@ describe( 'CKEditor Component', () => {
} );
describe( '#onFocus', () => {
- it( 'listens to the "viewDocument#focus" event in order to call "onFocus" callback', done => {
+ it( 'listens to the "viewDocument#focus" event in order to call "onFocus" callback', async () => {
const editorInstance = new Editor();
const viewDocument = Editor._editing.view.document;
sinon.stub( Editor, 'create' ).resolves( editorInstance );
sinon.stub( editorInstance, 'getData' ).returns( 'Foo.
' );
- wrapper = mount( );
-
- setTimeout( () => {
- // More events are being attached to `viewDocument`.
- expect( viewDocument.on.calledTwice ).to.be.true;
- expect( viewDocument.on.firstCall.args[ 0 ] ).to.equal( 'focus' );
- expect( viewDocument.on.firstCall.args[ 1 ] ).to.be.a( 'function' );
-
- done();
+ await new Promise( res => {
+ wrapper = mount( );
} );
+
+ // More events are being attached to `viewDocument`.
+ expect( viewDocument.on.calledTwice ).to.be.true;
+ expect( viewDocument.on.firstCall.args[ 0 ] ).to.equal( 'focus' );
+ expect( viewDocument.on.firstCall.args[ 1 ] ).to.be.a( 'function' );
} );
- it( 'executes "onFocus" callback if it was specified and the editor was focused', done => {
+ it( 'executes "onFocus" callback if it was specified and the editor was focused', async () => {
const onFocus = sinon.spy();
const editorInstance = new Editor();
const viewDocument = Editor._editing.view.document;
sinon.stub( Editor, 'create' ).resolves( editorInstance );
- wrapper = mount( );
-
- setTimeout( () => {
- const fireChanges = viewDocument.on.firstCall.args[ 1 ];
- const event = { name: 'focus' };
+ await new Promise( res => {
+ wrapper = mount( );
+ } );
- fireChanges( event );
+ const fireChanges = viewDocument.on.firstCall.args[ 1 ];
+ const event = { name: 'focus' };
- expect( onFocus.calledOnce ).to.equal( true );
- expect( onFocus.firstCall.args[ 0 ] ).to.equal( event );
- expect( onFocus.firstCall.args[ 1 ] ).to.equal( editorInstance );
+ fireChanges( event );
- done();
- } );
+ expect( onFocus.calledOnce ).to.equal( true );
+ expect( onFocus.firstCall.args[ 0 ] ).to.equal( event );
+ expect( onFocus.firstCall.args[ 1 ] ).to.equal( editorInstance );
} );
- it( 'executes "onFocus" callback if it is available in runtime when the editor was focused', done => {
+ it( 'executes "onFocus" callback if it is available in runtime when the editor was focused', async () => {
const onFocus = sinon.spy();
const editorInstance = new Editor();
const viewDocument = Editor._editing.view.document;
sinon.stub( Editor, 'create' ).resolves( editorInstance );
- wrapper = mount( );
-
- setTimeout( () => {
- wrapper.setProps( { onFocus } );
+ await new Promise( res => {
+ wrapper = mount( );
+ } );
- const fireChanges = viewDocument.on.firstCall.args[ 1 ];
- const event = { name: 'focus' };
+ wrapper.setProps( { onFocus } );
- fireChanges( event );
+ const fireChanges = viewDocument.on.firstCall.args[ 1 ];
+ const event = { name: 'focus' };
- expect( onFocus.calledOnce ).to.equal( true );
- expect( onFocus.firstCall.args[ 0 ] ).to.equal( event );
- expect( onFocus.firstCall.args[ 1 ] ).to.equal( editorInstance );
+ fireChanges( event );
- done();
- } );
+ expect( onFocus.calledOnce ).to.equal( true );
+ expect( onFocus.firstCall.args[ 0 ] ).to.equal( event );
+ expect( onFocus.firstCall.args[ 1 ] ).to.equal( editorInstance );
} );
} );
describe( '#onBlur', () => {
- it( 'listens to the "viewDocument#blur" event in order to call "onBlur" callback', done => {
+ it( 'listens to the "viewDocument#blur" event in order to call "onBlur" callback', async () => {
const editorInstance = new Editor();
const viewDocument = Editor._editing.view.document;
sinon.stub( Editor, 'create' ).resolves( editorInstance );
sinon.stub( editorInstance, 'getData' ).returns( 'Foo.
' );
- wrapper = mount( );
-
- setTimeout( () => {
- // More events are being attached to `viewDocument`.
- expect( viewDocument.on.calledTwice ).to.be.true;
- expect( viewDocument.on.secondCall.args[ 0 ] ).to.equal( 'blur' );
- expect( viewDocument.on.secondCall.args[ 1 ] ).to.be.a( 'function' );
-
- done();
+ await new Promise( res => {
+ wrapper = mount( );
} );
+
+ // More events are being attached to `viewDocument`.
+ expect( viewDocument.on.calledTwice ).to.be.true;
+ expect( viewDocument.on.secondCall.args[ 0 ] ).to.equal( 'blur' );
+ expect( viewDocument.on.secondCall.args[ 1 ] ).to.be.a( 'function' );
} );
- it( 'executes "onBlur" callback if it was specified and the editor was blurred', done => {
+ it( 'executes "onBlur" callback if it was specified and the editor was blurred', async () => {
const onBlur = sinon.spy();
const editorInstance = new Editor();
const viewDocument = Editor._editing.view.document;
sinon.stub( Editor, 'create' ).resolves( editorInstance );
- wrapper = mount( );
-
- setTimeout( () => {
- const fireChanges = viewDocument.on.secondCall.args[ 1 ];
- const event = { name: 'blur' };
+ await new Promise( res => {
+ wrapper = mount( );
+ } );
- fireChanges( event );
+ const fireChanges = viewDocument.on.secondCall.args[ 1 ];
+ const event = { name: 'blur' };
- expect( onBlur.calledOnce ).to.equal( true );
- expect( onBlur.firstCall.args[ 0 ] ).to.equal( event );
- expect( onBlur.firstCall.args[ 1 ] ).to.equal( editorInstance );
+ fireChanges( event );
- done();
- } );
+ expect( onBlur.calledOnce ).to.equal( true );
+ expect( onBlur.firstCall.args[ 0 ] ).to.equal( event );
+ expect( onBlur.firstCall.args[ 1 ] ).to.equal( editorInstance );
} );
- it( 'executes "onBlur" callback if it is available in runtime when the editor was blurred', done => {
+ it( 'executes "onBlur" callback if it is available in runtime when the editor was blurred', async () => {
const onBlur = sinon.spy();
const editorInstance = new Editor();
const viewDocument = Editor._editing.view.document;
sinon.stub( Editor, 'create' ).resolves( editorInstance );
- wrapper = mount( );
-
- setTimeout( () => {
- wrapper.setProps( { onBlur } );
+ await new Promise( res => {
+ wrapper = mount( );
+ } );
- const fireChanges = viewDocument.on.secondCall.args[ 1 ];
- const event = { name: 'blur' };
+ wrapper.setProps( { onBlur } );
- fireChanges( event );
+ const fireChanges = viewDocument.on.secondCall.args[ 1 ];
+ const event = { name: 'blur' };
- expect( onBlur.calledOnce ).to.equal( true );
- expect( onBlur.firstCall.args[ 0 ] ).to.equal( event );
- expect( onBlur.firstCall.args[ 1 ] ).to.equal( editorInstance );
+ fireChanges( event );
- done();
- } );
+ expect( onBlur.calledOnce ).to.equal( true );
+ expect( onBlur.firstCall.args[ 0 ] ).to.equal( event );
+ expect( onBlur.firstCall.args[ 1 ] ).to.equal( editorInstance );
} );
} );
@@ -488,7 +491,7 @@ describe( 'CKEditor Component', () => {
} );
describe( '#id', () => {
- it( 'should make the editor restart when its value changes', async () => {
+ it( 'should re-mount the editor if the attribute has changed', async () => {
sinon.stub( Editor, 'create' ).callsFake( async () => new Editor() );
const editor = await new Promise( ( res, rej ) => {
@@ -497,7 +500,8 @@ describe( 'CKEditor Component', () => {
onReady={ res }
onError={ rej }
config={ { initialData: 'foo
' } }
- id="1" /> );
+ id="1"
+ /> );
} );
sinon.assert.calledOnce( Editor.create );
@@ -514,14 +518,15 @@ describe( 'CKEditor Component', () => {
expect( editor ).to.not.equal( editor2 );
} );
- it( 'should not make the editor restart when its value does not change', async () => {
+ it( 'should not re-mount the editor if the attribute has not changed', async () => {
await new Promise( ( res, rej ) => {
wrapper = mount( foo' } }
- id="1" /> );
+ id="1"
+ /> );
} );
sinon.stub( Editor, 'create' ).callsFake( async () => new Editor() );
@@ -532,6 +537,47 @@ describe( 'CKEditor Component', () => {
sinon.assert.notCalled( Editor.create );
} );
+
+ it( 'should destroy the old watchdog instance while re-mounting the editor', async () => {
+ await new Promise( ( res, rej ) => {
+ wrapper = mount( foo' } }
+ id="1"
+ /> );
+ } );
+
+ const { watchdog: firstWatchdog } = wrapper.instance();
+
+ await new Promise( res => {
+ wrapper.setProps( { onReady: res, id: '2', config: { initialData: 'bar
' } } );
+ } );
+
+ const { watchdog: secondWatchdog } = wrapper.instance();
+
+ expect( firstWatchdog ).to.not.equal( secondWatchdog );
+ expect( firstWatchdog.state ).to.equal( 'destroyed' );
+ expect( secondWatchdog.state ).to.equal( 'ready' );
+ } );
+ } );
+
+ describe( '#onInit', () => {
+ it( 'should throw an error when using the unsupported property', async () => {
+ const consoleErrorStub = sinon.stub( console, 'error' );
+ const onInit = sinon.spy();
+
+ wrapper = mount( );
+
+ consoleErrorStub.restore();
+
+ expect( onInit.called ).to.equal( false );
+ expect( consoleErrorStub.calledOnce ).to.equal( true );
+ expect( consoleErrorStub.firstCall.args[ 0 ] ).to.match(
+ /The "onInit" property is not supported anymore by the CKEditor component\. Use the "onReady" property instead./
+ );
+ } );
} );
} );
diff --git a/tests/ckeditorcontext.jsx b/tests/ckeditorcontext.jsx
index e63ddff..76cc0b9 100644
--- a/tests/ckeditorcontext.jsx
+++ b/tests/ckeditorcontext.jsx
@@ -11,12 +11,12 @@ import CKEditor from '../src/ckeditor.jsx';
import EditorMock from './_utils/editor.js';
import ContextWatchdog from '@ckeditor/ckeditor5-watchdog/src/contextwatchdog';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
-import turnOffDefaultErrorCatching from './_utils-tests/turnoffdefaulterrorcatching.js';
+import turnOffDefaultErrorCatching from './_utils/turnoffdefaulterrorcatching.js';
import ContextMock from './_utils/context.js';
configure( { adapter: new Adapter() } );
-describe( 'CKEditor Context Component', () => {
+describe( ' Component', () => {
let wrapper;
afterEach( () => {
@@ -39,6 +39,14 @@ describe( 'CKEditor Context Component', () => {
expect( component.contextWatchdog ).to.be.instanceOf( ContextWatchdog );
} );
+ it( 'should not create anything if the layout is not ready', async () => {
+ wrapper = mount( );
+
+ const component = wrapper.instance();
+
+ expect( component.contextWatchdog ).to.equal( null );
+ } );
+
it( 'should render its children', async () => {
wrapper = mount(
@@ -174,7 +182,7 @@ describe( 'CKEditor Context Component', () => {
} );
} );
- describe( 'onReady', () => {
+ describe( '#onReady', () => {
it( 'should be called when all editors are ready', async () => {
const editorReadySpy = sinon.spy();
@@ -195,8 +203,8 @@ describe( 'CKEditor Context Component', () => {
} );
} );
- describe( 'Restarting CKEditor Context with editor', () => {
- it( 'should restart the Context and all editors if the Context id changes', async () => {
+ describe( 'restarting CKEditorContext with nested CKEditor components', () => {
+ it( 'should restart the Context and all editors if the Context#id has changed', async () => {
const oldContext = await new Promise( res => {
wrapper = mount(
@@ -215,5 +223,124 @@ describe( 'CKEditor Context Component', () => {
expect( newContext ).to.not.equal( oldContext );
expect( newContext ).to.be.an.instanceOf( ContextMock );
} );
+
+ it( 'should re-render the entire component when the layout is ready', async () => {
+ wrapper = mount(
+
+
+
+ );
+
+ const context = await new Promise( res => {
+ wrapper.setProps( {
+ onReady: res,
+ isLayoutReady: true
+ } );
+ } );
+
+ expect( context ).to.be.an.instanceOf( ContextMock );
+ expect( wrapper.instance().contextWatchdog ).to.not.equal( null );
+ } );
+
+ it( 'should not re-render the component if layout is not ready after initialization', async () => {
+ const oldContext = await new Promise( res => {
+ wrapper = mount(
+
+
+
+ );
+ } );
+
+ wrapper.setProps( {
+ isLayoutReady: false
+ } );
+
+ const componentInstance = wrapper.instance(); //
+
+ expect( componentInstance.contextWatchdog.context ).to.equal( oldContext );
+ } );
+
+ it( 'should restart the Context and all editors if children has changed', async () => {
+ const oldContext = await new Promise( res => {
+ wrapper = mount(
+
+
+
+ );
+ } );
+
+ const newContext = await new Promise( res => {
+ wrapper.setProps( {
+ onReady: res,
+ children: [
+ // The `key` property is required when defining children this way.
+ // See: https://reactjs.org/docs/lists-and-keys.html#keys.
+ ,
+
+ ]
+ } );
+ } );
+
+ expect( newContext ).to.not.equal( oldContext );
+ expect( newContext ).to.be.an.instanceOf( ContextMock );
+ } );
+ } );
+} );
+
+describe( 'EditorWatchdogAdapter', () => {
+ let wrapper;
+
+ afterEach( () => {
+ sinon.restore();
+
+ if ( wrapper ) {
+ wrapper.unmount();
+ }
+ } );
+
+ describe( '#on', () => {
+ const error = new Error( 'Example error.' );
+
+ it( 'should execute the onError callback if an error was reported by the CKEditorContext component', async () => {
+ const errorSpy = sinon.spy();
+
+ await new Promise( res => {
+ wrapper = mount(
+
+
+
+ );
+ } );
+
+ const { watchdog } = wrapper.childAt( 0 ).instance();
+
+ watchdog._contextWatchdog._fire( 'itemError', { error, itemId: watchdog._id } );
+
+ expect( errorSpy.calledOnce ).to.equal( true );
+ expect( errorSpy.firstCall.args[ 0 ] ).to.equal( error );
+ } );
+
+ it( 'should execute the onError callback for proper editor', async () => {
+ const firstEditorErrorSpy = sinon.spy();
+ const secondEditorErrorSpy = sinon.spy();
+
+ await new Promise( res => {
+ wrapper = mount(
+
+
+
+
+ );
+ } );
+
+ // Report an error for the second editor.
+ const { watchdog } = wrapper.childAt( 1 ).instance();
+
+ watchdog._contextWatchdog._fire( 'itemError', { error, itemId: watchdog._id } );
+
+ expect( firstEditorErrorSpy.called ).to.equal( false );
+ expect( secondEditorErrorSpy.calledOnce ).to.equal( true );
+ expect( secondEditorErrorSpy.firstCall.args[ 0 ] ).to.equal( error );
+ } );
} );
} );
diff --git a/tests/index.jsx b/tests/index.jsx
index ae21673..2fd9f4c 100644
--- a/tests/index.jsx
+++ b/tests/index.jsx
@@ -13,42 +13,44 @@ import { CKEditor, CKEditorContext } from '../dist/ckeditor';
configure( { adapter: new Adapter() } );
-describe( 'index.js - CKEditor', () => {
- let wrapper;
+describe( 'index.js', () => {
+ describe( 'the component', () => {
+ let wrapper;
+
+ afterEach( () => {
+ if ( wrapper ) {
+ wrapper.unmount();
+ }
+ } );
- afterEach( () => {
- if ( wrapper ) {
- wrapper.unmount();
- }
- } );
+ it( 'should be the CKEditor Component', async () => {
+ expect( CKEditor ).to.be.a( 'function' );
- it( 'should be a CKEditor Component', async () => {
- expect( CKEditor ).to.be.a( 'function' );
+ const editor = await new Promise( res => {
+ wrapper = mount( );
+ } );
- const editor = await new Promise( res => {
- wrapper = mount( );
+ expect( editor ).to.be.instanceOf( Editor );
} );
-
- expect( editor ).to.be.instanceOf( Editor );
} );
-} );
-describe( 'index.js - CKEditorContext', () => {
- let wrapper;
+ describe( 'the component', () => {
+ let wrapper;
- afterEach( () => {
- if ( wrapper ) {
- wrapper.unmount();
- }
- } );
+ afterEach( () => {
+ if ( wrapper ) {
+ wrapper.unmount();
+ }
+ } );
- it( 'should be a CKEditorContext Component', async () => {
- expect( CKEditorContext ).to.be.a( 'function' );
+ it( 'should be the CKEditorContext Component', async () => {
+ expect( CKEditorContext ).to.be.a( 'function' );
- const context = await new Promise( res => {
- wrapper = mount( );
- } );
+ const context = await new Promise( res => {
+ wrapper = mount( );
+ } );
- expect( context ).to.be.instanceOf( ContextMock );
+ expect( context ).to.be.instanceOf( ContextMock );
+ } );
} );
} );
diff --git a/tests/integrations/ckeditor-classiceditor.jsx b/tests/integrations/ckeditor-classiceditor.jsx
new file mode 100644
index 0000000..e77d32e
--- /dev/null
+++ b/tests/integrations/ckeditor-classiceditor.jsx
@@ -0,0 +1,160 @@
+/**
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* global ClassicEditor, window, document */
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { configure, mount } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+
+import { CKEditor } from '../..';
+
+configure( { adapter: new Adapter() } );
+
+describe( 'CKEditor Component + ClassicEditor Build', () => {
+ let wrapper;
+
+ afterEach( () => {
+ if ( wrapper ) {
+ wrapper.unmount();
+ }
+ } );
+
+ it( 'should initialize the ClassicEditor properly', async () => {
+ await new Promise( res => {
+ wrapper = mount( );
+ } );
+
+ const component = wrapper.instance();
+
+ expect( component.editor ).to.not.be.null;
+ expect( component.editor.element ).to.not.be.null;
+ } );
+} );
+
+// The memory test based on: https://github.com/ckeditor/ckeditor5/blob/master/packages/ckeditor5-core/tests/_utils/memory.js.
+// It's the simplified, adjusted version that allows checking whether the component destroys all references.
+const TEST_RETRIES = 2;
+const TEST_TIMEOUT = 5000;
+const GARBAGE_COLLECTOR_TIMEOUT = 500;
+
+describe( ' memory usage', () => {
+ const config = {
+ initialData: 'Editor 1
\n' +
+ 'This is an editor instance. And there\'s some link.
'
+ };
+
+ let div;
+
+ // Will skip test suite if tests are run inside incompatible environment:
+ // - No window.gc (only Google Chrome).
+ // - Chrome on Windows (tests heavily break).
+ //
+ // Currently on Google Chrome supports this method and must be run with proper flags:
+ //
+ // google-chrome -js-flags="--expose-gc"
+ //
+ before( function() {
+ if ( !window.gc || isWindows() ) {
+ this.skip();
+ }
+ } );
+
+ // Single test case for memory usage test. Handles the memory leak test procedure.
+ //
+ // 1. Mount and unmount the component to pre-fill the memory with some cacheable data.
+ // 2. Record the heap size.
+ // 3. Mount and unmount the component 5 times.
+ // 4. Record the heap size and compare with the previous result.
+ // 5. Fail when exceeded a 1MB treshold (see code comments for why 1MB).
+ it( 'should not grow on multiple component creations', function() {
+ this.timeout( TEST_TIMEOUT );
+
+ // Unfortunately the tests fails from time to time so retry a failed tests.
+ this.retries( TEST_RETRIES );
+
+ function createEditor() {
+ div = document.createElement( 'div' );
+ document.body.appendChild( div );
+
+ return new Promise( res => {
+ ReactDOM.render( , div );
+ } );
+ }
+
+ function destroyEditor() {
+ return new Promise( res => {
+ ReactDOM.unmountComponentAtNode( div );
+ div.remove();
+
+ res();
+ } );
+ }
+
+ return runTest( createEditor, destroyEditor );
+ } );
+
+ // Runs a single test case.
+ function runTest( createEditor, destroyEditor ) {
+ let memoryAfterFirstStart;
+
+ return Promise
+ .resolve()
+ // Initialize the first editor before measuring the heap size.
+ // A cold start may allocate a bit of memory on the module-level.
+ .then( createAndDestroy )
+ .then( () => {
+ return collectMemoryStats().then( mem => {
+ memoryAfterFirstStart = mem;
+ } );
+ } )
+ // Run create&destroy multiple times. Helps scaling up the issue.
+ .then( createAndDestroy ) // #1
+ .then( createAndDestroy ) // #2
+ .then( createAndDestroy ) // #3
+ .then( createAndDestroy ) // #4
+ .then( createAndDestroy ) // #5
+ .then( collectMemoryStats )
+ .then( memory => {
+ const memoryDifference = memory.usedJSHeapSize - memoryAfterFirstStart.usedJSHeapSize;
+ // While theoretically we should get 0KB when there's no memory leak, in reality,
+ // the results we get (when there are no leaks) vary from -500KB to 500KB (depending on which tests are executed).
+ // However, when we had memory leaks, memoryDifference was reaching 20MB,
+ // so, in order to detect significant memory leaks we can expect that the heap won't grow more than 1MB.
+ expect( memoryDifference, 'used heap size should not grow' ).to.be.at.most( 1e6 );
+ } );
+
+ function createAndDestroy() {
+ return Promise.resolve()
+ .then( createEditor )
+ .then( destroyEditor );
+ }
+ }
+
+ function collectMemoryStats() {
+ return new Promise( resolve => {
+ // Enforce garbage collection before recording memory stats.
+ window.gc();
+
+ setTimeout( () => {
+ const memeInfo = window.performance.memory;
+
+ resolve( {
+ totalJSHeapSize: memeInfo.totalJSHeapSize,
+ usedJSHeapSize: memeInfo.usedJSHeapSize,
+ jsHeapSizeLimit: memeInfo.jsHeapSizeLimit
+ } );
+ }, GARBAGE_COLLECTOR_TIMEOUT );
+ } );
+ }
+
+ // The windows environment does not cooperate with this tests.
+ function isWindows() {
+ const userAgent = window.navigator.userAgent.toLowerCase();
+
+ return userAgent.indexOf( 'windows' ) > -1;
+ }
+} );
diff --git a/tests/ckeditor-integration.jsx b/tests/integrations/ckeditor-editor-data.jsx
similarity index 98%
rename from tests/ckeditor-integration.jsx
rename to tests/integrations/ckeditor-editor-data.jsx
index 5d9a1b8..840a0e3 100644
--- a/tests/ckeditor-integration.jsx
+++ b/tests/integrations/ckeditor-editor-data.jsx
@@ -11,7 +11,7 @@ import ReactDOM from 'react-dom';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
-import CKEditor from '../src/ckeditor.jsx';
+import CKEditor from '../../src/ckeditor.jsx';
configure( { adapter: new Adapter() } );
diff --git a/tests/integrations/39-frozen browser.jsx b/tests/issues/39-frozen browser.jsx
similarity index 91%
rename from tests/integrations/39-frozen browser.jsx
rename to tests/issues/39-frozen browser.jsx
index e418629..fbd850c 100644
--- a/tests/integrations/39-frozen browser.jsx
+++ b/tests/issues/39-frozen browser.jsx
@@ -44,7 +44,7 @@ class App extends React.Component {
}
}
-describe( '#37 - bug: a browser is being froze', () => {
+describe( 'issue #37: the browser is being frozen', () => {
let div, component;
beforeEach( () => {
@@ -60,7 +60,7 @@ describe( '#37 - bug: a browser is being froze', () => {
div.remove();
} );
- it( 'if "data" property is not specified, a browser should not be freeze', () => {
+ it( 'if the "#data" property is not specified, the browser should not freeze', () => {
const editor = component.editor;
editor.model.change( writer => {
diff --git a/yarn.lock b/yarn.lock
index dace7ab..47de7cd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9,7 +9,7 @@
dependencies:
"@babel/highlight" "^7.10.4"
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658"
integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==
@@ -234,21 +234,6 @@
"@babel/plugin-transform-react-jsx-development" "^7.12.12"
"@babel/plugin-transform-react-pure-annotations" "^7.12.1"
-"@babel/runtime-corejs3@^7.10.2":
- version "7.13.9"
- resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.13.9.tgz#b2fa9a6e5690ef8d4c4f2d30cac3ec1a8bb633ce"
- integrity sha512-p6WSr71+5u/VBf1KDS/Y4dK3ZwbV+DD6wQO3X2EbUVluEOiyXUk09DzcwSaUH4WomYXrEPC+i2rqzuthhZhOJw==
- dependencies:
- core-js-pure "^3.0.0"
- regenerator-runtime "^0.13.4"
-
-"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5":
- version "7.13.9"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.9.tgz#97dbe2116e2630c489f22e0656decd60aaa1fcee"
- integrity sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==
- dependencies:
- regenerator-runtime "^0.13.4"
-
"@babel/template@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
@@ -366,17 +351,6 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
-"@jest/types@^26.6.2":
- version "26.6.2"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
- integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
- dependencies:
- "@types/istanbul-lib-coverage" "^2.0.0"
- "@types/istanbul-reports" "^3.0.0"
- "@types/node" "*"
- "@types/yargs" "^15.0.0"
- chalk "^4.0.0"
-
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -563,33 +537,6 @@
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
-"@testing-library/dom@^7.28.1":
- version "7.30.0"
- resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.30.0.tgz#53697851f7708a1448cc30b74a2ea056dd709cd6"
- integrity sha512-v4GzWtltaiDE0yRikLlcLAfEiiK8+ptu6OuuIebm9GdC2XlZTNDPGEfM2UkEtnH7hr9TRq2sivT5EA9P1Oy7bw==
- dependencies:
- "@babel/code-frame" "^7.10.4"
- "@babel/runtime" "^7.12.5"
- "@types/aria-query" "^4.2.0"
- aria-query "^4.2.2"
- chalk "^4.1.0"
- dom-accessibility-api "^0.5.4"
- lz-string "^1.4.4"
- pretty-format "^26.6.2"
-
-"@testing-library/react@^11.2.5":
- version "11.2.5"
- resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.5.tgz#ae1c36a66c7790ddb6662c416c27863d87818eb9"
- integrity sha512-yEx7oIa/UWLe2F2dqK0FtMF9sJWNXD+2PPtp39BvE0Kh9MJ9Kl0HrZAgEuhUJR+Lx8Di6Xz+rKwSdEPY2UV8ZQ==
- dependencies:
- "@babel/runtime" "^7.12.5"
- "@testing-library/dom" "^7.28.1"
-
-"@types/aria-query@^4.2.0":
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.1.tgz#78b5433344e2f92e8b306c06a5622c50c245bf6b"
- integrity sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==
-
"@types/glob@^7.1.1":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
@@ -598,25 +545,6 @@
"@types/minimatch" "*"
"@types/node" "*"
-"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
- integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==
-
-"@types/istanbul-lib-report@*":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686"
- integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
- dependencies:
- "@types/istanbul-lib-coverage" "*"
-
-"@types/istanbul-reports@^3.0.0":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821"
- integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==
- dependencies:
- "@types/istanbul-lib-report" "*"
-
"@types/json-schema@^7.0.5":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
@@ -652,18 +580,6 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
-"@types/yargs-parser@*":
- version "20.2.0"
- resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9"
- integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==
-
-"@types/yargs@^15.0.0":
- version "15.0.13"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc"
- integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==
- dependencies:
- "@types/yargs-parser" "*"
-
"@ungap/promise-all-settled@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
@@ -1029,14 +945,6 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
-aria-query@^4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
- integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
- dependencies:
- "@babel/runtime" "^7.10.2"
- "@babel/runtime-corejs3" "^7.10.2"
-
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@@ -2204,11 +2112,6 @@ copy-descriptor@^0.1.0:
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
-core-js-pure@^3.0.0:
- version "3.9.1"
- resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.9.1.tgz#677b322267172bd490e4464696f790cbc355bec5"
- integrity sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==
-
core-js@^2.4.0:
version "2.6.12"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
@@ -2705,11 +2608,6 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
-dom-accessibility-api@^0.5.4:
- version "0.5.4"
- resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz#b06d059cdd4a4ad9a79275f9d414a5c126241166"
- integrity sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==
-
dom-serialize@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
@@ -5231,11 +5129,6 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
-lz-string@^1.4.4:
- version "1.4.4"
- resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
- integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
-
macos-release@^2.2.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac"
@@ -6564,16 +6457,6 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
-pretty-format@^26.6.2:
- version "26.6.2"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
- integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
- dependencies:
- "@jest/types" "^26.6.2"
- ansi-regex "^5.0.0"
- ansi-styles "^4.0.0"
- react-is "^17.0.1"
-
process-nextick-args@^2.0.0, process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -6784,11 +6667,6 @@ react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.6:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
-react-is@^17.0.1:
- version "17.0.1"
- resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
- integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
-
react-test-renderer@^16.0.0-0:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae"
@@ -6897,11 +6775,6 @@ regenerator-runtime@^0.11.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
-regenerator-runtime@^0.13.4:
- version "0.13.7"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
- integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
-
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"