fix: Don't block for microphone initialization if browser does not support microphone (#1576)
This commit is contained in:
@@ -52,7 +52,7 @@ export class InitializationManager {
|
|||||||
InitializationAspect.LANGUAGES,
|
InitializationAspect.LANGUAGES,
|
||||||
InitializationAspect.SIDE_LOAD_ELEMENTS,
|
InitializationAspect.SIDE_LOAD_ELEMENTS,
|
||||||
InitializationAspect.CAMERAS,
|
InitializationAspect.CAMERAS,
|
||||||
...(config.live.microphone.always_connected
|
...(this._api.getMicrophoneManager().shouldConnectOnInitialization()
|
||||||
? [InitializationAspect.MICROPHONE_CONNECT]
|
? [InitializationAspect.MICROPHONE_CONNECT]
|
||||||
: []),
|
: []),
|
||||||
InitializationAspect.VIEW,
|
InitializationAspect.VIEW,
|
||||||
@@ -99,7 +99,7 @@ export class InitializationManager {
|
|||||||
// avoid issues with some cameras that only allow 2-way audio on the
|
// avoid issues with some cameras that only allow 2-way audio on the
|
||||||
// first stream initialized. See:
|
// first stream initialized. See:
|
||||||
// https://github.com/dermotduffy/frigate-hass-card/issues/1235
|
// https://github.com/dermotduffy/frigate-hass-card/issues/1235
|
||||||
...(config.live.microphone.always_connected && {
|
...(this._api.getMicrophoneManager().shouldConnectOnInitialization() && {
|
||||||
[InitializationAspect.MICROPHONE_CONNECT]: async () =>
|
[InitializationAspect.MICROPHONE_CONNECT]: async () =>
|
||||||
await this._api.getMicrophoneManager().connect(),
|
await this._api.getMicrophoneManager().connect(),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -33,7 +33,27 @@ export class MicrophoneManager implements ReadonlyMicrophoneManager {
|
|||||||
this._setConditionState();
|
this._setConditionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public shouldConnectOnInitialization(): boolean {
|
||||||
|
return (
|
||||||
|
!!this._api.getConfigManager().getConfig()?.live.microphone?.always_connected &&
|
||||||
|
// If it won't be possible to connect the microphone at all, we do not
|
||||||
|
// block the initialization of the card (the microphone just won't work)
|
||||||
|
this.isSupported()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSupported(): boolean {
|
||||||
|
// Some browsers will have mediaDevices/getUserMedia as undefined if
|
||||||
|
// accessed over http.
|
||||||
|
// See: https://github.com/dermotduffy/frigate-hass-card/issues/1543
|
||||||
|
return !!navigator.mediaDevices?.getUserMedia;
|
||||||
|
}
|
||||||
|
|
||||||
public async connect(): Promise<boolean> {
|
public async connect(): Promise<boolean> {
|
||||||
|
if (!this.isSupported()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._stream = await navigator.mediaDevices.getUserMedia({
|
this._stream = await navigator.mediaDevices.getUserMedia({
|
||||||
audio: true,
|
audio: true,
|
||||||
@@ -76,6 +96,10 @@ export class MicrophoneManager implements ReadonlyMicrophoneManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async unmute(): Promise<void> {
|
public async unmute(): Promise<void> {
|
||||||
|
if (!this.isSupported()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const wasUnmuted = !this.isMuted();
|
const wasUnmuted = !this.isMuted();
|
||||||
|
|
||||||
const unmute = (): void => {
|
const unmute = (): void => {
|
||||||
|
|||||||
@@ -374,11 +374,12 @@ export class MenuButtonController {
|
|||||||
currentMediaLoadedInfo?: MediaLoadedInfo | null,
|
currentMediaLoadedInfo?: MediaLoadedInfo | null,
|
||||||
): MenuItem | null {
|
): MenuItem | null {
|
||||||
if (microphoneManager && currentMediaLoadedInfo?.capabilities?.supports2WayAudio) {
|
if (microphoneManager && currentMediaLoadedInfo?.capabilities?.supports2WayAudio) {
|
||||||
const forbidden = microphoneManager.isForbidden();
|
const unavailable =
|
||||||
|
microphoneManager.isForbidden() || !microphoneManager.isSupported();
|
||||||
const muted = microphoneManager.isMuted();
|
const muted = microphoneManager.isMuted();
|
||||||
const buttonType = config.menu.buttons.microphone.type;
|
const buttonType = config.menu.buttons.microphone.type;
|
||||||
return {
|
return {
|
||||||
icon: forbidden
|
icon: unavailable
|
||||||
? 'mdi:microphone-message-off'
|
? 'mdi:microphone-message-off'
|
||||||
: muted
|
: muted
|
||||||
? 'mdi:microphone-off'
|
? 'mdi:microphone-off'
|
||||||
@@ -386,8 +387,8 @@ export class MenuButtonController {
|
|||||||
...config.menu.buttons.microphone,
|
...config.menu.buttons.microphone,
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
title: localize('config.menu.buttons.microphone'),
|
title: localize('config.menu.buttons.microphone'),
|
||||||
style: forbidden || muted ? {} : this._getEmphasizedStyle(true),
|
style: unavailable || muted ? {} : this._getEmphasizedStyle(true),
|
||||||
...(!forbidden &&
|
...(!unavailable &&
|
||||||
buttonType === 'momentary' && {
|
buttonType === 'momentary' && {
|
||||||
start_tap_action: createGeneralAction(
|
start_tap_action: createGeneralAction(
|
||||||
'microphone_unmute',
|
'microphone_unmute',
|
||||||
@@ -396,7 +397,7 @@ export class MenuButtonController {
|
|||||||
'microphone_mute',
|
'microphone_mute',
|
||||||
) as FrigateCardCustomAction,
|
) as FrigateCardCustomAction,
|
||||||
}),
|
}),
|
||||||
...(!forbidden &&
|
...(!unavailable &&
|
||||||
buttonType === 'toggle' && {
|
buttonType === 'toggle' && {
|
||||||
tap_action: createGeneralAction(
|
tap_action: createGeneralAction(
|
||||||
muted ? 'microphone_unmute' : 'microphone_mute',
|
muted ? 'microphone_unmute' : 'microphone_mute',
|
||||||
|
|||||||
@@ -39,15 +39,10 @@ describe('InitializationManager', () => {
|
|||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new InitializationManager(api);
|
const manager = new InitializationManager(api);
|
||||||
|
|
||||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
createConfig({
|
vi.mocked(
|
||||||
live: {
|
api.getMicrophoneManager().shouldConnectOnInitialization,
|
||||||
microphone: {
|
).mockReturnValue(true);
|
||||||
always_connected: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(manager.isInitializedMandatory()).toBeFalsy();
|
expect(manager.isInitializedMandatory()).toBeFalsy();
|
||||||
});
|
});
|
||||||
@@ -103,15 +98,10 @@ describe('InitializationManager', () => {
|
|||||||
it('successfully with microphone if configured', async () => {
|
it('successfully with microphone if configured', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
vi.mocked(
|
||||||
createConfig({
|
api.getMicrophoneManager().shouldConnectOnInitialization,
|
||||||
live: {
|
).mockReturnValue(true);
|
||||||
microphone: {
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
always_connected: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
vi.mocked(loadLanguages).mockResolvedValue(true);
|
vi.mocked(loadLanguages).mockResolvedValue(true);
|
||||||
vi.mocked(sideLoadHomeAssistantElements).mockResolvedValue(true);
|
vi.mocked(sideLoadHomeAssistantElements).mockResolvedValue(true);
|
||||||
vi.mocked(api.getCameraManager().initializeCamerasFromConfig).mockResolvedValue(
|
vi.mocked(api.getCameraManager().initializeCamerasFromConfig).mockResolvedValue(
|
||||||
|
|||||||
@@ -3,12 +3,21 @@ import { mock } from 'vitest-mock-extended';
|
|||||||
import { MicrophoneManager } from '../../src/card-controller/microphone-manager';
|
import { MicrophoneManager } from '../../src/card-controller/microphone-manager';
|
||||||
import { createCardAPI, createConfig } from '../test-utils';
|
import { createCardAPI, createConfig } from '../test-utils';
|
||||||
|
|
||||||
const navigatorMock = {
|
const navigatorMock: Navigator = {
|
||||||
|
...mock<Navigator>(),
|
||||||
mediaDevices: {
|
mediaDevices: {
|
||||||
|
...mock<MediaDevices>(),
|
||||||
getUserMedia: vi.fn(),
|
getUserMedia: vi.fn(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const medialessNavigatorMock: Navigator = {
|
||||||
|
...navigatorMock,
|
||||||
|
|
||||||
|
// Some browser will set mediaDevices to undefined when access over http.
|
||||||
|
mediaDevices: undefined as unknown as MediaDevices,
|
||||||
|
};
|
||||||
|
|
||||||
// @vitest-environment jsdom
|
// @vitest-environment jsdom
|
||||||
describe('MicrophoneManager', () => {
|
describe('MicrophoneManager', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -19,6 +28,7 @@ describe('MicrophoneManager', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.resetAllMocks();
|
vi.resetAllMocks();
|
||||||
vi.unstubAllGlobals;
|
vi.unstubAllGlobals;
|
||||||
|
navigator.mediaDevices;
|
||||||
});
|
});
|
||||||
|
|
||||||
const createMockStream = (mute?: boolean): MediaStream => {
|
const createMockStream = (mute?: boolean): MediaStream => {
|
||||||
@@ -45,7 +55,7 @@ describe('MicrophoneManager', () => {
|
|||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
|
|
||||||
const stream = createMockStream();
|
const stream = createMockStream();
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(stream);
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(stream);
|
||||||
|
|
||||||
await manager.connect();
|
await manager.connect();
|
||||||
|
|
||||||
@@ -55,13 +65,35 @@ describe('MicrophoneManager', () => {
|
|||||||
expect(api.getCardElementManager().update).toBeCalled();
|
expect(api.getCardElementManager().update).toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be unsupported without browser support', () => {
|
||||||
|
vi.stubGlobal('navigator', medialessNavigatorMock);
|
||||||
|
|
||||||
|
const manager = new MicrophoneManager(createCardAPI());
|
||||||
|
|
||||||
|
expect(manager.isSupported()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not connect when not supported', async () => {
|
||||||
|
vi.stubGlobal('navigator', medialessNavigatorMock);
|
||||||
|
|
||||||
|
const api = createCardAPI();
|
||||||
|
const manager = new MicrophoneManager(api);
|
||||||
|
|
||||||
|
const stream = createMockStream();
|
||||||
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(stream);
|
||||||
|
|
||||||
|
await manager.connect();
|
||||||
|
|
||||||
|
expect(manager.isConnected()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should be forbidden when permission denied', async () => {
|
it('should be forbidden when permission denied', async () => {
|
||||||
// Don't actually log messages to the console during the test.
|
// Don't actually log messages to the console during the test.
|
||||||
vi.spyOn(global.console, 'warn').mockReturnValue(undefined);
|
vi.spyOn(global.console, 'warn').mockReturnValue(undefined);
|
||||||
|
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockRejectedValue(new Error());
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockRejectedValue(new Error());
|
||||||
|
|
||||||
expect(await manager.connect()).toBeFalsy();
|
expect(await manager.connect()).toBeFalsy();
|
||||||
|
|
||||||
@@ -73,7 +105,9 @@ describe('MicrophoneManager', () => {
|
|||||||
it('should mute and unmute', async () => {
|
it('should mute and unmute', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(createMockStream());
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(
|
||||||
|
createMockStream(),
|
||||||
|
);
|
||||||
|
|
||||||
await manager.connect();
|
await manager.connect();
|
||||||
expect(manager.isMuted()).toBeTruthy();
|
expect(manager.isMuted()).toBeTruthy();
|
||||||
@@ -91,7 +125,7 @@ describe('MicrophoneManager', () => {
|
|||||||
it('should not unmute when microphone forbidden', async () => {
|
it('should not unmute when microphone forbidden', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(null);
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockRejectedValue(new Error());
|
||||||
|
|
||||||
await manager.connect();
|
await manager.connect();
|
||||||
|
|
||||||
@@ -103,10 +137,23 @@ describe('MicrophoneManager', () => {
|
|||||||
expect(api.getCardElementManager().update).toBeCalledTimes(1);
|
expect(api.getCardElementManager().update).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not unmute when not supported', async () => {
|
||||||
|
vi.stubGlobal('navigator', medialessNavigatorMock);
|
||||||
|
|
||||||
|
const manager = new MicrophoneManager(createCardAPI());
|
||||||
|
|
||||||
|
await manager.unmute();
|
||||||
|
|
||||||
|
expect(manager.isConnected()).toBeFalsy();
|
||||||
|
expect(manager.isMuted()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should connect on unmute', async () => {
|
it('should connect on unmute', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(createMockStream());
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(
|
||||||
|
createMockStream(),
|
||||||
|
);
|
||||||
|
|
||||||
expect(manager.isConnected()).toBeFalsy();
|
expect(manager.isConnected()).toBeFalsy();
|
||||||
|
|
||||||
@@ -122,7 +169,9 @@ describe('MicrophoneManager', () => {
|
|||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
|
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(createMockStream());
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(
|
||||||
|
createMockStream(),
|
||||||
|
);
|
||||||
|
|
||||||
await manager.connect();
|
await manager.connect();
|
||||||
expect(manager.isConnected()).toBeTruthy();
|
expect(manager.isConnected()).toBeTruthy();
|
||||||
@@ -139,7 +188,9 @@ describe('MicrophoneManager', () => {
|
|||||||
const disconnectSeconds = 10;
|
const disconnectSeconds = 10;
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(createMockStream());
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(
|
||||||
|
createMockStream(),
|
||||||
|
);
|
||||||
|
|
||||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||||
createConfig({
|
createConfig({
|
||||||
@@ -168,7 +219,9 @@ describe('MicrophoneManager', () => {
|
|||||||
const disconnectSeconds = 10;
|
const disconnectSeconds = 10;
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(createMockStream());
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(
|
||||||
|
createMockStream(),
|
||||||
|
);
|
||||||
|
|
||||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||||
createConfig({
|
createConfig({
|
||||||
@@ -191,10 +244,64 @@ describe('MicrophoneManager', () => {
|
|||||||
expect(api.getCardElementManager().update).toBeCalledTimes(1);
|
expect(api.getCardElementManager().update).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('should require initialization', async () => {
|
||||||
|
it('when configured and supported', async () => {
|
||||||
|
const api = createCardAPI();
|
||||||
|
const manager = new MicrophoneManager(api);
|
||||||
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||||
|
createConfig({
|
||||||
|
live: {
|
||||||
|
microphone: {
|
||||||
|
always_connected: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await manager.connect();
|
||||||
|
|
||||||
|
expect(manager.shouldConnectOnInitialization()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when configured but not supported', async () => {
|
||||||
|
vi.stubGlobal('navigator', medialessNavigatorMock);
|
||||||
|
|
||||||
|
const api = createCardAPI();
|
||||||
|
const manager = new MicrophoneManager(api);
|
||||||
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||||
|
createConfig({
|
||||||
|
live: {
|
||||||
|
microphone: {
|
||||||
|
always_connected: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await manager.connect();
|
||||||
|
|
||||||
|
expect(manager.shouldConnectOnInitialization()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when neither configured nor supported', async () => {
|
||||||
|
vi.stubGlobal('navigator', medialessNavigatorMock);
|
||||||
|
|
||||||
|
const api = createCardAPI();
|
||||||
|
const manager = new MicrophoneManager(api);
|
||||||
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
|
|
||||||
|
await manager.connect();
|
||||||
|
|
||||||
|
expect(manager.shouldConnectOnInitialization()).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should respect listeners', async () => {
|
it('should respect listeners', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(createMockStream());
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(
|
||||||
|
createMockStream(),
|
||||||
|
);
|
||||||
|
|
||||||
const listener = vi.fn();
|
const listener = vi.fn();
|
||||||
manager.addListener(listener);
|
manager.addListener(listener);
|
||||||
@@ -235,7 +342,9 @@ describe('MicrophoneManager', () => {
|
|||||||
it('should set condition state', async () => {
|
it('should set condition state', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const manager = new MicrophoneManager(api);
|
const manager = new MicrophoneManager(api);
|
||||||
navigatorMock.mediaDevices.getUserMedia.mockReturnValue(createMockStream());
|
vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(
|
||||||
|
createMockStream(),
|
||||||
|
);
|
||||||
|
|
||||||
expect(api.getConditionsManager().setState).not.toBeCalled();
|
expect(api.getConditionsManager().setState).not.toBeCalled();
|
||||||
|
|
||||||
|
|||||||
@@ -903,7 +903,11 @@ describe('MenuButtonController', () => {
|
|||||||
|
|
||||||
describe('should have microphone button', () => {
|
describe('should have microphone button', () => {
|
||||||
it('with suitable loaded media', () => {
|
it('with suitable loaded media', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = mock<MicrophoneManager>();
|
||||||
|
vi.mocked(microphoneManager.isForbidden).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isMuted).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isSupported).mockReturnValue(true);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
microphoneManager: microphoneManager,
|
microphoneManager: microphoneManager,
|
||||||
currentMediaLoadedInfo: createMediaLoadedInfo({
|
currentMediaLoadedInfo: createMediaLoadedInfo({
|
||||||
@@ -935,7 +939,11 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('without suitable loaded media', () => {
|
it('without suitable loaded media', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = mock<MicrophoneManager>();
|
||||||
|
vi.mocked(microphoneManager.isForbidden).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isMuted).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isSupported).mockReturnValue(true);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
microphoneManager: microphoneManager,
|
microphoneManager: microphoneManager,
|
||||||
currentMediaLoadedInfo: createMediaLoadedInfo({
|
currentMediaLoadedInfo: createMediaLoadedInfo({
|
||||||
@@ -951,8 +959,9 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('with forbidden microphone', () => {
|
it('with forbidden microphone', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = mock<MicrophoneManager>();
|
||||||
mock<MicrophoneManager>(microphoneManager).isForbidden.mockReturnValue(true);
|
vi.mocked(microphoneManager.isForbidden).mockReturnValue(true);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
microphoneManager: microphoneManager,
|
microphoneManager: microphoneManager,
|
||||||
currentMediaLoadedInfo: createMediaLoadedInfo({
|
currentMediaLoadedInfo: createMediaLoadedInfo({
|
||||||
@@ -973,8 +982,11 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('with muted microphone', () => {
|
it('with muted microphone', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = mock<MicrophoneManager>();
|
||||||
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(true);
|
vi.mocked(microphoneManager.isForbidden).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isMuted).mockReturnValue(true);
|
||||||
|
vi.mocked(microphoneManager.isSupported).mockReturnValue(true);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
microphoneManager: microphoneManager,
|
microphoneManager: microphoneManager,
|
||||||
currentMediaLoadedInfo: createMediaLoadedInfo({
|
currentMediaLoadedInfo: createMediaLoadedInfo({
|
||||||
@@ -1002,9 +1014,37 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('with unsupported microphone', () => {
|
||||||
|
const microphoneManager = mock<MicrophoneManager>();
|
||||||
|
vi.mocked(microphoneManager.isForbidden).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isMuted).mockReturnValue(true);
|
||||||
|
vi.mocked(microphoneManager.isSupported).mockReturnValue(false);
|
||||||
|
|
||||||
|
const buttons = calculateButtons(controller, {
|
||||||
|
microphoneManager: microphoneManager,
|
||||||
|
currentMediaLoadedInfo: createMediaLoadedInfo({
|
||||||
|
capabilities: {
|
||||||
|
supports2WayAudio: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(buttons).toContainEqual({
|
||||||
|
icon: 'mdi:microphone-message-off',
|
||||||
|
enabled: false,
|
||||||
|
priority: 50,
|
||||||
|
type: 'custom:frigate-card-menu-icon',
|
||||||
|
title: 'Microphone',
|
||||||
|
style: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('with muted toggle type microphone', () => {
|
it('with muted toggle type microphone', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = mock<MicrophoneManager>();
|
||||||
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(true);
|
vi.mocked(microphoneManager.isForbidden).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isMuted).mockReturnValue(true);
|
||||||
|
vi.mocked(microphoneManager.isSupported).mockReturnValue(true);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
microphoneManager: microphoneManager,
|
microphoneManager: microphoneManager,
|
||||||
currentMediaLoadedInfo: createMediaLoadedInfo({
|
currentMediaLoadedInfo: createMediaLoadedInfo({
|
||||||
@@ -1032,8 +1072,11 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('with unmuted toggle type microphone', () => {
|
it('with unmuted toggle type microphone', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = mock<MicrophoneManager>();
|
||||||
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(false);
|
vi.mocked(microphoneManager.isForbidden).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isMuted).mockReturnValue(false);
|
||||||
|
vi.mocked(microphoneManager.isSupported).mockReturnValue(true);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
microphoneManager: microphoneManager,
|
microphoneManager: microphoneManager,
|
||||||
currentMediaLoadedInfo: createMediaLoadedInfo({
|
currentMediaLoadedInfo: createMediaLoadedInfo({
|
||||||
|
|||||||
Reference in New Issue
Block a user