Skip to content
Open
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
79 changes: 79 additions & 0 deletions packages/suite-base/src/Workspace.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "@lichtblick/suite-base/components/MessagePipeline";
import Sidebars from "@lichtblick/suite-base/components/Sidebars";
import { SidebarItem } from "@lichtblick/suite-base/components/Sidebars/types";
import { useAlertsActions } from "@lichtblick/suite-base/context/AlertsContext";
import { useAppContext } from "@lichtblick/suite-base/context/AppContext";
import {
useCurrentUser,
Expand Down Expand Up @@ -153,6 +154,9 @@ jest.mock("@lichtblick/suite-base/context/CurrentUserContext", () => ({
useCurrentUser: jest.fn(),
useCurrentUserType: jest.fn(),
}));
jest.mock("@lichtblick/suite-base/context/AlertsContext", () => ({
useAlertsActions: jest.fn(),
}));
jest.mock("@lichtblick/suite-base/context/EventsContext", () => ({
useEvents: jest.fn(),
}));
Expand Down Expand Up @@ -250,6 +254,7 @@ const mockWorkspaceActions = {
},
openLayoutBrowser: jest.fn(),
};
const mockClearAlerts = jest.fn();

describe("Workspace - alerts badge in leftSidebarItems", () => {
beforeEach(() => {
Expand All @@ -274,6 +279,7 @@ describe("Workspace - alerts badge in leftSidebarItems", () => {
(useAppConfigurationValue as jest.Mock).mockReturnValue([false]);
(useCurrentUser as jest.Mock).mockReturnValue({ currentUser: undefined, signIn: undefined });
(useCurrentUserType as jest.Mock).mockReturnValue("unauthenticated");
(useAlertsActions as jest.Mock).mockReturnValue({ clearAlerts: mockClearAlerts });
(useEvents as jest.Mock).mockImplementation(
(selector: (store: { eventsSupported: boolean; selectEvent: jest.Mock }) => unknown) =>
selector({ eventsSupported: false, selectEvent: jest.fn() }),
Expand All @@ -287,7 +293,9 @@ describe("Workspace - alerts badge in leftSidebarItems", () => {
});

afterEach(() => {
mockClearAlerts.mockClear();
MockedSidebars.mockClear();
mockPipelineContext.playerState.playerId = "";
});

it("should not set badge on alerts sidebar item when alertCount is 0", () => {
Expand Down Expand Up @@ -338,3 +346,74 @@ describe("Workspace - alerts badge in leftSidebarItems", () => {
expect(leftItems.get("alerts")?.badge?.count).toBe(5);
});
});

describe("Workspace - session alerts reset on player change", () => {
beforeEach(() => {
(useMessagePipeline as jest.Mock).mockImplementation(
(selector: (ctx: typeof mockPipelineContext) => unknown) => selector(mockPipelineContext),
);
(useMessagePipelineGetter as jest.Mock).mockReturnValue(() => mockPipelineContext);
(useWorkspaceStore as jest.Mock).mockImplementation(
(selector: (store: typeof mockWorkspaceStore) => unknown) => selector(mockWorkspaceStore),
);
(useWorkspaceActions as jest.Mock).mockReturnValue(mockWorkspaceActions);
(usePlayerSelection as jest.Mock).mockReturnValue({
availableSources: [],
selectSource: jest.fn(),
});
(useAlertCount as jest.Mock).mockReturnValue({
playerAlerts: [],
sessionAlerts: [],
alertCount: 0,
});
(useHandleFiles as jest.Mock).mockReturnValue({ handleFiles: jest.fn() });
(useAppConfigurationValue as jest.Mock).mockReturnValue([false]);
(useCurrentUser as jest.Mock).mockReturnValue({ currentUser: undefined, signIn: undefined });
(useCurrentUserType as jest.Mock).mockReturnValue("unauthenticated");
(useAlertsActions as jest.Mock).mockReturnValue({ clearAlerts: mockClearAlerts });
(useEvents as jest.Mock).mockImplementation(
(selector: (store: { eventsSupported: boolean; selectEvent: jest.Mock }) => unknown) =>
selector({ eventsSupported: false, selectEvent: jest.fn() }),
);
(useAppContext as jest.Mock).mockReturnValue({
PerformanceSidebarComponent: undefined,
sidebarItems: [],
layoutBrowser: undefined,
workspaceStoreCreator: undefined,
});
});

afterEach(() => {
mockClearAlerts.mockClear();
MockedSidebars.mockClear();
mockPipelineContext.playerState.playerId = "";
});

it("clears session alerts when playerId changes", () => {
// Given
mockPipelineContext.playerState.playerId = "player-1";

const { rerender } = render(<Workspace />);
expect(mockClearAlerts).not.toHaveBeenCalled();

// When
mockPipelineContext.playerState.playerId = "player-2";
rerender(<Workspace />);

// Then
expect(mockClearAlerts).toHaveBeenCalledTimes(1);
});

it("does not clear session alerts when playerId stays the same", () => {
// Given
mockPipelineContext.playerState.playerId = "player-1";
const { rerender } = render(<Workspace />);
mockClearAlerts.mockClear();

// When
rerender(<Workspace />);

// Then
expect(mockClearAlerts).not.toHaveBeenCalled();
});
});
11 changes: 11 additions & 0 deletions packages/suite-base/src/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { TopicList } from "@lichtblick/suite-base/components/TopicList";
import VariablesList from "@lichtblick/suite-base/components/VariablesList";
import { WorkspaceDialogs } from "@lichtblick/suite-base/components/WorkspaceDialogs";
import { AllowedFileExtensions } from "@lichtblick/suite-base/constants/allowedFileExtensions";
import { useAlertsActions } from "@lichtblick/suite-base/context/AlertsContext";
import { useAppContext } from "@lichtblick/suite-base/context/AppContext";
import {
LayoutState,
Expand Down Expand Up @@ -186,6 +187,8 @@ function WorkspaceContent(props: WorkspaceProps): React.JSX.Element {
// We use playerId to detect when a player changes for RemountOnValueChange below
// see comment below above the RemountOnValueChange component
const playerId = useMessagePipeline(selectPlayerId);
const { clearAlerts } = useAlertsActions();
const previousPlayerIdRef = useRef<string | undefined>();

const currentUserType = useCurrentUserType();

Expand Down Expand Up @@ -224,6 +227,14 @@ function WorkspaceContent(props: WorkspaceProps): React.JSX.Element {
}
}, [dialogActions.dataSource, playerPresence]);

// Session alerts from message converters should not carry over when switching to a new player.
useEffect(() => {
if (previousPlayerIdRef.current != undefined && previousPlayerIdRef.current !== playerId) {
clearAlerts();
}
previousPlayerIdRef.current = playerId;
}, [clearAlerts, playerId]);

useEffect(() => {
// Focus on page load to enable keyboard interaction.
if (containerRef.current) {
Expand Down
1 change: 1 addition & 0 deletions packages/suite-base/src/context/AlertsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type AlertsContextStore = Immutable<{
alerts: TaggedAlert[];
actions: {
clearAlert: (tag: string) => void;
clearAlerts: () => void;
setAlert: (tag: string, alert: Immutable<SessionAlert>) => void;
};
}>;
Expand Down
32 changes: 32 additions & 0 deletions packages/suite-base/src/providers/AlertsContextProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { PropsWithChildren } from "react";

import {
AlertsContextStore,
SessionAlert,
useAlertsActions,
useAlertsStore,
} from "@lichtblick/suite-base/context/AlertsContext";
Expand Down Expand Up @@ -98,4 +99,35 @@ describe("AlertsContextProvider", () => {
expect(result.current.alerts).toHaveLength(1);
expect(result.current.alerts[0]).toMatchObject({ tag: alertTag, ...updatedAlert });
});

it("clears all alerts when clearAlerts is called", () => {
// Given
const firstAlert: SessionAlert = { severity: "warn", message: "first" };
const secondAlert: SessionAlert = { severity: "error", message: "second" };
const firstTag = BasicBuilder.string();
const secondTag = BasicBuilder.string();

const { result } = renderHook(
() => ({
alerts: useAlertsStore(selectAlerts),
actions: useAlertsActions(),
}),
{ wrapper },
);

act(() => {
result.current.actions.setAlert(firstTag, firstAlert);
result.current.actions.setAlert(secondTag, secondAlert);
});

expect(result.current.alerts).toHaveLength(2);

// When
act(() => {
result.current.actions.clearAlerts();
});

// Then
expect(result.current.alerts).toHaveLength(0);
});
});
3 changes: 3 additions & 0 deletions packages/suite-base/src/providers/AlertsContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ function createAlertsStore(): StoreApi<AlertsContextStore> {
alerts: get().alerts.filter((al) => al.tag !== tag),
});
},
clearAlerts: () => {
set({ alerts: [] });
},
setAlert: (tag: string, alert: Immutable<SessionAlert>) => {
const newAlert = { tag, ...alert };
const alerts = get().alerts;
Expand Down
Loading