1+ """
2+ Improved integration test for hot reload persistence.
3+ This test focuses on validating the core state persistence mechanism
4+ without depending on specific board content.
5+ """
6+
7+ import asyncio
8+ import json
9+ from pathlib import Path
10+
11+ import pytest
12+ from playwright .async_api import async_playwright , expect
13+
14+
15+ class TestHotReloadIntegrationImproved :
16+ """Integration tests for hot reload state persistence."""
17+
18+ @pytest .mark .asyncio
19+ async def test_state_persistence_mechanism (self ):
20+ """Test that the state persistence mechanism works correctly."""
21+ async with async_playwright () as p :
22+ browser = await p .chromium .launch (headless = True )
23+ page = await browser .new_page ()
24+
25+ try :
26+ # Navigate to the app
27+ await page .goto ("http://localhost:8080" )
28+ await page .wait_for_load_state ("networkidle" )
29+
30+ # Verify board is loaded
31+ tiles = await page .locator ("[style*='cursor: pointer']" ).all ()
32+ assert len (tiles ) == 25 , f"Should have exactly 25 tiles, got { len (tiles )} "
33+
34+ # Map tile indices to board positions
35+ # Board is 5x5, so index = row * 5 + col
36+ tiles_to_click = [
37+ (0 , 0 ), # Top-left
38+ (0 , 4 ), # Top-right
39+ (1 , 1 ), # Second row, second column
40+ (3 , 2 ), # Fourth row, middle
41+ ]
42+
43+ # Click tiles by their board position
44+ for row , col in tiles_to_click :
45+ index = row * 5 + col
46+ await tiles [index ].click ()
47+ await asyncio .sleep (0.1 ) # Small delay for state save
48+
49+ # Wait a bit longer to ensure all saves complete
50+ await asyncio .sleep (0.5 )
51+
52+ # Verify state file exists and contains our clicks
53+ state_file = Path ("game_state.json" )
54+ assert state_file .exists (), "State file should exist"
55+
56+ with open (state_file , 'r' ) as f :
57+ state_before = json .load (f )
58+
59+ # Convert to set of tuples for easier comparison
60+ clicked_before = {tuple (pos ) for pos in state_before ['clicked_tiles' ]}
61+
62+ # Should have our 4 clicks + FREE MEAT at (2,2)
63+ assert len (clicked_before ) == 5 , f"Should have 5 clicked tiles, got { len (clicked_before )} "
64+
65+ # Verify our clicked positions are in the state
66+ for pos in tiles_to_click :
67+ assert pos in clicked_before , f"Position { pos } should be in clicked tiles"
68+
69+ # Verify FREE MEAT is clicked
70+ assert (2 , 2 ) in clicked_before , "FREE MEAT at (2,2) should be clicked"
71+
72+ # Take screenshot before reload for debugging
73+ await page .screenshot (path = "test_before_reload.png" )
74+
75+ # Reload the page
76+ await page .reload ()
77+ await page .wait_for_load_state ("networkidle" )
78+
79+ # Take screenshot after reload for debugging
80+ await page .screenshot (path = "test_after_reload.png" )
81+
82+ # Verify state file still exists and has same data
83+ with open (state_file , 'r' ) as f :
84+ state_after = json .load (f )
85+
86+ clicked_after = {tuple (pos ) for pos in state_after ['clicked_tiles' ]}
87+
88+ # State should be identical
89+ assert clicked_before == clicked_after , "Clicked tiles should be preserved after reload"
90+
91+ # Verify board hasn't changed
92+ assert state_before ['board' ] == state_after ['board' ], "Board should remain the same"
93+ assert state_before ['board_iteration' ] == state_after ['board_iteration' ], "Board iteration should remain the same"
94+ assert state_before ['today_seed' ] == state_after ['today_seed' ], "Seed should remain the same"
95+
96+ finally :
97+ await browser .close ()
98+
99+ @pytest .mark .asyncio
100+ async def test_visual_state_restoration (self ):
101+ """Test that clicked tiles visually appear clicked after reload."""
102+ async with async_playwright () as p :
103+ browser = await p .chromium .launch (headless = False ) # Set to True for CI
104+ page = await browser .new_page ()
105+
106+ try :
107+ # Navigate to the app
108+ await page .goto ("http://localhost:8080" )
109+ await page .wait_for_load_state ("networkidle" )
110+
111+ # Click a specific tile and get its visual state
112+ first_tile = page .locator ("[style*='cursor: pointer']" ).first
113+
114+ # Get background color before clicking
115+ bg_before = await first_tile .evaluate ("el => window.getComputedStyle(el).backgroundColor" )
116+
117+ # Click the tile
118+ await first_tile .click ()
119+ await asyncio .sleep (0.5 )
120+
121+ # Get background color after clicking
122+ bg_after_click = await first_tile .evaluate ("el => window.getComputedStyle(el).backgroundColor" )
123+
124+ # Colors should be different (tile is now clicked)
125+ assert bg_before != bg_after_click , "Tile background should change when clicked"
126+
127+ # Reload the page
128+ await page .reload ()
129+ await page .wait_for_load_state ("networkidle" )
130+
131+ # Get the same tile after reload
132+ first_tile_after_reload = page .locator ("[style*='cursor: pointer']" ).first
133+
134+ # Get background color after reload
135+ bg_after_reload = await first_tile_after_reload .evaluate ("el => window.getComputedStyle(el).backgroundColor" )
136+
137+ # Color should match the clicked state
138+ assert bg_after_click == bg_after_reload , "Tile should maintain clicked appearance after reload"
139+
140+ finally :
141+ await browser .close ()
142+
143+ @pytest .mark .asyncio
144+ async def test_multiple_sessions_share_state (self ):
145+ """Test that multiple browser sessions see the same state."""
146+ async with async_playwright () as p :
147+ browser1 = await p .chromium .launch (headless = True )
148+ browser2 = await p .chromium .launch (headless = True )
149+
150+ try :
151+ # User 1 connects
152+ page1 = await browser1 .new_page ()
153+ await page1 .goto ("http://localhost:8080" )
154+ await page1 .wait_for_load_state ("networkidle" )
155+
156+ # User 1 clicks a tile
157+ tiles1 = await page1 .locator ("[style*='cursor: pointer']" ).all ()
158+ await tiles1 [0 ].click () # Click first tile
159+ await asyncio .sleep (0.5 )
160+
161+ # User 2 connects
162+ page2 = await browser2 .new_page ()
163+ await page2 .goto ("http://localhost:8080" )
164+ await page2 .wait_for_load_state ("networkidle" )
165+
166+ # Both users should see the same state
167+ state_file = Path ("game_state.json" )
168+ with open (state_file , 'r' ) as f :
169+ shared_state = json .load (f )
170+
171+ clicked_tiles = {tuple (pos ) for pos in shared_state ['clicked_tiles' ]}
172+
173+ # Should have tile at (0,0) and FREE MEAT at (2,2)
174+ assert (0 , 0 ) in clicked_tiles , "First tile should be clicked"
175+ assert (2 , 2 ) in clicked_tiles , "FREE MEAT should be clicked"
176+ assert len (clicked_tiles ) == 2 , "Should have exactly 2 clicked tiles"
177+
178+ # User 2 clicks another tile
179+ tiles2 = await page2 .locator ("[style*='cursor: pointer']" ).all ()
180+ await tiles2 [6 ].click () # Click a different tile
181+ await asyncio .sleep (0.5 )
182+
183+ # User 1 reloads to see User 2's changes
184+ await page1 .reload ()
185+ await page1 .wait_for_load_state ("networkidle" )
186+
187+ # Check final state
188+ with open (state_file , 'r' ) as f :
189+ final_state = json .load (f )
190+
191+ final_clicked = {tuple (pos ) for pos in final_state ['clicked_tiles' ]}
192+ assert len (final_clicked ) == 3 , "Should have 3 clicked tiles after both users clicked"
193+
194+ finally :
195+ await browser1 .close ()
196+ await browser2 .close ()
197+
198+
199+ if __name__ == "__main__" :
200+ pytest .main ([__file__ , "-v" , "-s" ])
0 commit comments