Skip to content

Commit b5ec6ad

Browse files
committed
fix coverage
1 parent f44acf4 commit b5ec6ad

File tree

3 files changed

+114
-3
lines changed

3 files changed

+114
-3
lines changed

tests/conftest.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
import contextlib
44
import os
55
import subprocess
6-
import sys
7-
from time import sleep
86

97
import pytest
108
from _pytest.config.argparsing import Parser

tests/test_reactjs/test_utils.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
from pathlib import Path
2+
from unittest.mock import patch
23

34
import pytest
45
import responses
56

67
from reactpy.reactjs.utils import (
8+
copy_file,
79
module_name_suffix,
810
normalize_url_path,
911
resolve_from_module_file,
1012
resolve_from_module_source,
1113
resolve_from_module_url,
14+
simple_file_lock,
1215
)
1316
from reactpy.testing import assert_reactpy_did_log
1417

@@ -163,3 +166,35 @@ def test_resolve_relative_url():
163166
== "https://some.url/path/to/another.js"
164167
)
165168
assert normalize_url_path("/some/path", "to/another.js") == "to/another.js"
169+
170+
171+
def test_copy_file_fallback(tmp_path):
172+
source = tmp_path / "source.txt"
173+
source.write_text("content")
174+
target = tmp_path / "target.txt"
175+
176+
path_cls = type(target)
177+
178+
with patch("shutil.copy"):
179+
with patch.object(
180+
path_cls, "replace", side_effect=[OSError, OSError]
181+
) as mock_replace:
182+
with patch.object(path_cls, "rename") as mock_rename:
183+
with patch.object(path_cls, "exists", return_value=True):
184+
with patch.object(path_cls, "unlink") as mock_unlink:
185+
with patch("time.sleep"): # Speed up test
186+
copy_file(target, source, symlink=False)
187+
188+
assert mock_replace.call_count == 2
189+
mock_unlink.assert_called_once()
190+
mock_rename.assert_called_once()
191+
192+
193+
def test_simple_file_lock_timeout(tmp_path):
194+
lock_file = tmp_path / "lock"
195+
196+
with patch("os.open", side_effect=OSError):
197+
with patch("time.sleep"): # Speed up test
198+
with pytest.raises(TimeoutError, match="Could not acquire lock"):
199+
with simple_file_lock(lock_file, timeout=0.1):
200+
pass

tests/test_testing.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import logging
22
import os
3+
from unittest.mock import AsyncMock, MagicMock, patch
34

45
import pytest
56

67
from reactpy import Ref, component, html, testing
78
from reactpy.logging import ROOT_LOGGER
8-
from reactpy.testing.backend import _hotswap
9+
from reactpy.testing.backend import BackendFixture, _hotswap
910
from reactpy.testing.display import DisplayFixture
1011
from tests.sample import SampleApp
1112

@@ -205,3 +206,80 @@ async def on_click(event):
205206
await display.page.wait_for_selector("#hotswap-2")
206207
await client_incr_button.click()
207208
await display.page.wait_for_selector("#hotswap-3")
209+
210+
211+
@pytest.mark.asyncio
212+
async def test_backend_server_failure():
213+
# We need to mock uvicorn.Server to fail starting
214+
with patch("uvicorn.Server") as mock_server_cls:
215+
mock_server = mock_server_cls.return_value
216+
mock_server.started = False
217+
mock_server.servers = []
218+
mock_server.config.get_loop_factory = MagicMock()
219+
220+
# Mock serve to just return (or sleep briefly then return)
221+
mock_server.serve = AsyncMock(return_value=None)
222+
223+
backend = BackendFixture()
224+
225+
# We need to speed up the loop
226+
with patch("asyncio.sleep", new_callable=AsyncMock):
227+
with pytest.raises(RuntimeError, match="Server failed to start"):
228+
await backend.__aenter__()
229+
230+
231+
@pytest.mark.asyncio
232+
async def test_display_fixture_headless_logic():
233+
# Mock async_playwright to avoid launching real browser
234+
with patch("reactpy.testing.display.async_playwright") as mock_pw:
235+
mock_context_manager = mock_pw.return_value
236+
mock_playwright_instance = AsyncMock()
237+
mock_context_manager.__aenter__.return_value = mock_playwright_instance
238+
239+
mock_browser = AsyncMock()
240+
mock_playwright_instance.chromium.launch.return_value = mock_browser
241+
242+
mock_page = AsyncMock()
243+
# Configure synchronous methods on page
244+
mock_page.set_default_timeout = MagicMock()
245+
mock_page.on = MagicMock()
246+
247+
mock_browser.new_page.return_value = mock_page
248+
249+
# Case: headless=False, PLAYWRIGHT_HEADLESS='1'
250+
with patch.dict(os.environ, {"PLAYWRIGHT_HEADLESS": "1"}):
251+
async with DisplayFixture(headless=False):
252+
pass
253+
# Check that launch was called with headless=True
254+
mock_playwright_instance.chromium.launch.assert_called_with(headless=True)
255+
256+
257+
@pytest.mark.asyncio
258+
async def test_display_fixture_internal_backend():
259+
# This covers line 87: await self.backend_exit_stack.aclose()
260+
# when backend is internal (default)
261+
262+
with patch("reactpy.testing.display.async_playwright") as mock_pw:
263+
mock_context_manager = mock_pw.return_value
264+
mock_playwright_instance = AsyncMock()
265+
mock_context_manager.__aenter__.return_value = mock_playwright_instance
266+
267+
mock_browser = AsyncMock()
268+
mock_playwright_instance.chromium.launch.return_value = mock_browser
269+
270+
mock_page = AsyncMock()
271+
mock_page.set_default_timeout = MagicMock()
272+
mock_page.on = MagicMock()
273+
mock_browser.new_page.return_value = mock_page
274+
275+
# We also need to mock BackendFixture to avoid starting real server
276+
with patch("reactpy.testing.display.BackendFixture") as mock_backend_cls:
277+
mock_backend = AsyncMock()
278+
mock_backend.mount = MagicMock() # mount is synchronous
279+
mock_backend_cls.return_value = mock_backend
280+
281+
async with DisplayFixture() as display:
282+
assert not display.backend_is_external
283+
284+
# Verify backend exit stack closed (implied if no error and backend.__aexit__ called)
285+
mock_backend.__aexit__.assert_called()

0 commit comments

Comments
 (0)