Skip to content

Commit

Permalink
Add cloud tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Mati365 committed Aug 14, 2024
1 parent c5cf829 commit 7190c82
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 24 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,20 @@
},
"dependencies": {
"prop-types": "^15.7.2",
"@ckeditor/ckeditor5-integrations-common": ">=1.0.0 || ^0.0.0-nightly"
"@ckeditor/ckeditor5-integrations-common": "file:../ckeditor5-integrations-common"
},
"peerDependencies": {
"ckeditor5": ">=42.0.0 || ^0.0.0-nightly",
"ckeditor5-premium-features": ">=42.0.0 || ^0.0.0-nightly",
"@ckeditor/ckeditor5-integrations-common": "file:../ckeditor5-integrations-common",
"react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.8",
"@ckeditor/ckeditor5-dev-bump-year": "^40.0.0",
"@ckeditor/ckeditor5-dev-ci": "^40.0.0",
"@ckeditor/ckeditor5-dev-release-tools": "^40.0.0",
"@ckeditor/ckeditor5-dev-utils": "^40.0.0",
"@ckeditor/ckeditor5-integrations-common": "file:../ckeditor5-integrations-common",
"@testing-library/dom": "^10.3.1",
"@testing-library/react": "^16.0.0",
"@types/react": "^18.0.0",
Expand Down
19 changes: 8 additions & 11 deletions src/cloud/withCKEditorCloud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,21 @@ const withCKEditorCloud = <A extends CKExternalPluginsMap>( config: CKEditorClou
const ckeditorCloudResult = useCKEditorCloud( config.cloud );

switch ( ckeditorCloudResult.status ) {
// The cloud information is being fetched.
case 'idle':
case 'loading':
return config.renderLoader?.() ?? null;

// An error occurred while fetching the cloud information.
case 'error':
return config.renderError?.( ckeditorCloudResult.error ) ?? null;
if ( !config.renderError ) {
return 'Unable to load CKEditor Cloud data!';
}

return config.renderError( ckeditorCloudResult.error );

// The cloud information has been fetched successfully.
case 'success':
return <WrappedComponent {...props as P} cloud={ ckeditorCloudResult } />;

default: {
const unknownResult: never = ckeditorCloudResult;
console.warn( 'Unknown result of cloud integration:', unknownResult );
return null;
}
// The cloud information is being fetched.
default:
return config.renderLoader?.() ?? null;
}
};

Expand Down
40 changes: 40 additions & 0 deletions tests/_utils/cloud.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

import { CK_CDN_URL, INJECTED_SCRIPTS, INJECTED_STYLESHEETS } from '@ckeditor/ckeditor5-integrations-common';

export const removeCkCdnScripts = (): void => {
[ ...document.querySelectorAll( 'script' ) ]
.filter( script => script.src.startsWith( CK_CDN_URL ) )
.forEach( script => {
INJECTED_SCRIPTS.delete( script.src );
script.remove();
} );

// Clear the CKEditor global variables
delete window.CKEDITOR;
delete window.CKEDITOR_PREMIUM_FEATURES;
window.CKEDITOR_VERSION = '';
};

/**
* Removes all CKEditor stylesheets and preloads from the DOM.
*/
export const removeCkCdnLinks = (): void => {
[ ...document.querySelectorAll( 'link' ) ]
.filter( link => link.href.startsWith( CK_CDN_URL ) )
.forEach( link => {
INJECTED_STYLESHEETS.delete( link.href );
link.remove();
} );
};

/**
* Removes all CKEditor scripts and stylesheets from the DOM.
*/
export const removeInjectedCdnStuff = (): void => {
removeCkCdnScripts();
removeCkCdnLinks();
};
8 changes: 1 addition & 7 deletions tests/ckeditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ import { expectToBeTruthy } from './_utils/expectToBeTruthy.js';
import type { LifeCycleElementSemaphore } from '../src/lifecycle/LifeCycleElementSemaphore.js';
import type { EditorSemaphoreMountResult } from '../src/lifecycle/LifeCycleEditorSemaphore.js';

declare global {
interface Window {
CKEDITOR_VERSION: any;
}
}

const MockEditor = MockedEditor as any;

describe( '<CKEditor> Component', () => {
Expand Down Expand Up @@ -59,7 +53,7 @@ describe( '<CKEditor> Component', () => {

describe( 'initialization', async () => {
it( 'should print a warning if the "window.CKEDITOR_VERSION" variable is not available', async () => {
delete window.CKEDITOR_VERSION;
window.CKEDITOR_VERSION = '';
const warnStub = vi.spyOn( console, 'warn' ).mockImplementation( () => {} );

component = render(
Expand Down
72 changes: 72 additions & 0 deletions tests/cloud/useCKEditorCloud.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

import { beforeEach, describe, expect, it } from 'vitest';
import { renderHook, waitFor, act } from '@testing-library/react';

import type { CKEditorCloudConfig } from '@ckeditor/ckeditor5-integrations-common';

import { removeInjectedCdnStuff } from '../_utils/cloud';
import useCKEditorCloud from '../../src/cloud/useCKEditorCloud';

describe( 'useCKEditorCloud', () => {
beforeEach( removeInjectedCdnStuff );

it( 'should load CKEditor bundles from CDN', async () => {
const { result } = renderHook( () => useCKEditorCloud( {
version: '43.0.0',
languages: [ 'en', 'de' ]
} ) );

expect( result.current.status ).toBe( 'loading' );

await waitFor( () => {
expect( result.current.status ).toBe( 'success' );

if ( result.current.status === 'success' ) {
expect( result.current.CKEditor ).toBeDefined();
}
} );
} );

it( 'should load additional bundle after updating deps', async () => {
const { result, rerender } = renderHook(
( config: CKEditorCloudConfig<any> ) => useCKEditorCloud( config ),
{
initialProps: {
version: '43.0.0',
withPremiumFeatures: false
}
}
);

await waitFor( () => {
expect( result.current.status ).toBe( 'success' );

if ( result.current.status === 'success' ) {
expect( result.current.CKEditor ).toBeDefined();
expect( result.current.CKEditorPremiumFeatures ).toBeUndefined();
}
} );

rerender( {
version: '43.0.0',
withPremiumFeatures: true
} );

act( () => {
expect( result.current.status ).toBe( 'loading' );
} );

await waitFor( () => {
expect( result.current.status ).toBe( 'success' );

if ( result.current.status === 'success' ) {
expect( result.current.CKEditor ).toBeDefined();
expect( result.current.CKEditorPremiumFeatures ).toBeDefined();
}
} );
} );
} );
118 changes: 118 additions & 0 deletions tests/cloud/withCKEditorCloud.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

import React, { type MutableRefObject } from 'react';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { cleanup, render } from '@testing-library/react';
import { createDefer } from '@ckeditor/ckeditor5-integrations-common';

import { removeInjectedCdnStuff } from '../_utils/cloud';
import withCKEditorCloud, { type WithCKEditorCloudHocProps } from '../../src/cloud/withCKEditorCloud';

describe( 'withCKEditorCloud', () => {
const lastRenderedMockProps: MutableRefObject<WithCKEditorCloudHocProps | null> = {
current: null
};

afterEach( cleanup );

beforeEach( () => {
removeInjectedCdnStuff();
lastRenderedMockProps.current = null;
} );

const MockComponent = ( props: WithCKEditorCloudHocProps & { editorId: number } ) => {
lastRenderedMockProps.current = { ...props };

return (
<div>
Your Editor { props.editorId }
</div>
);
};

it( 'should inject cloud integration to the wrapped component', async () => {
const WrappedComponent = withCKEditorCloud( {
cloud: {
version: '43.0.0'
}
} )( MockComponent );

const { findByText } = render( <WrappedComponent editorId={ 1 } /> );

expect( await findByText( 'Your Editor 1' ) ).toBeVisible();
expect( lastRenderedMockProps.current ).toMatchObject( {
editorId: 1,
cloud: expect.objectContaining( {
CKEditor: expect.objectContaining( {
ClassicEditor: expect.any( Function )
} )
} )
} );
} );

it( 'should show loading spinner when cloud is not ready', async () => {
const deferredPlugin = createDefer<number>();
const WrappedComponent = withCKEditorCloud( {
renderLoader: () => <div>Loading...</div>,
cloud: {
version: '43.0.0',
plugins: {
Plugin: {
getExportedEntries: () => deferredPlugin.promise
}
}
}
} )( MockComponent );

const { findByText } = render( <WrappedComponent editorId={ 1 } /> );

expect( await findByText( 'Loading...' ) ).toBeVisible();

deferredPlugin.resolve( 123 );

expect( await findByText( 'Your Editor 1' ) ).toBeVisible();
expect( lastRenderedMockProps.current?.cloud.CKPlugins?.Plugin ).toBe( 123 );
} );

it( 'should show error message when cloud loading fails', async () => {
const WrappedComponent = withCKEditorCloud( {
renderError: error => <div>Error: { error.message }</div>,
cloud: {
version: '43.0.0',
plugins: {
Plugin: {
getExportedEntries: () => {
throw new Error( 'Failed to load plugin' );
}
}
}
}
} )( MockComponent );

const { findByText } = render( <WrappedComponent editorId={ 1 } /> );

expect( await findByText( 'Error: Failed to load plugin' ) ).toBeVisible();
} );

it( 'should render default error message when cloud loading fails and there is no error handler specified', async () => {
const WrappedComponent = withCKEditorCloud( {
cloud: {
version: '43.0.0',
plugins: {
Plugin: {
getExportedEntries: () => {
throw new Error( 'Failed to load plugin' );
}
}
}
}
} )( MockComponent );

const { findByText } = render( <WrappedComponent editorId={ 1 } /> );

expect( await findByText( 'Unable to load CKEditor Cloud data!' ) ).toBeVisible();
} );
} );
1 change: 1 addition & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default defineConfig( {

// https://vitest.dev/config/
test: {
setupFiles: [ './vitest-setup.ts' ],
include: [
'tests/**/*.test.[j|t]sx'
],
Expand Down
6 changes: 6 additions & 0 deletions vitest-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

import '@testing-library/jest-dom/vitest';
Loading

0 comments on commit 7190c82

Please sign in to comment.