fix: Various editor / diagnostic improvements (#2363)

- Closes: #2360 
 - Closes: #2328
This commit is contained in:
Dermot Duffy
2026-02-21 09:31:10 -08:00
committed by GitHub
parent 6d5a76ac0a
commit df3614f793
13 changed files with 313 additions and 58 deletions
+5 -1
View File
@@ -20,7 +20,11 @@ assignees: ''
**[REQUIRED] Card diagnostic information:** **[REQUIRED] Card diagnostic information:**
<!-- <!--
On the card, hold down the Iris (default) menu button for a few seconds then paste the diagnostics *between* the pair of triple backticks below. No help can be provided without this. No help can be provided without this information. Fetch the diagnostics and paste them *between* the pair of triple backticks below.
Use either of two ways to fetch the diagnostics:
- Hold down the Iris (default) menu button for a few seconds.
- Open the card editor and click the 'Toggle Diagnostics' button.
--> -->
```yaml ```yaml
+4 -2
View File
@@ -19,8 +19,10 @@ incrementally improve, and I think time is better spent working on reproducible
issues or new features, than guessing what versions/options you are using. The issues or new features, than guessing what versions/options you are using. The
new issue template asks for the minimum possible information to support you: new issue template asks for the minimum possible information to support you:
- **Diagnostics**: On the card, hold down the Iris (default) menu button for a - **Diagnostics**: On the card, fetch the diagnostics and paste into the issue
few seconds then paste the diagnostics into the issue between the backticks. between the backticks. You can fetch the diagnostics in either of two ways:
- Hold down the Iris (default) menu button for a few seconds.
- Open the card editor and click the 'Toggle Diagnostics' button.
- **Description of the problem**: I need to understand the issue in sufficient - **Description of the problem**: I need to understand the issue in sufficient
detail. detail.
+1 -9
View File
@@ -32,15 +32,7 @@ class ActionHandler extends HTMLElement implements ActionHandlerInterface {
private started = false; private started = false;
public connectedCallback(): void { public connectedCallback(): void {
[ ['mouseup', 'mousewheel', 'scroll', 'touchcancel', 'wheel'].forEach((ev) => {
'touchcancel',
'mouseout',
'mouseup',
'touchmove',
'mousewheel',
'wheel',
'scroll',
].forEach((ev) => {
document.addEventListener( document.addEventListener(
ev, ev,
() => { () => {
+29 -1
View File
@@ -1,8 +1,10 @@
import { LitElement, ReactiveControllerHost } from 'lit'; import { LitElement, ReactiveControllerHost } from 'lit';
import { ActionEventTarget } from '../action-handler-directive'; import { ActionEventTarget } from '../action-handler-directive';
import { isCardInPanel } from '../ha/panel'; import { isCardInPanel } from '../ha/panel';
import { LovelaceCard } from '../ha/types';
import { setOrRemoveAttribute } from '../utils/basic'; import { setOrRemoveAttribute } from '../utils/basic';
import { isBeingCasted } from '../utils/casting'; import { isBeingCasted } from '../utils/casting';
import { isAncestorInEventPath } from '../utils/event-ancestor';
import { CardMediaReviewEventTarget } from '../utils/review'; import { CardMediaReviewEventTarget } from '../utils/review';
import { ViewItem } from '../view/item'; import { ViewItem } from '../view/item';
import { ActionExecutionRequestEventTarget } from './actions/utils/execution-request'; import { ActionExecutionRequestEventTarget } from './actions/utils/execution-request';
@@ -12,7 +14,8 @@ import { CardElementAPI } from './types';
export type ScrollCallback = () => void; export type ScrollCallback = () => void;
export type MenuToggleCallback = () => void; export type MenuToggleCallback = () => void;
export type CardHTMLElement = LitElement & export type CardHTMLElement = LovelaceCard &
LitElement &
ReactiveControllerHost & ReactiveControllerHost &
ActionEventTarget & ActionEventTarget &
ActionExecutionRequestEventTarget & ActionExecutionRequestEventTarget &
@@ -141,6 +144,10 @@ export class CardElementManager {
'popstate', 'popstate',
this._api.getQueryStringManager().requestExecution, this._api.getQueryStringManager().requestExecution,
); );
window.addEventListener(
'advanced-camera-card:editor:diagnostics',
this._editorDiagnosticsHandler,
);
this._api.getConditionStateManager()?.setState({ this._api.getConditionStateManager()?.setState({
userAgent: navigator.userAgent, userAgent: navigator.userAgent,
@@ -218,6 +225,10 @@ export class CardElementManager {
'popstate', 'popstate',
this._api.getQueryStringManager().requestExecution, this._api.getQueryStringManager().requestExecution,
); );
window.removeEventListener(
'advanced-camera-card:editor:diagnostics',
this._editorDiagnosticsHandler,
);
} }
private _handleMediaReviewed = (ev: CustomEvent<ViewItem>): void => { private _handleMediaReviewed = (ev: CustomEvent<ViewItem>): void => {
@@ -230,4 +241,21 @@ export class CardElementManager {
this.update(); this.update();
} }
}; };
protected _editorDiagnosticsHandler = (ev: Event): void => {
const toggleDiagnostics = (): void => {
const viewManager = this._api.getViewManager();
if (viewManager.getView()?.view === 'diagnostics') {
viewManager.setViewDefault();
} else {
viewManager.setViewByParameters({
params: { view: 'diagnostics' },
});
}
};
if (isAncestorInEventPath(this._element, ev, 'hui-dialog-edit-card')) {
toggleDiagnostics();
}
};
} }
+29 -20
View File
@@ -271,6 +271,7 @@ import { localize } from './localize/localize.js';
import editorStyle from './scss/editor.scss'; import editorStyle from './scss/editor.scss';
import { arrayMove, prettifyTitle } from './utils/basic.js'; import { arrayMove, prettifyTitle } from './utils/basic.js';
import { getCameraID } from './utils/camera.js'; import { getCameraID } from './utils/camera.js';
import { fireAdvancedCameraCardEvent } from './utils/fire-advanced-camera-card-event.js';
import { getFolderID } from './utils/folder.js'; import { getFolderID } from './utils/folder.js';
const MENU_CAMERAS = 'cameras'; const MENU_CAMERAS = 'cameras';
@@ -2965,26 +2966,6 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
[]) as RawAdvancedCameraCardConfigArray; []) as RawAdvancedCameraCardConfigArray;
return html` return html`
${this._configUpgradeable
? html` <div class="upgrade">
<span>${localize('editor.upgrade_available')}</span>
<span>
<mwc-button
raised
label="${localize('editor.upgrade')}"
@click=${() => {
if (this._config) {
const upgradedConfig = copyConfig(this._config);
upgradeConfig(upgradedConfig);
this._updateConfig(upgradedConfig);
}
}}
>
</mwc-button>
</span>
</div>
<br />`
: html``}
<div class="card-config"> <div class="card-config">
${this._renderOptionSetHeader('cameras')} ${this._renderOptionSetHeader('cameras')}
${this._expandedMenus[MENU_OPTIONS] === 'cameras' ${this._expandedMenus[MENU_OPTIONS] === 'cameras'
@@ -3597,6 +3578,34 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
</div>` </div>`
: ''}` : ''}`
: html``} : html``}
<div class="action-buttons">
${this._configUpgradeable
? html`<ha-button
appearance="filled"
variant="warning"
title=${localize('editor.upgrade_available')}
aria-label=${localize('editor.upgrade_available')}
@click=${() => {
if (this._config) {
const upgradedConfig = copyConfig(this._config);
upgradeConfig(upgradedConfig);
this._updateConfig(upgradedConfig);
}
}}
>
${localize('editor.upgrade')}
</ha-button>`
: ''}
<ha-button
title=${localize('editor.toggle_diagnostics')}
aria-label=${localize('editor.toggle_diagnostics')}
@click=${() => {
fireAdvancedCameraCardEvent(this, 'editor:diagnostics');
}}
>
${localize('editor.toggle_diagnostics')}
</ha-button>
</div>
</div> </div>
`; `;
} }
-1
View File
@@ -22,7 +22,6 @@ export const sideLoadHomeAssistantElements = async (): Promise<boolean> => {
'ha-spinner', 'ha-spinner',
'ha-state-icon', 'ha-state-icon',
'ha-web-rtc-player', 'ha-web-rtc-player',
'mwc-button',
'mwc-list-item', 'mwc-list-item',
'state-badge', 'state-badge',
]; ];
+1
View File
@@ -189,6 +189,7 @@ export interface LovelaceCard extends HTMLElement {
hass?: HomeAssistant; hass?: HomeAssistant;
isPanel?: boolean; isPanel?: boolean;
editMode?: boolean; editMode?: boolean;
preview?: boolean;
getCardSize(): number | Promise<number>; getCardSize(): number | Promise<number>;
setConfig(config: LovelaceCardConfig): void; setConfig(config: LovelaceCardConfig): void;
} }
+2 -1
View File
@@ -688,11 +688,12 @@
"profiles_secondary": "Choose pre-configured sets of defaults", "profiles_secondary": "Choose pre-configured sets of defaults",
"remote_control": "Remote Control", "remote_control": "Remote Control",
"remote_control_secondary": "Options for remote controlling the card", "remote_control_secondary": "Options for remote controlling the card",
"toggle_diagnostics": "Toggle diagnostics",
"status_bar": "Status bar", "status_bar": "Status bar",
"status_bar_secondary": "Status bar look & feel options", "status_bar_secondary": "Status bar look & feel options",
"timeline": "Timeline", "timeline": "Timeline",
"timeline_secondary": "Event timeline options", "timeline_secondary": "Event timeline options",
"upgrade": "Upgrade", "upgrade": "Automatic Upgrade",
"upgrade_available": "An automatic card configuration upgrade is available", "upgrade_available": "An automatic card configuration upgrade is available",
"view": "View", "view": "View",
"view_secondary": "What the card should show and how to show it" "view_secondary": "What the card should show and how to show it"
+6 -9
View File
@@ -36,16 +36,9 @@
.submenu + .option { .submenu + .option {
margin-top: 10px; margin-top: 10px;
} }
div.upgrade { .action-buttons {
width: auto;
border: 1px dotted var(--primary-color);
margin: 10px;
display: flex; display: flex;
justify-content: space-between; flex-wrap: wrap;
align-items: center;
}
div.upgrade span {
padding: 10px;
} }
.submenu-header { .submenu-header {
@@ -150,3 +143,7 @@ advanced-camera-card-message::part(icon) {
margin-top: 0; margin-top: 0;
pointer-events: none; pointer-events: none;
} }
ha-button {
margin: 10px;
}
+21
View File
@@ -0,0 +1,21 @@
/**
* Walk up `element`'s ancestor chain (through shadow boundaries) looking for
* an ancestor with the given tag name. Returns true if one is found and that
* same element also appears in the event's composedPath — indicating the event
* originated from within the same subtree as the element.
*/
export const isAncestorInEventPath = (
element: Element,
ev: Event,
tagName: string,
): boolean => {
const composedPath = ev.composedPath();
let node: Node | null = element;
while (node) {
if (node instanceof Element && node.tagName.toLowerCase() === tagName) {
return composedPath.includes(node);
}
node = node instanceof ShadowRoot ? node.host : node.parentNode;
}
return false;
};
@@ -1,13 +1,14 @@
import { afterEach, describe, expect, it, vi } from 'vitest'; import { afterEach, assert, describe, expect, it, vi } from 'vitest';
import { mock } from 'vitest-mock-extended'; import { mock } from 'vitest-mock-extended';
import { CardElementManager } from '../../src/card-controller/card-element-manager'; import { CardElementManager } from '../../src/card-controller/card-element-manager';
import { StateWatcher } from '../../src/card-controller/hass/state-watcher'; import { StateWatcher } from '../../src/card-controller/hass/state-watcher';
import { QueryResults } from '../../src/view/query-results'; import { QueryResults } from '../../src/view/query-results';
import { View } from '../../src/view/view';
import { import {
callStateWatcherCallback, callStateWatcherCallback,
createCardAPI, createCardAPI,
createCardHTMLElement,
createConfig, createConfig,
createLitElement,
createStateEntity, createStateEntity,
createView, createView,
TestViewMedia, TestViewMedia,
@@ -20,7 +21,7 @@ describe('CardElementManager', () => {
}); });
it('should get element', () => { it('should get element', () => {
const element = createLitElement(); const element = createCardHTMLElement();
const manager = new CardElementManager( const manager = new CardElementManager(
createCardAPI(), createCardAPI(),
element, element,
@@ -35,7 +36,7 @@ describe('CardElementManager', () => {
const callback = vi.fn(); const callback = vi.fn();
const manager = new CardElementManager( const manager = new CardElementManager(
createCardAPI(), createCardAPI(),
createLitElement(), createCardHTMLElement(),
callback, callback,
() => undefined, () => undefined,
); );
@@ -49,7 +50,7 @@ describe('CardElementManager', () => {
const callback = vi.fn(); const callback = vi.fn();
const manager = new CardElementManager( const manager = new CardElementManager(
createCardAPI(), createCardAPI(),
createLitElement(), createCardHTMLElement(),
() => undefined, () => undefined,
callback, callback,
); );
@@ -60,7 +61,7 @@ describe('CardElementManager', () => {
}); });
it('should update', () => { it('should update', () => {
const element = createLitElement(); const element = createCardHTMLElement();
const manager = new CardElementManager( const manager = new CardElementManager(
createCardAPI(), createCardAPI(),
element, element,
@@ -73,7 +74,7 @@ describe('CardElementManager', () => {
}); });
it('should get hasUpdated', () => { it('should get hasUpdated', () => {
const element = createLitElement(); const element = createCardHTMLElement();
element.hasUpdated = true; element.hasUpdated = true;
const manager = new CardElementManager( const manager = new CardElementManager(
createCardAPI(), createCardAPI(),
@@ -89,7 +90,7 @@ describe('CardElementManager', () => {
const windowAddEventListener = vi.spyOn(global.window, 'addEventListener'); const windowAddEventListener = vi.spyOn(global.window, 'addEventListener');
const addEventListener = vi.fn(); const addEventListener = vi.fn();
const element = createLitElement(); const element = createCardHTMLElement();
element.addEventListener = addEventListener; element.addEventListener = addEventListener;
const api = createCardAPI(); const api = createCardAPI();
@@ -132,6 +133,10 @@ describe('CardElementManager', () => {
); );
expect(windowAddEventListener).toBeCalledWith('location-changed', expect.anything()); expect(windowAddEventListener).toBeCalledWith('location-changed', expect.anything());
expect(windowAddEventListener).toBeCalledWith('popstate', expect.anything()); expect(windowAddEventListener).toBeCalledWith('popstate', expect.anything());
expect(windowAddEventListener).toBeCalledWith(
'advanced-camera-card:editor:diagnostics',
expect.anything(),
);
expect(api.getInteractionManager().initialize).toBeCalled(); expect(api.getInteractionManager().initialize).toBeCalled();
expect(api.getFullscreenManager().initialize).toBeCalled(); expect(api.getFullscreenManager().initialize).toBeCalled();
@@ -143,7 +148,7 @@ describe('CardElementManager', () => {
it('should disconnect', () => { it('should disconnect', () => {
const windowRemoveEventListener = vi.spyOn(global.window, 'removeEventListener'); const windowRemoveEventListener = vi.spyOn(global.window, 'removeEventListener');
const element = createLitElement(); const element = createCardHTMLElement();
element.setAttribute('panel', ''); element.setAttribute('panel', '');
element.setAttribute('casted', ''); element.setAttribute('casted', '');
@@ -195,6 +200,10 @@ describe('CardElementManager', () => {
expect.anything(), expect.anything(),
); );
expect(windowRemoveEventListener).toBeCalledWith('popstate', expect.anything()); expect(windowRemoveEventListener).toBeCalledWith('popstate', expect.anything());
expect(windowRemoveEventListener).toBeCalledWith(
'advanced-camera-card:editor:diagnostics',
expect.anything(),
);
expect(api.getMediaLoadedInfoManager().clear).toBeCalled(); expect(api.getMediaLoadedInfoManager().clear).toBeCalled();
expect(api.getFullscreenManager().disconnect).toBeCalled(); expect(api.getFullscreenManager().disconnect).toBeCalled();
@@ -217,7 +226,7 @@ describe('CardElementManager', () => {
const stateWatcher = mock<StateWatcher>(); const stateWatcher = mock<StateWatcher>();
vi.mocked(api.getHASSManager().getStateWatcher).mockReturnValue(stateWatcher); vi.mocked(api.getHASSManager().getStateWatcher).mockReturnValue(stateWatcher);
const element = createLitElement(); const element = createCardHTMLElement();
const manager = new CardElementManager( const manager = new CardElementManager(
api, api,
element, element,
@@ -245,7 +254,7 @@ describe('CardElementManager', () => {
const stateWatcher = mock<StateWatcher>(); const stateWatcher = mock<StateWatcher>();
vi.mocked(api.getHASSManager().getStateWatcher).mockReturnValue(stateWatcher); vi.mocked(api.getHASSManager().getStateWatcher).mockReturnValue(stateWatcher);
const element = createLitElement(); const element = createCardHTMLElement();
const manager = new CardElementManager( const manager = new CardElementManager(
api, api,
element, element,
@@ -275,7 +284,7 @@ describe('CardElementManager', () => {
vi.mocked(api.getViewManager().getView).mockReturnValue(view); vi.mocked(api.getViewManager().getView).mockReturnValue(view);
const element = createLitElement(); const element = createCardHTMLElement();
const manager = new CardElementManager( const manager = new CardElementManager(
api, api,
element, element,
@@ -310,7 +319,7 @@ describe('CardElementManager', () => {
vi.mocked(api.getViewManager().getView).mockReturnValue(view); vi.mocked(api.getViewManager().getView).mockReturnValue(view);
const element = createLitElement(); const element = createCardHTMLElement();
const manager = new CardElementManager( const manager = new CardElementManager(
api, api,
element, element,
@@ -334,4 +343,95 @@ describe('CardElementManager', () => {
expect(element.requestUpdate).not.toBeCalled(); expect(element.requestUpdate).not.toBeCalled();
}); });
}); });
describe('should handle diagnostics', () => {
afterEach(() => {
vi.clearAllMocks();
});
const createDialogWithCard = (element: HTMLElement) => {
const dialog = document.createElement('hui-dialog-edit-card');
dialog.attachShadow({ mode: 'open' });
assert(dialog.shadowRoot);
dialog.shadowRoot.append(element);
return dialog;
};
const fireFromDialog = (dialog: HTMLElement) => {
const editorDiv = document.createElement('div');
assert(dialog.shadowRoot);
dialog.shadowRoot.append(editorDiv);
editorDiv.dispatchEvent(
new CustomEvent('advanced-camera-card:editor:diagnostics', {
bubbles: true,
composed: true,
}),
);
};
it('sets view to diagnostics if card is in editor', () => {
const api = createCardAPI();
const element = createCardHTMLElement();
const manager = new CardElementManager(
api,
element,
() => undefined,
() => undefined,
);
const dialog = createDialogWithCard(element);
document.body.append(dialog);
manager.elementConnected();
fireFromDialog(dialog);
expect(api.getViewManager().setViewByParameters).toBeCalledWith({
params: { view: 'diagnostics' },
});
});
it('resets to default view if already in diagnostics view', () => {
const api = createCardAPI();
const element = createCardHTMLElement();
const manager = new CardElementManager(
api,
element,
() => undefined,
() => undefined,
);
vi.mocked(api.getViewManager().getView).mockReturnValue(
new View({ view: 'diagnostics' }),
);
const dialog = createDialogWithCard(element);
document.body.append(dialog);
manager.elementConnected();
fireFromDialog(dialog);
expect(api.getViewManager().setViewDefault).toBeCalled();
});
it('does not set view to diagnostics if card is not in editor', () => {
const api = createCardAPI();
const element = createCardHTMLElement();
const manager = new CardElementManager(
api,
element,
() => undefined,
() => undefined,
);
manager.elementConnected();
// Event fired from a different dialog that does not contain the card
const otherDialog = document.createElement('hui-dialog-edit-card');
otherDialog.attachShadow({ mode: 'open' });
document.body.append(otherDialog);
fireFromDialog(otherDialog);
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
});
});
}); });
+11 -1
View File
@@ -24,7 +24,10 @@ import {
import { ActionsManager } from '../src/card-controller/actions/actions-manager'; import { ActionsManager } from '../src/card-controller/actions/actions-manager';
import { AutomationsManager } from '../src/card-controller/automations-manager'; import { AutomationsManager } from '../src/card-controller/automations-manager';
import { CameraURLManager } from '../src/card-controller/camera-url-manager'; import { CameraURLManager } from '../src/card-controller/camera-url-manager';
import { CardElementManager } from '../src/card-controller/card-element-manager'; import {
CardElementManager,
CardHTMLElement,
} from '../src/card-controller/card-element-manager';
import { ConfigManager } from '../src/card-controller/config/config-manager'; import { ConfigManager } from '../src/card-controller/config/config-manager';
import { CardController } from '../src/card-controller/controller'; import { CardController } from '../src/card-controller/controller';
import { DefaultManager } from '../src/card-controller/default-manager'; import { DefaultManager } from '../src/card-controller/default-manager';
@@ -626,6 +629,13 @@ export const createParent = (options?: { children?: HTMLElement[] }): HTMLElemen
return parent; return parent;
}; };
export const createCardHTMLElement = (): CardHTMLElement => {
const element = createLitElement() as CardHTMLElement;
element.getCardSize = vi.fn();
element.setConfig = vi.fn();
return element;
};
export const createLitElement = (): LitElement => { export const createLitElement = (): LitElement => {
const element = document.createElement('div') as unknown as LitElement; const element = document.createElement('div') as unknown as LitElement;
element.addController = vi.fn(); element.addController = vi.fn();
+91
View File
@@ -0,0 +1,91 @@
import { assert, describe, expect, it } from 'vitest';
import { isAncestorInEventPath } from '../../src/utils/event-ancestor';
// @vitest-environment jsdom
describe('isAncestorInEventPath', () => {
const dispatch = (source: EventTarget, listener: (ev: Event) => void) => {
window.addEventListener('test', listener, { once: true });
source.dispatchEvent(new CustomEvent('test', { bubbles: true, composed: true }));
};
it('returns true when element shares the ancestor with the event source', () => {
const element = document.createElement('div');
const ancestor = document.createElement('hui-dialog-edit-card');
ancestor.attachShadow({ mode: 'open' });
assert(ancestor.shadowRoot);
ancestor.shadowRoot.append(element);
document.body.append(ancestor);
const eventSource = document.createElement('div');
ancestor.shadowRoot.append(eventSource);
let result: boolean | undefined;
dispatch(eventSource, (ev) => {
result = isAncestorInEventPath(element, ev, 'hui-dialog-edit-card');
});
expect(result).toBe(true);
});
it('returns false when element and event source are in different ancestors with the same tag', () => {
const element = document.createElement('div');
const ancestor1 = document.createElement('hui-dialog-edit-card');
ancestor1.attachShadow({ mode: 'open' });
assert(ancestor1.shadowRoot);
ancestor1.shadowRoot.append(element);
document.body.append(ancestor1);
const ancestor2 = document.createElement('hui-dialog-edit-card');
ancestor2.attachShadow({ mode: 'open' });
assert(ancestor2.shadowRoot);
const eventSource = document.createElement('div');
ancestor2.shadowRoot.append(eventSource);
document.body.append(ancestor2);
let result: boolean | undefined;
dispatch(eventSource, (ev) => {
result = isAncestorInEventPath(element, ev, 'hui-dialog-edit-card');
});
expect(result).toBe(false);
});
it('returns false when element has no ancestor with the given tag', () => {
const element = document.createElement('div');
document.body.append(element);
const eventSource = document.createElement('div');
document.body.append(eventSource);
let result: boolean | undefined;
dispatch(eventSource, (ev) => {
result = isAncestorInEventPath(element, ev, 'hui-dialog-edit-card');
});
expect(result).toBe(false);
});
it('traverses shadow boundaries in the element ancestor chain', () => {
const element = document.createElement('div');
const inner = document.createElement('hui-card');
inner.attachShadow({ mode: 'open' });
assert(inner.shadowRoot);
inner.shadowRoot.append(element);
const ancestor = document.createElement('hui-dialog-edit-card');
ancestor.attachShadow({ mode: 'open' });
assert(ancestor.shadowRoot);
ancestor.shadowRoot.append(inner);
document.body.append(ancestor);
const eventSource = document.createElement('div');
ancestor.shadowRoot.append(eventSource);
let result: boolean | undefined;
dispatch(eventSource, (ev) => {
result = isAncestorInEventPath(element, ev, 'hui-dialog-edit-card');
});
expect(result).toBe(true);
});
});