@@ -5,6 +5,14 @@ import { activationReasonAtom, activationStatusAtom } from "../../atoms/activati
55import { connectionStatusAtom , lastReconnectAttemptAtom } from "../../atoms/connection" ;
66import { ConnectionStatusBanner } from "./connection-status-banner" ;
77
8+ const viewportMocks = vi . hoisted ( ( ) => ( {
9+ value : "desktop" as "desktop" | "mobile" ,
10+ } ) ) ;
11+
12+ vi . mock ( "../../components/ui/_internal/use-viewport" , ( ) => ( {
13+ useViewport : ( ) => viewportMocks . value ,
14+ } ) ) ;
15+
816function renderBanner ( ) {
917 const store = createStore ( ) ;
1018
@@ -24,6 +32,7 @@ describe("ConnectionStatusBanner", () => {
2432
2533 afterEach ( ( ) => {
2634 vi . useRealTimers ( ) ;
35+ viewportMocks . value = "desktop" ;
2736 } ) ;
2837
2938 it ( "renders the unified reconnect message while reconnecting" , ( ) => {
@@ -36,6 +45,18 @@ describe("ConnectionStatusBanner", () => {
3645 expect ( screen . getByText ( "连接已断开,正在重新连接..." ) ) . toBeInTheDocument ( ) ;
3746 } ) ;
3847
48+ it ( "uses the shared mobile banner layout while reconnecting" , ( ) => {
49+ viewportMocks . value = "mobile" ;
50+ const store = renderBanner ( ) ;
51+
52+ act ( ( ) => {
53+ store . set ( connectionStatusAtom , "reconnecting" ) ;
54+ } ) ;
55+
56+ expect ( screen . getByRole ( "status" ) ) . toHaveClass ( "connection-banner--mobile" ) ;
57+ expect ( screen . getByRole ( "status" ) ) . not . toHaveClass ( "connection-banner--stacked" ) ;
58+ } ) ;
59+
3960 it ( "shows the displaced-session message instead of reconnecting when activation is gated" , ( ) => {
4061 const store = renderBanner ( ) ;
4162
@@ -49,6 +70,21 @@ describe("ConnectionStatusBanner", () => {
4970 expect ( screen . queryByText ( "连接已断开,正在重新连接..." ) ) . not . toBeInTheDocument ( ) ;
5071 } ) ;
5172
73+ it ( "uses the shared mobile banner layout for displaced-session state" , ( ) => {
74+ viewportMocks . value = "mobile" ;
75+ const store = renderBanner ( ) ;
76+
77+ act ( ( ) => {
78+ store . set ( activationStatusAtom , "gated" ) ;
79+ store . set ( activationReasonAtom , "displaced" ) ;
80+ store . set ( connectionStatusAtom , "disconnected" ) ;
81+ } ) ;
82+
83+ expect ( screen . getByRole ( "status" ) ) . toHaveClass ( "connection-banner--mobile" ) ;
84+ expect ( screen . getByRole ( "status" ) ) . toHaveClass ( "connection-banner--error" ) ;
85+ expect ( screen . getByText ( "另一个标签页已激活" ) ) . toBeInTheDocument ( ) ;
86+ } ) ;
87+
5288 it ( "shows the slow recovery hint after 25 seconds" , ( ) => {
5389 const startedAt = new Date ( "2026-05-14T00:00:00.000Z" ) . getTime ( ) ;
5490 vi . setSystemTime ( startedAt + 25_000 ) ;
@@ -101,4 +137,23 @@ describe("ConnectionStatusBanner", () => {
101137 screen . queryByText ( "连接恢复较慢,可能是网络问题。如果长时间没有恢复,可以刷新页面。" )
102138 ) . not . toBeInTheDocument ( ) ;
103139 } ) ;
140+
141+ it ( "stacks the slow recovery hint and uses compact copy on mobile" , ( ) => {
142+ viewportMocks . value = "mobile" ;
143+ const startedAt = new Date ( "2026-05-14T00:00:00.000Z" ) . getTime ( ) ;
144+ vi . setSystemTime ( startedAt + 25_000 ) ;
145+ const store = renderBanner ( ) ;
146+
147+ act ( ( ) => {
148+ store . set ( connectionStatusAtom , "reconnecting" ) ;
149+ store . set ( lastReconnectAttemptAtom , startedAt ) ;
150+ } ) ;
151+
152+ expect ( screen . getByRole ( "status" ) ) . toHaveClass ( "connection-banner--mobile" ) ;
153+ expect ( screen . getByRole ( "status" ) ) . toHaveClass ( "connection-banner--stacked" ) ;
154+ expect ( screen . getByText ( "连接恢复较慢,长时间未恢复可刷新页面。" ) ) . toBeInTheDocument ( ) ;
155+ expect (
156+ screen . queryByText ( "连接恢复较慢,可能是网络问题。如果长时间没有恢复,可以刷新页面。" )
157+ ) . not . toBeInTheDocument ( ) ;
158+ } ) ;
104159} ) ;
0 commit comments