diff --git a/test/extension/deviceStatusIndicator.test.ts b/test/extension/deviceStatusIndicator.test.ts new file mode 100644 index 000000000..d3d323581 --- /dev/null +++ b/test/extension/deviceStatusIndicator.test.ts @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +import assert = require("assert"); +import * as vscode from "vscode"; +import * as sinon from "sinon"; +import { DeviceStatusIndicator } from "../../src/extension/deviceStatusIndicator"; + +function makeFakeStatusBarItem(): vscode.StatusBarItem { + return { + id: "", + name: "", + text: "", + tooltip: undefined, + color: undefined, + backgroundColor: undefined, + command: undefined, + accessibilityInformation: undefined, + alignment: vscode.StatusBarAlignment.Left, + priority: undefined, + show: sinon.stub(), + hide: sinon.stub(), + dispose: sinon.stub(), + } as unknown as vscode.StatusBarItem; +} + +suite("DeviceStatusIndicator", function () { + suite("extensionContext", function () { + let createStatusBarItemStub: Sinon.SinonStub; + let fakeItem: vscode.StatusBarItem; + setup(() => { + fakeItem = makeFakeStatusBarItem(); + createStatusBarItemStub = sinon.stub(vscode.window, "createStatusBarItem"); + createStatusBarItemStub.returns(fakeItem); + }); + teardown(() => { + const instance = (DeviceStatusIndicator as any).instance; + if (instance) { + instance.dispose(); + } + createStatusBarItemStub.restore(); + }); + test("show should set correct icon and device name in text", function () { + DeviceStatusIndicator.show("iPhone 15"); + assert.strictEqual(fakeItem.text, "$(device-mobile) iPhone 15"); + }); + test("show should set correct tooltip with device name", function () { + DeviceStatusIndicator.show("Pixel 7"); + assert.strictEqual(fakeItem.tooltip, "Active debug target: Pixel 7"); + }); + test("show should call show on the status bar item", function () { + DeviceStatusIndicator.show("iPhone 15"); + assert.ok((fakeItem.show as Sinon.SinonStub).calledOnce); + }); + test("hide should call hide on the status bar item after show", function () { + DeviceStatusIndicator.show("iPhone 15"); + DeviceStatusIndicator.hide(); + assert.ok((fakeItem.hide as Sinon.SinonStub).calledOnce); + }); + test("hide before show should not throw", function () { + assert.doesNotThrow(() => DeviceStatusIndicator.hide()); + }); + test("show should reuse singleton instance across multiple calls", function () { + DeviceStatusIndicator.show("iPhone 15"); + DeviceStatusIndicator.show("Pixel 7"); + assert.strictEqual(createStatusBarItemStub.callCount, 1); + assert.strictEqual(fakeItem.text, "$(device-mobile) Pixel 7"); + }); + test("dispose should clear the singleton instance", function () { + DeviceStatusIndicator.show("iPhone 15"); + const instance = (DeviceStatusIndicator as any).instance as DeviceStatusIndicator; + instance.dispose(); + assert.strictEqual((DeviceStatusIndicator as any).instance, undefined); + }); + }); +}); diff --git a/test/extension/packagerStatusIndicator.test.ts b/test/extension/packagerStatusIndicator.test.ts new file mode 100644 index 000000000..204f6ba9c --- /dev/null +++ b/test/extension/packagerStatusIndicator.test.ts @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +import assert = require("assert"); +import * as vscode from "vscode"; +import * as sinon from "sinon"; +import { + PackagerStatus, + PackagerStatusIndicator, +} from "../../src/extension/packagerStatusIndicator"; +import { SettingsHelper } from "../../src/extension/settingsHelper"; + +function makeFakeStatusBarItem(): vscode.StatusBarItem { + return { + id: "", + name: "", + text: "", + tooltip: undefined, + color: undefined, + backgroundColor: undefined, + command: undefined, + accessibilityInformation: undefined, + alignment: vscode.StatusBarAlignment.Left, + priority: undefined, + show: sinon.stub(), + hide: sinon.stub(), + dispose: sinon.stub(), + } as unknown as vscode.StatusBarItem; +} + +suite("PackagerStatusIndicator", function () { + suite("extensionContext", function () { + let createStatusBarItemStub: Sinon.SinonStub; + let getShowIndicatorStub: Sinon.SinonStub; + let getPatternStub: Sinon.SinonStub; + let fakeToggleItem: vscode.StatusBarItem; + let fakeRestartItem: vscode.StatusBarItem; + let indicator: PackagerStatusIndicator; + const PROJECT_ROOT = "/workspace"; + setup(() => { + fakeRestartItem = makeFakeStatusBarItem(); + fakeToggleItem = makeFakeStatusBarItem(); + createStatusBarItemStub = sinon.stub(vscode.window, "createStatusBarItem"); + createStatusBarItemStub.onFirstCall().returns(fakeRestartItem); + createStatusBarItemStub.onSecondCall().returns(fakeToggleItem); + getShowIndicatorStub = sinon.stub(SettingsHelper, "getShowIndicator").returns(true); + getPatternStub = sinon + .stub(SettingsHelper, "getPackagerStatusIndicatorPattern") + .returns(PackagerStatusIndicator.FULL_VERSION); + indicator = new PackagerStatusIndicator(PROJECT_ROOT); + }); + teardown(() => { + indicator.dispose(); + createStatusBarItemStub.restore(); + getShowIndicatorStub.restore(); + getPatternStub.restore(); + }); + test("should display port in full version when packager starts with port", function () { + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED, 8081); + assert.ok( + (fakeToggleItem.text as string).includes(":8081"), + `Expected text to contain ':8081', got: ${fakeToggleItem.text}`, + ); + }); + test("should not display port in full version when packager starts without port", function () { + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED); + assert.ok( + !(fakeToggleItem.text as string).includes(":"), + `Expected text to not contain port, got: ${fakeToggleItem.text}`, + ); + }); + test("should clear port from status bar text when packager stops", function () { + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED, 8081); + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STOPPED); + assert.ok( + !(fakeToggleItem.text as string).includes(":8081"), + `Expected text to not contain ':8081' after stop, got: ${fakeToggleItem.text}`, + ); + }); + test("should display port in short version when packager starts with port", function () { + getPatternStub.returns(PackagerStatusIndicator.SHORT_VERSION); + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED, 9090); + assert.ok( + (fakeToggleItem.text as string).includes(":9090"), + `Expected short version text to contain ':9090', got: ${fakeToggleItem.text}`, + ); + }); + test("should retain port across status transitions from starting to started", function () { + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTING, 8081); + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED); + assert.ok( + (fakeToggleItem.text as string).includes(":8081"), + `Expected port to be retained after STARTED, got: ${fakeToggleItem.text}`, + ); + }); + test("should update port when called with a new port value", function () { + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED, 8081); + indicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED, 9090); + assert.ok( + (fakeToggleItem.text as string).includes(":9090"), + `Expected text to contain updated port ':9090', got: ${fakeToggleItem.text}`, + ); + assert.ok( + !(fakeToggleItem.text as string).includes(":8081"), + `Expected old port ':8081' to be gone, got: ${fakeToggleItem.text}`, + ); + }); + }); +});