Skip to content

Commit 346c355

Browse files
INT-3396: adjust tests to support both zoneless/zone.js change detection (#463)
* INT-3396: remove zoneless readme * INT-3396: enable zoneless in tests * INT-3396: mark test file as zonefull * INT-3396: adjust event blacklisting test * INT-3396: simplify ng zone tests * INT-3396: ensure default zoneless change detection in tests
1 parent 4448762 commit 346c355

4 files changed

Lines changed: 71 additions & 45 deletions

File tree

README.md

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,8 @@ This package is a thin wrapper around [TinyMCE](https://github.com/tinymce/tinym
2020
|<= 8 |3.x |
2121
|< 5 | Not supported |
2222

23-
### Not yet Zoneless ( >=Angular v21 )
24-
* This wrapper still requires `zone.js` to ensure backward compatibility to older Angular versions. Therefore, if your application uses Angular v21 or higher, it needs to include `provideZoneDetection()` in its providers.
25-
26-
```jsx
27-
import { NgModule, provideZoneChangeDetection } from '@angular/core';
28-
29-
@NgModule({
30-
declarations: [
31-
// ...
32-
],
33-
imports: [
34-
// ...
35-
],
36-
providers: [ provideZoneChangeDetection() ],
37-
bootstrap: [ AppComponent ]
38-
})
39-
```
23+
### Zoneless Support
24+
This wrapper supports Angular's zoneless change detection. No additional configuration is needed — the component works with both zone-based and zoneless applications.
4025

4126
### Issues
4227

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,41 @@
11
import 'core-js/features/reflect';
2+
3+
// zone.js is imported here because our test suite needs to cover both zoneless and
4+
// zone.js-based Angular applications. As a component library, our users may run
5+
// either mode, so we must ensure compatibility with both. Since Angular 21, zoneless
6+
// is the default, but zone.js remains supported. Once Angular drops zone.js support
7+
// entirely, this import, ng-zone specific tests and the zone.js devDependency can be removed.
8+
//
9+
// Note: importing zone.js patches native browser APIs (addEventListener, setTimeout,
10+
// setInterval, etc.), but Angular does not use these patches for change detection by
11+
// default. Change detection only relies on zone.js in tests that explicitly configure
12+
// `provideZoneChangeDetection`.
213
import 'zone.js';
314
import 'zone.js/plugins/fake-async-test';
415

516
import { TestBed } from '@angular/core/testing';
617
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
7-
import { NgModule, provideZoneChangeDetection } from '@angular/core';
18+
import { NgModule, provideZonelessChangeDetection } from '@angular/core';
819

20+
// According to Angular docs, TestBed uses zone-based change detection by default
21+
// when zone.js is loaded via polyfills:
22+
// https://angular.dev/guide/zoneless#testing-and-debugging
23+
//
24+
// In practice, this behaviour seems to be driven by Angular's built-in test runners
25+
// (Karma, Vitest) rather than TestBed itself. Since we use Bedrock, we appear to be
26+
// immune to this — zone-based detection does not kick in automatically even with
27+
// zone.js loaded. Nonetheless, we explicitly opt into zoneless change detection here
28+
// to stay aligned with the Angular documentation and to be safe. Zone.js-specific
29+
// tests can override this with `provideZoneChangeDetection` on a per-test basis.
930
@NgModule({
10-
providers: [ provideZoneChangeDetection() ],
31+
providers: [ provideZonelessChangeDetection() ],
1132
})
1233
class AppTestingModule {}
1334

1435
TestBed.initTestEnvironment(
15-
[ BrowserTestingModule, AppTestingModule ], platformBrowserTesting(),
36+
[ BrowserTestingModule, AppTestingModule ],
37+
platformBrowserTesting(),
1638
{
17-
teardown: { destroyAfterEach: true },
39+
teardown: { destroyAfterEach: true }
1840
}
1941
);
Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import '../alien/InitTestEnvironment';
22

3-
import { describe, it } from '@ephox/bedrock-client';
3+
import { context, describe, it } from '@ephox/bedrock-client';
44

55
import { EditorComponent } from '../../../main/ts/public_api';
6-
import { eachVersionContext, editorHook } from '../alien/TestHooks';
7-
import { map, merge, timer, first, buffer, Observable, tap, firstValueFrom } from 'rxjs';
8-
import { NgZone } from '@angular/core';
6+
import { eachVersionContext, EditorFixture, editorHook } from '../alien/TestHooks';
7+
import { map, merge, timer, first, buffer, Observable, tap, firstValueFrom, identity } from 'rxjs';
8+
import { NgZone, provideZoneChangeDetection } from '@angular/core';
99
import { Assertions } from '@ephox/agar';
1010
import { Fun } from '@ephox/katamari';
1111
import { throwTimeout } from '../alien/TestHelpers';
@@ -16,28 +16,47 @@ describe('EventBlacklistingTest', () => {
1616
tap(() => Assertions.assertEq('Subscribers to events should run within NgZone', true, NgZone.isInAngularZone()))
1717
);
1818

19+
const testEventsShouldBeBoundWhenAllowed = async (fixture: EditorFixture<EditorComponent>, isZoneless: boolean) => {
20+
const pEventsCompleted = firstValueFrom(
21+
merge(
22+
fixture.editorComponent.onKeyUp.pipe(map(Fun.constant('onKeyUp')), isZoneless ? identity : shouldRunInAngularZone),
23+
fixture.editorComponent.onKeyDown.pipe(map(Fun.constant('onKeyDown')), isZoneless ? identity : shouldRunInAngularZone),
24+
fixture.editorComponent.onClick.pipe(map(Fun.constant('onClick')), isZoneless ? identity : shouldRunInAngularZone)
25+
).pipe(throwTimeout(10000, 'Timed out waiting for some event to fire'), buffer(timer(100)), first())
26+
);
27+
fixture.editor.fire('keydown');
28+
fixture.editor.fire('keyclick');
29+
fixture.editor.fire('keyup');
30+
const eventsCompleted = await pEventsCompleted;
31+
Assertions.assertEq('Only one event should have fired', 1, eventsCompleted.length);
32+
Assertions.assertEq('Only keyup should fire', 'onKeyUp', eventsCompleted[0]);
33+
};
34+
1935
eachVersionContext([ '4', '5', '6', '7', '8' ], () => {
20-
const createFixture = editorHook(EditorComponent);
36+
context('zoneless', () => {
37+
const createFixture = editorHook(EditorComponent);
38+
const isZoneless = true;
2139

22-
it('Events should be bound when allowed', async () => {
23-
const fixture = await createFixture({
24-
allowedEvents: 'onKeyUp,onClick,onInit',
25-
ignoreEvents: 'onClick',
40+
it('Events should be bound when allowed', async () => {
41+
const fixture = await createFixture({
42+
allowedEvents: 'onKeyUp,onClick,onInit',
43+
ignoreEvents: 'onClick',
44+
});
45+
await testEventsShouldBeBoundWhenAllowed(fixture, isZoneless);
2646
});
47+
});
2748

28-
const pEventsCompleted = firstValueFrom(
29-
merge(
30-
fixture.editorComponent.onKeyUp.pipe(map(Fun.constant('onKeyUp')), shouldRunInAngularZone),
31-
fixture.editorComponent.onKeyDown.pipe(map(Fun.constant('onKeyDown')), shouldRunInAngularZone),
32-
fixture.editorComponent.onClick.pipe(map(Fun.constant('onClick')), shouldRunInAngularZone)
33-
).pipe(throwTimeout(10000, 'Timed out waiting for some event to fire'), buffer(timer(100)), first())
34-
);
35-
fixture.editor.fire('keydown');
36-
fixture.editor.fire('keyclick');
37-
fixture.editor.fire('keyup');
38-
const eventsCompleted = await pEventsCompleted;
39-
Assertions.assertEq('Only one event should have fired', 1, eventsCompleted.length);
40-
Assertions.assertEq('Only keyup should fire', 'onKeyUp', eventsCompleted[0]);
49+
context('with zone.js', () => {
50+
const createFixture = editorHook(EditorComponent, { providers: [ provideZoneChangeDetection() ] });
51+
const isZoneless = false;
52+
53+
it('Events should be bound when allowed', async () => {
54+
const fixture = await createFixture({
55+
allowedEvents: 'onKeyUp,onClick,onInit',
56+
ignoreEvents: 'onClick',
57+
});
58+
await testEventsShouldBeBoundWhenAllowed(fixture, isZoneless);
59+
});
4160
});
4261
});
4362
});

tinymce-angular-component/src/test/ts/browser/NgZoneTest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import '../alien/InitTestEnvironment';
22

3-
import { NgZone } from '@angular/core';
3+
import { NgZone, provideZoneChangeDetection } from '@angular/core';
44
import { Assertions } from '@ephox/agar';
55
import { describe, it } from '@ephox/bedrock-client';
66

@@ -11,7 +11,7 @@ import { throwTimeout } from '../alien/TestHelpers';
1111

1212
describe('NgZoneTest', () => {
1313
eachVersionContext([ '4', '5', '6', '7', '8' ], () => {
14-
const createFixture = fixtureHook(EditorComponent, { imports: [ EditorComponent ] });
14+
const createFixture = fixtureHook(EditorComponent, { imports: [ EditorComponent ], providers: [ provideZoneChangeDetection() ] });
1515

1616
it('Subscribers to events should run within NgZone', async () => {
1717
const fixture = createFixture();

0 commit comments

Comments
 (0)