Skip to content

Commit 6ffae5d

Browse files
committed
wip: test fixes?
1 parent bc2ddb8 commit 6ffae5d

File tree

6 files changed

+88
-33
lines changed

6 files changed

+88
-33
lines changed

goldens/public-api/core/index.api.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,6 +1759,21 @@ export interface SelfDecorator {
17591759
// @public
17601760
export function setTestabilityGetter(getter: GetTestability): void;
17611761

1762+
// @public
1763+
export const SHARED_STYLES_HOST: InjectionToken<SharedStylesHost>;
1764+
1765+
// @public
1766+
export interface SharedStylesHost {
1767+
// (undocumented)
1768+
addHost(hostNode: Node): void;
1769+
// (undocumented)
1770+
addStyles(styles: string[]): void;
1771+
// (undocumented)
1772+
removeHost(hostNode: Node): void;
1773+
// (undocumented)
1774+
removeStyles(styles: string[]): void;
1775+
}
1776+
17621777
// @public
17631778
export type Signal<T> = (() => T) & {
17641779
[SIGNAL]: unknown;

packages/core/src/render3/component_ref.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,14 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
310310
// If the created component is detached from the document, this will be `null` and stylesheets
311311
// will be tracked once the component is attached to the document.
312312
const rootNode = hostElement.getRootNode?.();
313-
if (rootNode) {
314-
const sharedStylesHost = rootLView[INJECTOR].get(SHARED_STYLES_HOST);
315-
if (typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot) {
316-
sharedStylesHost.addHost(rootNode);
317-
} else {
318-
const doc = rootLView[INJECTOR].get(DOCUMENT);
319-
sharedStylesHost.addHost(doc.head);
320-
}
313+
const sharedStylesHost = rootLView[INJECTOR].get(SHARED_STYLES_HOST);
314+
const isShadowRoot =
315+
rootNode && typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot;
316+
if (isShadowRoot) {
317+
sharedStylesHost.addHost(rootNode);
318+
} else {
319+
const doc = rootLView[INJECTOR].get(DOCUMENT);
320+
sharedStylesHost.addHost(doc.head);
321321
}
322322

323323
// rootView is the parent when bootstrapping
@@ -566,16 +566,19 @@ export class ComponentRef<T> extends AbstractComponentRef<T> {
566566
}
567567

568568
override destroy(): void {
569+
if (this.hostView.destroyed) {
570+
return;
571+
}
569572
// Verify if the host element is referenced in the SharedStylesHost and remove it if so.
570573
const rootNode = this.location.nativeElement.getRootNode?.();
571-
if (rootNode) {
572-
const sharedStylesHost = this._rootLView[INJECTOR].get(SHARED_STYLES_HOST);
573-
if (typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot) {
574-
sharedStylesHost.removeHost(rootNode);
575-
} else {
576-
const doc = this._rootLView[INJECTOR].get(DOCUMENT);
577-
sharedStylesHost.removeHost(doc.head);
578-
}
574+
const sharedStylesHost = this._rootLView[INJECTOR].get(SHARED_STYLES_HOST);
575+
const isShadowRoot =
576+
rootNode && typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot;
577+
if (isShadowRoot) {
578+
sharedStylesHost.removeHost(rootNode);
579+
} else {
580+
const doc = this._rootLView[INJECTOR].get(DOCUMENT);
581+
sharedStylesHost.removeHost(doc.head);
579582
}
580583
this.hostView.destroy();
581584
}

packages/core/src/render3/view/container.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ export function addLViewToLContainer(
124124
// we need to register the shadow root as a host with the SharedStylesHost.
125125
// This logic handles the case where an LView is added to an LContainer (e.g. dynamic component).
126126
const rootNode = (lView[HOST] as any)?.getRootNode?.();
127-
if (rootNode instanceof ShadowRoot) {
128-
const sharedStylesHost = lView[INJECTOR].get(SHARED_STYLES_HOST, null);
129-
sharedStylesHost?.addHost(rootNode);
127+
if (rootNode && typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot) {
128+
const sharedStylesHost = lView[INJECTOR].get(SHARED_STYLES_HOST);
129+
sharedStylesHost.addHost(rootNode);
130130
}
131131

132132
// When in hydration mode, reset the pointer to the first child in
@@ -168,9 +168,9 @@ export function detachView(lContainer: LContainer, removeIndex: number): LView |
168168
if (viewToDetach) {
169169
// Undo the SharedStylesHost registration if needed
170170
const rootNode = (viewToDetach[HOST] as any)?.getRootNode?.();
171-
if (rootNode instanceof ShadowRoot) {
172-
const sharedStylesHost = viewToDetach[INJECTOR].get(SHARED_STYLES_HOST, null);
173-
sharedStylesHost?.removeHost(rootNode);
171+
if (rootNode && typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot) {
172+
const sharedStylesHost = viewToDetach[INJECTOR].get(SHARED_STYLES_HOST);
173+
sharedStylesHost.removeHost(rootNode);
174174
}
175175

176176
const declarationLContainer = viewToDetach[DECLARATION_LCONTAINER];

packages/core/test/acceptance/view_container_ref_spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,9 @@ import {
4747
} from '../../src/core';
4848
import {ComponentFixture, TestBed, TestComponentRenderer} from '../../testing';
4949
import {clearTranslations, loadTranslations} from '@angular/localize';
50-
import {By, DomSanitizer} from '@angular/platform-browser';
50+
import {By, DomSanitizer, ɵSharedStylesHost as SharedStylesHost} from '@angular/platform-browser';
5151
import {expect} from '@angular/private/testing/matchers';
5252
import {ANIMATION_QUEUE} from '../../src/animation/queue';
53-
import {SharedStylesHost} from '@angular/platform-browser/src/dom/shared_styles_host';
5453

5554
describe('ViewContainerRef', () => {
5655
/**
@@ -1414,7 +1413,11 @@ describe('ViewContainerRef', () => {
14141413
{provide: ErrorHandler, useValue: TestBed.inject(ErrorHandler)},
14151414
{provide: RendererFactory2, useValue: TestBed.inject(RendererFactory2)},
14161415
{provide: ANIMATION_QUEUE, useValue: TestBed.inject(ANIMATION_QUEUE)},
1417-
{provide: SHARED_STYLES_HOST, useExisting: SharedStylesHost},
1416+
{
1417+
provide: SHARED_STYLES_HOST,
1418+
useValue: {addHost: () => {}, removeHost: () => {}, addStyles: () => {}},
1419+
},
1420+
{provide: DOCUMENT, useValue: TestBed.inject(DOCUMENT)},
14181421
],
14191422
})
14201423
class MyAppModule {}

packages/core/test/render3/component_ref_spec.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import {ComponentFactoryResolver} from '../../src/render3/component_ref';
1010
import {Renderer} from '../../src/render3/interfaces/renderer';
1111
import {RElement} from '../../src/render3/interfaces/renderer_dom';
12+
import {SHARED_STYLES_HOST} from '../../src/render3/interfaces/shared_styles_host';
13+
import {DOCUMENT} from '@angular/common';
1214
import {TestBed} from '../../testing';
1315

1416
import {
@@ -41,7 +43,26 @@ const THROWING_RENDERER_FACTOR2_PROVIDER = {
4143
},
4244
};
4345

46+
const MOCK_SHARED_STYLES_HOST_PROVIDER = {
47+
provide: SHARED_STYLES_HOST,
48+
useValue: {
49+
addHost: () => {},
50+
removeHost: () => {},
51+
},
52+
};
53+
54+
const COMMON_PROVIDERS = [
55+
MOCK_SHARED_STYLES_HOST_PROVIDER,
56+
{provide: DOCUMENT, useValue: document},
57+
];
58+
4459
describe('ComponentFactory', () => {
60+
beforeEach(() => {
61+
TestBed.configureTestingModule({
62+
providers: [COMMON_PROVIDERS],
63+
});
64+
});
65+
4566
const cfr = new ComponentFactoryResolver();
4667

4768
describe('constructor()', () => {
@@ -124,7 +145,7 @@ describe('ComponentFactory', () => {
124145
describe('(when `ngModuleRef` is not provided)', () => {
125146
it('should retrieve `RendererFactory2` from the specified injector', () => {
126147
const injector = Injector.create({
127-
providers: [{provide: RendererFactory2, useValue: rendererFactorySpy}],
148+
providers: [{provide: RendererFactory2, useValue: rendererFactorySpy}, COMMON_PROVIDERS],
128149
});
129150

130151
cf.create(injector);
@@ -138,6 +159,7 @@ describe('ComponentFactory', () => {
138159
providers: [
139160
{provide: RendererFactory2, useValue: rendererFactorySpy},
140161
{provide: Sanitizer, useFactory: sanitizerFactorySpy, deps: []},
162+
COMMON_PROVIDERS,
141163
],
142164
});
143165

@@ -150,7 +172,7 @@ describe('ComponentFactory', () => {
150172
describe('(when `ngModuleRef` is provided)', () => {
151173
it('should retrieve `RendererFactory2` from the specified injector first', () => {
152174
const injector = Injector.create({
153-
providers: [{provide: RendererFactory2, useValue: rendererFactorySpy}],
175+
providers: [{provide: RendererFactory2, useValue: rendererFactorySpy}, COMMON_PROVIDERS],
154176
});
155177
const mInjector = Injector.create({providers: [THROWING_RENDERER_FACTOR2_PROVIDER]});
156178

@@ -160,7 +182,7 @@ describe('ComponentFactory', () => {
160182
});
161183

162184
it('should retrieve `RendererFactory2` from the `ngModuleRef` if not provided by the injector', () => {
163-
const injector = Injector.create({providers: []});
185+
const injector = Injector.create({providers: [COMMON_PROVIDERS]});
164186
const mInjector = Injector.create({
165187
providers: [{provide: RendererFactory2, useValue: rendererFactorySpy}],
166188
});
@@ -175,7 +197,10 @@ describe('ComponentFactory', () => {
175197
.createSpy('Injector#sanitizerFactory')
176198
.and.returnValue({});
177199
const injector = Injector.create({
178-
providers: [{provide: Sanitizer, useFactory: iSanitizerFactorySpy, deps: []}],
200+
providers: [
201+
{provide: Sanitizer, useFactory: iSanitizerFactorySpy, deps: []},
202+
COMMON_PROVIDERS,
203+
],
179204
});
180205

181206
const mSanitizerFactorySpy = jasmine
@@ -195,7 +220,7 @@ describe('ComponentFactory', () => {
195220
});
196221

197222
it('should retrieve `Sanitizer` from the `ngModuleRef` if not provided by the injector', () => {
198-
const injector = Injector.create({providers: []});
223+
const injector = Injector.create({providers: [COMMON_PROVIDERS]});
199224

200225
const mSanitizerFactorySpy = jasmine
201226
.createSpy('NgModuleRef#sanitizerFactory')
@@ -216,7 +241,7 @@ describe('ComponentFactory', () => {
216241
describe('(when the factory is bound to a `ngModuleRef`)', () => {
217242
it('should retrieve `RendererFactory2` from the specified injector first', () => {
218243
const injector = Injector.create({
219-
providers: [{provide: RendererFactory2, useValue: rendererFactorySpy}],
244+
providers: [{provide: RendererFactory2, useValue: rendererFactorySpy}, COMMON_PROVIDERS],
220245
});
221246
(cf as any).ngModule = {
222247
injector: Injector.create({providers: [THROWING_RENDERER_FACTOR2_PROVIDER]}),
@@ -228,7 +253,7 @@ describe('ComponentFactory', () => {
228253
});
229254

230255
it('should retrieve `RendererFactory2` from the `ngModuleRef` if not provided by the injector', () => {
231-
const injector = Injector.create({providers: []});
256+
const injector = Injector.create({providers: [COMMON_PROVIDERS]});
232257
(cf as any).ngModule = {
233258
injector: Injector.create({
234259
providers: [{provide: RendererFactory2, useValue: rendererFactorySpy}],
@@ -248,6 +273,7 @@ describe('ComponentFactory', () => {
248273
providers: [
249274
{provide: RendererFactory2, useValue: rendererFactorySpy},
250275
{provide: Sanitizer, useFactory: iSanitizerFactorySpy, deps: []},
276+
COMMON_PROVIDERS,
251277
],
252278
});
253279

@@ -267,7 +293,7 @@ describe('ComponentFactory', () => {
267293
});
268294

269295
it('should retrieve `Sanitizer` from the `ngModuleRef` if not provided by the injector', () => {
270-
const injector = Injector.create({providers: []});
296+
const injector = Injector.create({providers: [COMMON_PROVIDERS]});
271297

272298
const mSanitizerFactorySpy = jasmine
273299
.createSpy('NgModuleRef#sanitizerFactory')
@@ -303,6 +329,7 @@ describe('ComponentFactory', () => {
303329
const injector = Injector.create({
304330
providers: [
305331
{provide: RendererFactory2, useFactory: () => new TestMockRendererFactory(), deps: []},
332+
COMMON_PROVIDERS,
306333
],
307334
});
308335

packages/core/test/test_bed_spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ import {expect} from '@angular/private/testing/matchers';
5757
import {NgModuleType} from '../src/render3';
5858
import {depsTracker} from '../src/render3/deps_tracker/deps_tracker';
5959
import {setClassMetadataAsync} from '../src/render3/metadata';
60+
import {ɵSharedStylesHost as SharedStylesHost} from '@angular/platform-browser';
61+
import {SHARED_STYLES_HOST} from '../src/render3/interfaces/shared_styles_host';
62+
import {APP_ID} from '../src/core';
6063
import {
6164
ComponentFixtureAutoDetect,
6265
TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT,
@@ -2700,6 +2703,10 @@ describe('TestBed module teardown', () => {
27002703
TestBed.configureTestingModule({
27012704
declarations: [StyledComp1, StyledComp2],
27022705
teardown: {destroyAfterEach: true},
2706+
providers: [
2707+
{provide: SHARED_STYLES_HOST, useClass: SharedStylesHost},
2708+
{provide: APP_ID, useValue: 'ng'},
2709+
],
27032710
});
27042711

27052712
const fixtures = [TestBed.createComponent(StyledComp1), TestBed.createComponent(StyledComp2)];

0 commit comments

Comments
 (0)