Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add reporting for shadowSupportMode #3980

Merged
merged 3 commits into from Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/@lwc/engine-core/src/framework/def.ts
Expand Up @@ -48,6 +48,7 @@ import {
HTMLElementConstructor,
} from './base-bridge-element';
import { getComponentOrSwappedComponent } from './hot-swaps';
import { isReportingEnabled, report, ReportingEventId } from './reporting';

export interface ComponentDef {
name: string;
Expand Down Expand Up @@ -198,6 +199,17 @@ function createComponentDef(Ctor: LightningElementConstructor): ComponentDef {
let shadowSupportMode = superDef.shadowSupportMode;
if (!isUndefined(ctorShadowSupportMode)) {
shadowSupportMode = ctorShadowSupportMode;

if (
isReportingEnabled() &&
(shadowSupportMode === ShadowSupportMode.Any ||
shadowSupportMode === ShadowSupportMode.Native)
) {
report(ReportingEventId.ShadowSupportModeUsage, {
tagName: Ctor.name,
mode: shadowSupportMode,
});
}
}

let renderMode = superDef.renderMode;
Expand Down
10 changes: 9 additions & 1 deletion packages/@lwc/engine-core/src/framework/reporting.ts
Expand Up @@ -6,7 +6,7 @@
*/
import { noop } from '@lwc/shared';

import { ShadowMode } from './vm';
import { ShadowMode, ShadowSupportMode } from './vm';

export const enum ReportingEventId {
CrossRootAriaInSyntheticShadow = 'CrossRootAriaInSyntheticShadow',
Expand All @@ -16,6 +16,7 @@ export const enum ReportingEventId {
StylesheetMutation = 'StylesheetMutation',
ConnectedCallbackWhileDisconnected = 'ConnectedCallbackWhileDisconnected',
ShadowModeUsage = 'ShadowModeUsage',
ShadowSupportModeUsage = 'ShadowSupportModeUsage',
}

export interface BasePayload {
Expand Down Expand Up @@ -51,6 +52,12 @@ export interface ShadowModeUsagePayload extends BasePayload {
mode: ShadowMode;
}

// TODO [#3981]: Add schema to o11y schema repo so that we can use 'ctorName' or 'name'
// instead of overloading 'tagName'.
export interface ShadowSupportModeUsagePayload extends BasePayload {
mode: ShadowSupportMode;
}

export type ReportingPayloadMapping = {
[ReportingEventId.CrossRootAriaInSyntheticShadow]: CrossRootAriaInSyntheticShadowPayload;
[ReportingEventId.CompilerRuntimeVersionMismatch]: CompilerRuntimeVersionMismatchPayload;
Expand All @@ -59,6 +66,7 @@ export type ReportingPayloadMapping = {
[ReportingEventId.StylesheetMutation]: StylesheetMutationPayload;
[ReportingEventId.ConnectedCallbackWhileDisconnected]: ConnectedCallbackWhileDisconnectedPayload;
[ReportingEventId.ShadowModeUsage]: ShadowModeUsagePayload;
[ReportingEventId.ShadowSupportModeUsage]: ShadowSupportModeUsagePayload;
};

export type ReportingDispatcher<T extends ReportingEventId = ReportingEventId> = (
Expand Down
@@ -0,0 +1,66 @@
import { createElement } from 'lwc';
import { attachReportingControlDispatcher, detachReportingControlDispatcher } from 'test-utils';

import Any from 'x/any';
import Reset from 'x/reset';
import None from 'x/none';
import NativeOnly from 'x/native';

// Should be kept in sync with the enum in vm.ts
const ShadowSupportMode = {
Any: 'any',
Default: 'reset',
Native: 'native',
};

/**
* These tests must be the first ones to generate the component def for the components they use.
* This is because subsequent calls to create the component will use the cached ctor rather than
* recreating the entire def. We only report on that initial def creation for perf reasons.
*/
describe('shadow support mode reporting', () => {
let dispatcher;

beforeEach(() => {
dispatcher = jasmine.createSpy();
attachReportingControlDispatcher(dispatcher, ['ShadowSupportModeUsage']);
});

afterEach(() => {
detachReportingControlDispatcher();
});

it('should report use of value "any"', () => {
expect(() => {
createElement('x-any', { is: Any });
}).toLogWarningDev(/Invalid value 'any' for static property shadowSupportMode/);

expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledWith('ShadowSupportModeUsage', {
tagName: Any.name,
mode: ShadowSupportMode.Any,
});
});

it('should report use of value "native"', () => {
createElement('x-native', { is: NativeOnly });

expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledWith('ShadowSupportModeUsage', {
tagName: NativeOnly.name,
mode: ShadowSupportMode.Native,
});
});

it('should not report use of value "reset"', () => {
createElement('x-reset', { is: Reset });

expect(dispatcher).toHaveBeenCalledTimes(0);
});

it('should not report when no value is provided', () => {
createElement('x-none', { is: None });

expect(dispatcher).toHaveBeenCalledTimes(0);
});
});
@@ -0,0 +1,5 @@
import { LightningElement } from 'lwc';

export default class Any extends LightningElement {
static shadowSupportMode = 'any';
}
@@ -0,0 +1,5 @@
import { LightningElement } from 'lwc';

export default class Native extends LightningElement {
static shadowSupportMode = 'native';
}
@@ -0,0 +1,3 @@
import { LightningElement } from 'lwc';

export default class None extends LightningElement {}
@@ -0,0 +1,5 @@
import { LightningElement } from 'lwc';

export default class Reset extends LightningElement {
static shadowSupportMode = 'reset';
}