Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/extension/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,9 @@ export const activate = (context: vscode.ExtensionContext) => {
const terminalManager = TerminalManager.create(eventBus);
const configManager = ConfigManager.create({ configWriter, eventBus, localStorage });
const statusBarManager = StatusBarManager.create({
configManager,
configReader,
eventBus,
statusBarCreator,
store: appStore,
});
const treeProvider = CommandTreeProvider.create({ configManager, configReader, eventBus });
const importExportManager = ImportExportManager.create({
Expand All @@ -347,7 +346,6 @@ export const activate = (context: vscode.ExtensionContext) => {
eventBus,
});

statusBarManager.setButtonSetManager(buttonSetManager);
treeProvider.setButtonSetManager(buttonSetManager);

const webviewProvider = new ConfigWebviewProvider(
Expand Down
141 changes: 43 additions & 98 deletions src/internal/managers/status-bar-manager.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ButtonConfig } from "../../pkg/types";
import { EventBus } from "../event-bus";
import { createAppStore, AppStoreInstance } from "../stores";
import {
calculateButtonPriority,
createTooltipText,
Expand Down Expand Up @@ -188,7 +188,7 @@
});

describe("configureRefreshButton", () => {
let mockStatusBarItem: any;

Check warning on line 191 in src/internal/managers/status-bar-manager.spec.ts

View workflow job for this annotation

GitHub Actions / Lint Extension Code

Unexpected any. Specify a different type

beforeEach(() => {
mockStatusBarItem = {
Expand Down Expand Up @@ -231,7 +231,7 @@

it("should handle refresh config with color set to undefined", () => {
const refreshConfig = {
color: undefined as any,

Check warning on line 234 in src/internal/managers/status-bar-manager.spec.ts

View workflow job for this annotation

GitHub Actions / Lint Extension Code

Unexpected any. Specify a different type
enabled: true,
icon: "↻",
};
Expand Down Expand Up @@ -261,7 +261,7 @@

it("should handle refresh config with null color", () => {
const refreshConfig = {
color: null as any,

Check warning on line 264 in src/internal/managers/status-bar-manager.spec.ts

View workflow job for this annotation

GitHub Actions / Lint Extension Code

Unexpected any. Specify a different type
enabled: true,
icon: "↺",
};
Expand Down Expand Up @@ -294,7 +294,7 @@
});

describe("configureSetIndicator", () => {
let mockStatusBarItem: any;

Check warning on line 297 in src/internal/managers/status-bar-manager.spec.ts

View workflow job for this annotation

GitHub Actions / Lint Extension Code

Unexpected any. Specify a different type

beforeEach(() => {
mockStatusBarItem = {
Expand Down Expand Up @@ -338,10 +338,9 @@
});

describe("StatusBarManager", () => {
let mockConfigReader: any;

Check warning on line 341 in src/internal/managers/status-bar-manager.spec.ts

View workflow job for this annotation

GitHub Actions / Lint Extension Code

Unexpected any. Specify a different type
let mockStatusBarCreator: any;

Check warning on line 342 in src/internal/managers/status-bar-manager.spec.ts

View workflow job for this annotation

GitHub Actions / Lint Extension Code

Unexpected any. Specify a different type
let mockConfigManager: any;
let eventBus: EventBus;
let mockStore: AppStoreInstance;
let statusBarManager: StatusBarManager;

// Mock vscode module to avoid undefined errors
Expand All @@ -367,166 +366,114 @@
}),
};

mockStatusBarCreator = jest.fn().mockReturnValue({
mockStatusBarCreator = jest.fn().mockImplementation(() => ({
color: "",
command: "",
dispose: jest.fn(),
show: jest.fn(),
text: "",
tooltip: "",
});

mockConfigManager = {
getButtonsWithFallback: jest.fn().mockReturnValue({
buttons: [
{ command: "echo test1", id: "btn-1", name: "Test Button 1" },
{ command: "echo test2", id: "btn-2", name: "Test Button 2" },
],
}),
};
}));

eventBus = new EventBus();
mockStore = createAppStore();
mockStore.getState().setButtons([
{ command: "echo test1", id: "btn-1", name: "Test Button 1" },
{ command: "echo test2", id: "btn-2", name: "Test Button 2" },
]);
});

afterEach(() => {
if (statusBarManager) {
statusBarManager.dispose();
}
eventBus.dispose();
});

describe("EventBus integration", () => {
it("should subscribe to config:changed event when eventBus is provided", () => {
const eventBusSpy = jest.spyOn(eventBus, "on");

statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
eventBus,
statusBarCreator: mockStatusBarCreator,
});

expect(eventBusSpy).toHaveBeenCalledWith("config:changed", expect.any(Function));
expect(eventBusSpy).toHaveBeenCalledWith("buttonSet:switched", expect.any(Function));
});
describe("Store subscription", () => {
it("should subscribe to store on creation", () => {
const subscribeSpy = jest.spyOn(mockStore, "subscribe");

it("should not subscribe to events when eventBus is not provided", () => {
statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
statusBarCreator: mockStatusBarCreator,
store: mockStore,
});

// Should not throw errors - manager should handle missing eventBus gracefully
expect(statusBarManager).toBeDefined();
expect(subscribeSpy).toHaveBeenCalled();
});

it("should call refreshButtons when config:changed event is emitted", () => {
it("should call refreshButtons when store buttons change", () => {
statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
eventBus,
statusBarCreator: mockStatusBarCreator,
store: mockStore,
});

const refreshSpy = jest.spyOn(statusBarManager, "refreshButtons");

eventBus.emit("config:changed", { scope: "local" });
mockStore
.getState()
.setButtons([{ command: "echo new", id: "new-btn", name: "New Button" }]);

expect(refreshSpy).toHaveBeenCalled();
});

it("should call refreshButtons when buttonSet:switched event is emitted", () => {
it("should call refreshButtons when activeSet changes", () => {
statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
eventBus,
statusBarCreator: mockStatusBarCreator,
store: mockStore,
});

const refreshSpy = jest.spyOn(statusBarManager, "refreshButtons");

eventBus.emit("buttonSet:switched", { setName: "Frontend" });
mockStore.getState().setActiveSet("Frontend");

expect(refreshSpy).toHaveBeenCalled();
});

it("should unsubscribe from events when disposed", () => {
statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
eventBus,
statusBarCreator: mockStatusBarCreator,
});

const refreshSpy = jest.spyOn(statusBarManager, "refreshButtons");

// Dispose the manager
statusBarManager.dispose();

// Clear the spy to reset call count
refreshSpy.mockClear();

// Emit events after disposal
eventBus.emit("config:changed", { scope: "workspace" });
eventBus.emit("buttonSet:switched", { setName: null });
it("should use buttons from store for status bar", () => {
mockStore
.getState()
.setButtons([{ command: "echo store", id: "store-btn", name: "Store Button" }]);

// refreshButtons should not be called after disposal
expect(refreshSpy).not.toHaveBeenCalled();
});

it("should handle multiple event emissions", () => {
statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
eventBus,
statusBarCreator: mockStatusBarCreator,
store: mockStore,
});

// Mock refreshButtons to avoid vscode module issues
const refreshSpy = jest.spyOn(statusBarManager, "refreshButtons").mockImplementation(() => {
// No-op for test
});

eventBus.emit("config:changed", { scope: "local" });
eventBus.emit("buttonSet:switched", { setName: "Backend" });
eventBus.emit("config:changed", { scope: "global" });
statusBarManager.refreshButtons();

expect(refreshSpy).toHaveBeenCalledTimes(3);
const createdItems = mockStatusBarCreator.mock.results.map((r: any) => r.value);
const buttonItem = createdItems.find((item: any) => item.text === "Store Button");
expect(buttonItem).toBeDefined();
});

it("should preserve button set manager reference after event refresh", () => {
const mockButtonSetManager = {
getActiveSet: jest.fn().mockReturnValue("TestSet"),
getButtonsForActiveSet: jest
.fn()
.mockReturnValue([{ command: "echo set", id: "set-btn", name: "Set Button" }]),
};
it("should display activeSet from store in set indicator", () => {
mockStore.getState().setActiveSet("TestSet");

statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
eventBus,
statusBarCreator: mockStatusBarCreator,
store: mockStore,
});

// Set up the button set manager
statusBarManager.setButtonSetManager(mockButtonSetManager as any);

// Manually trigger refresh to verify button set manager is used
// Call refreshButtons to create status bar items
mockStatusBarCreator.mockClear();
statusBarManager.refreshButtons();

// Verify that button set manager is still accessible after refresh
expect(mockButtonSetManager.getButtonsForActiveSet).toHaveBeenCalled();
const createdItems = mockStatusBarCreator.mock.results.map((r: any) => r.value);
const setIndicator = createdItems.find((item: any) => item.text === "$(layers) [TestSet]");
expect(setIndicator).toBeDefined();
});
});

describe("dispose", () => {
it("should dispose all status bar items", () => {
statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
statusBarCreator: mockStatusBarCreator,
store: mockStore,
});

statusBarManager.refreshButtons();
Expand All @@ -540,19 +487,17 @@
});
});

it("should clear unsubscribers array", () => {
it("should unsubscribe from store when disposed", () => {
statusBarManager = StatusBarManager.create({
configManager: mockConfigManager,
configReader: mockConfigReader,
eventBus,
statusBarCreator: mockStatusBarCreator,
store: mockStore,
});

statusBarManager.dispose();

// Verify unsubscribers were cleared by checking events don't trigger after disposal
const refreshSpy = jest.spyOn(statusBarManager, "refreshButtons");
eventBus.emit("config:changed", { scope: "local" });
mockStore.getState().setButtons([]);

expect(refreshSpy).not.toHaveBeenCalled();
});
Expand Down
Loading
Loading