From af616aa2c2fe5df5e621059b6f1988f3b823e57f Mon Sep 17 00:00:00 2001 From: Przemyslaw Zan Date: Fri, 3 Feb 2023 13:34:58 +0100 Subject: [PATCH] Added unit tests. --- src/app/demo-form/demo-form.component.html | 9 +++---- src/app/demo-form/demo-form.component.spec.ts | 26 +++++++++++++++++++ src/app/demo-form/demo-form.component.ts | 3 ++- .../demo-reactive-form.component.html | 15 +++++++++-- .../demo-reactive-form.component.spec.ts | 26 +++++++++++++++++++ .../demo-reactive-form.component.ts | 16 ++++++++++++ src/ckeditor/ckeditor.component.spec.ts | 25 ++++++++++++++---- src/ckeditor/ckeditor.component.ts | 5 ++-- 8 files changed, 110 insertions(+), 15 deletions(-) diff --git a/src/app/demo-form/demo-form.component.html b/src/app/demo-form/demo-form.component.html index 7083430..96aadff 100644 --- a/src/app/demo-form/demo-form.component.html +++ b/src/app/demo-form/demo-form.component.html @@ -27,19 +27,18 @@

User profile form

Description has been "touched".

- - Two-way data binding is {{ shouldDisableTwoWayDataBinding ? 'disabled' : 'enabled' }}. + +  Two-way data binding is {{ shouldDisableTwoWayDataBinding ? 'disabled' : 'enabled' }}.

- (Open the console first) + +  (Open the console first)

- -

Editor data preview (readable and writable)

Note that it's only a prove of concept of using the `ngModel`. diff --git a/src/app/demo-form/demo-form.component.spec.ts b/src/app/demo-form/demo-form.component.spec.ts index 59ed42d..38f0d2b 100644 --- a/src/app/demo-form/demo-form.component.spec.ts +++ b/src/app/demo-form/demo-form.component.spec.ts @@ -59,4 +59,30 @@ describe( 'DemoFormComponent', () => { done(); } ); } ); + + it( 'should assign editor data to the model description if two way binding is disabled', done => { + setTimeout( () => { + const spy = spyOn( console, 'log' ); + + const toggleButton: HTMLButtonElement = fixture.debugElement.query( By.css( '#toggleBinding' ) ).nativeElement; + const submitButton: HTMLButtonElement = fixture.debugElement.query( By.css( 'button[type=submit]' ) ).nativeElement; + const editorDataPreview: HTMLTextAreaElement = fixture.debugElement.query( By.css( 'textarea' ) ).nativeElement; + + toggleButton.click(); + component.editorInstance.setData( '

Foo bar baz.

' ); + submitButton.click(); + + expect( editorDataPreview.value ).toEqual( '

A really nice fellow.

' ); + + expect( spy ).toHaveBeenCalledTimes( 1 ); + expect( spy.calls.first().args[ 0 ] ).toEqual( 'Form submit, model' ); + expect( spy.calls.first().args[ 1 ] ).toEqual( { + name: 'John', + surname: 'Doe', + description: '

Foo bar baz.

' + } ); + + done(); + } ); + } ); } ); diff --git a/src/app/demo-form/demo-form.component.ts b/src/app/demo-form/demo-form.component.ts index 702bdb2..83eac09 100644 --- a/src/app/demo-form/demo-form.component.ts +++ b/src/app/demo-form/demo-form.component.ts @@ -26,10 +26,11 @@ export class DemoFormComponent implements AfterViewInit { surname: 'Doe', description: '

A really nice fellow.

' }; + public formDataPreview?: string; public shouldDisableTwoWayDataBinding = false; - protected editorInstance: typeof ClassicEditor; + public editorInstance: typeof ClassicEditor; public get description(): AbstractControl { return this.demoForm!.controls.description; diff --git a/src/app/demo-reactive-form/demo-reactive-form.component.html b/src/app/demo-reactive-form/demo-reactive-form.component.html index ad9753f..5d50222 100644 --- a/src/app/demo-reactive-form/demo-reactive-form.component.html +++ b/src/app/demo-reactive-form/demo-reactive-form.component.html @@ -16,7 +16,9 @@

User profile form

@@ -24,8 +26,17 @@

User profile form

Description is "dirty".

Description has been "touched".

-

-

(Open the console first)

+

+ +  Two-way data binding is {{ shouldDisableTwoWayDataBinding ? 'disabled' : 'enabled' }}. +

+

+ +

+

+ +  (Open the console first) +

Editor data preview (readable)

diff --git a/src/app/demo-reactive-form/demo-reactive-form.component.spec.ts b/src/app/demo-reactive-form/demo-reactive-form.component.spec.ts index 55b9602..048aacb 100644 --- a/src/app/demo-reactive-form/demo-reactive-form.component.spec.ts +++ b/src/app/demo-reactive-form/demo-reactive-form.component.spec.ts @@ -59,4 +59,30 @@ describe( 'DemoReactiveFormComponent', () => { done(); } ); } ); + + it( 'should assign editor data to the model description if two way binding is disabled', done => { + setTimeout( () => { + const spy = spyOn( console, 'log' ); + + const toggleButton: HTMLButtonElement = fixture.debugElement.query( By.css( '#toggleBinding' ) ).nativeElement; + const submitButton: HTMLButtonElement = fixture.debugElement.query( By.css( 'button[type=submit]' ) ).nativeElement; + const editorDataPreview: HTMLTextAreaElement = fixture.debugElement.query( By.css( 'textarea' ) ).nativeElement; + + toggleButton.click(); + component.editorInstance.setData( '

Foo bar baz.

' ); + submitButton.click(); + + expect( editorDataPreview.value ).toEqual( '

A really nice fellow.

' ); + + expect( spy ).toHaveBeenCalledTimes( 1 ); + expect( spy.calls.first().args[ 0 ] ).toEqual( 'Form submit, model' ); + expect( spy.calls.first().args[ 1 ] ).toEqual( { + name: 'John', + surname: 'Doe', + description: '

Foo bar baz.

' + } ); + + done(); + } ); + } ); } ); diff --git a/src/app/demo-reactive-form/demo-reactive-form.component.ts b/src/app/demo-reactive-form/demo-reactive-form.component.ts index d865d8e..85b1c36 100644 --- a/src/app/demo-reactive-form/demo-reactive-form.component.ts +++ b/src/app/demo-reactive-form/demo-reactive-form.component.ts @@ -26,6 +26,13 @@ export class DemoReactiveFormComponent implements AfterViewInit { } ); public formDataPreview?: string; + public shouldDisableTwoWayDataBinding = false; + + public editorInstance: typeof ClassicEditor; + + public toggleDisableTwoWayDataBinding(): void { + this.shouldDisableTwoWayDataBinding = !this.shouldDisableTwoWayDataBinding; + } public ngAfterViewInit(): void { this.demoReactiveForm!.valueChanges @@ -34,7 +41,16 @@ export class DemoReactiveFormComponent implements AfterViewInit { } ); } + public onReady( editor: typeof ClassicEditor ): void { + this.editorInstance = editor; + } + public onSubmit(): void { + // Read editor's data only when two-way data binding is disabled + if ( this.shouldDisableTwoWayDataBinding ) { + this.demoReactiveForm.value.description = this.editorInstance.getData(); + } + console.log( 'Form submit, model', this.demoReactiveForm.value ); } diff --git a/src/ckeditor/ckeditor.component.spec.ts b/src/ckeditor/ckeditor.component.spec.ts index a491546..5da2104 100644 --- a/src/ckeditor/ckeditor.component.spec.ts +++ b/src/ckeditor/ckeditor.component.spec.ts @@ -61,7 +61,7 @@ describe( 'CKEditorComponent', () => { expect( console.warn ).toHaveBeenCalledWith( 'The component requires using CKEditor 5 in version 34 or higher.' ); } ); - it( 'should not print any warninig if using CKEditor 5 in version 34 or higher', () => { + it( 'should not print any warning if using CKEditor 5 in version 34 or higher', () => { window.CKEDITOR_VERSION = '34.0.0'; fixture = TestBed.createComponent( CKEditorComponent ); @@ -220,9 +220,8 @@ describe( 'CKEditorComponent', () => { it( 'change - should not calculate editor data when the control value ancestor is not specified', () => { fixture.detectChanges(); - - const changeSpy = jasmine.createSpy(); - component.change.subscribe( changeSpy ); + const spy = jasmine.createSpy(); + component.change.subscribe( spy ); return waitCycle().then( () => { spyOn( component.editorInstance!, 'getData' ).and.callThrough(); @@ -232,7 +231,23 @@ describe( 'CKEditorComponent', () => { component.editorInstance!.execute( 'input', { text: 'foo' } ); expect( component.editorInstance!.getData ).toHaveBeenCalledTimes( 0 ); - expect( changeSpy ).toHaveBeenCalledTimes( 3 ); + expect( spy ).toHaveBeenCalledTimes( 3 ); + } ); + } ); + + it( 'change - should not calculate editor data when the two way data binding is disabled', () => { + component.disableTwoWayDataBinding = true; + + fixture.detectChanges(); + const spy = jasmine.createSpy(); + component.change.subscribe( spy ); + + return waitCycle().then( () => { + spyOn( component.editorInstance!, 'getData' ).and.callThrough(); + + component.editorInstance!.execute( 'input', { text: 'foo' } ); + + expect( spy ).toHaveBeenCalledTimes( 0 ); } ); } ); diff --git a/src/ckeditor/ckeditor.component.ts b/src/ckeditor/ckeditor.component.ts index 432a14a..9edffe7 100644 --- a/src/ckeditor/ckeditor.component.ts +++ b/src/ckeditor/ckeditor.component.ts @@ -103,9 +103,10 @@ export class CKEditorComponent implements AfterViewInit, OnDestroy, ControlValue @Input() public watchdog?: CKEditor5.ContextWatchdog; /** - * Allows disabling the two-way data binding mechanism. It can boosts performance for large documents if set to `true`. + * Allows disabling the two-way data binding mechanism. Disabling it can boost performance for large documents. * - * When a component is connected using the [(ngModel)] or [formControl] directives then all data will be never synchronized. + * When a component is connected using the [(ngModel)] or [formControl] directives and this value is set to true then none of the data + * will ever be synchronized. * * An integrator must call `editor.getData()` manually once the application needs the editor's data. * An editor instance can be received in the `ready()` callback.