peijun1's picture
Deploy AI Studio Proxy API to Hugging Face Spaces
a5784e9
Raw
History Blame Contribute Delete
10.7 kB
"""
Tests for browser_utils/initialization/scripts.py
"""
from unittest.mock import AsyncMock, mock_open, patch
import pytest
from browser_utils.initialization.scripts import (
_clean_userscript_headers,
add_init_scripts_to_context,
)
class TestCleanUserscriptHeaders:
"""Test _clean_userscript_headers function"""
def test_clean_headers_basic(self):
"""Test basic UserScript header cleanup"""
script = """// ==UserScript==
// @name Test Script
// @version 1.0
// ==/UserScript==
console.log('Hello');"""
result = _clean_userscript_headers(script)
assert "// ==UserScript==" not in result
assert "// @name" not in result
assert "// ==/UserScript==" not in result
assert "console.log('Hello');" in result
def test_clean_headers_no_headers(self):
"""Test script without UserScript headers"""
script = "console.log('No headers');"
result = _clean_userscript_headers(script)
assert result == script
def test_clean_headers_empty_script(self):
"""Test empty script"""
script = ""
result = _clean_userscript_headers(script)
assert result == ""
def test_clean_headers_only_headers(self):
"""Test script containing only headers"""
script = """// ==UserScript==
// @name Test
// ==/UserScript=="""
result = _clean_userscript_headers(script)
# Should only have empty lines left
assert result.strip() == ""
def test_clean_headers_multiple_blocks(self):
"""Test multiple UserScript blocks"""
script = """// ==UserScript==
// @name Block1
// ==/UserScript==
console.log('First');
// ==UserScript==
// @name Block2
// ==/UserScript==
console.log('Second');"""
result = _clean_userscript_headers(script)
assert "// @name" not in result
assert "console.log('First');" in result
assert "console.log('Second');" in result
def test_clean_headers_preserves_other_comments(self):
"""Test preserving other comments"""
script = """// ==UserScript==
// @name Test
// ==/UserScript==
// This is a regular comment
console.log('Code');"""
result = _clean_userscript_headers(script)
assert "// This is a regular comment" in result
assert "// @name Test" not in result
def test_clean_headers_whitespace_handling(self):
"""Test whitespace handling"""
script = """ // ==UserScript==
// @name Test
// ==/UserScript==
console.log('Code');"""
result = _clean_userscript_headers(script)
assert "// @name" not in result
assert "console.log('Code');" in result
def test_clean_headers_incomplete_block(self):
"""Test incomplete UserScript block (only start marker)"""
script = """// ==UserScript==
// @name Test
console.log('No closing tag');"""
result = _clean_userscript_headers(script)
# Everything after the start marker should be treated as header and removed
assert "// @name Test" not in result
# Since there's no end marker, subsequent content is also removed
assert "console.log" not in result or "No closing tag" not in result
class TestAddInitScriptsToContext:
"""Test add_init_scripts_to_context function"""
@pytest.fixture
def mock_context(self):
"""Create mock browser context"""
context = AsyncMock()
context.add_init_script = AsyncMock()
return context
@pytest.mark.asyncio
def test_add_scripts_success(self, mock_context):
"""Test successful script addition"""
script_content = """// ==UserScript==
// @name Test
// ==/UserScript==
console.log('Hello');"""
with patch("config.settings.USERSCRIPT_PATH", "/fake/path/script.js"):
with patch(
"browser_utils.initialization.scripts.os.path.exists", return_value=True
):
with patch(
"browser_utils.initialization.scripts.open",
mock_open(read_data=script_content),
):
# add_init_scripts_to_context is async, but tested synchronously here?
# Wait, it is async in the source.
pass
@pytest.mark.asyncio
async def test_add_scripts_success_async(self, mock_context):
"""Test successful script addition async"""
script_content = """// ==UserScript==
// @name Test
// ==/UserScript==
console.log('Hello');"""
with patch("config.settings.USERSCRIPT_PATH", "/fake/path/script.js"):
with patch(
"browser_utils.initialization.scripts.os.path.exists", return_value=True
):
with patch(
"browser_utils.initialization.scripts.open",
mock_open(read_data=script_content),
):
await add_init_scripts_to_context(mock_context)
# Verify add_init_script called
mock_context.add_init_script.assert_called_once()
# Verify passed script does not contain headers
called_script = mock_context.add_init_script.call_args[0][0]
assert "// ==UserScript==" not in called_script
assert "console.log('Hello');" in called_script
@pytest.mark.asyncio
async def test_add_scripts_file_not_exists(self, mock_context, caplog):
"""Test case where script file does not exist"""
with patch("config.settings.USERSCRIPT_PATH", "/fake/path/script.js"):
with patch(
"browser_utils.initialization.scripts.os.path.exists",
return_value=False,
):
await add_init_scripts_to_context(mock_context)
# Verify add_init_script not called
mock_context.add_init_script.assert_not_called()
# Verify log recorded
assert (
"Script file does not exist" in caplog.text or len(caplog.records) == 0
) # Might not have been captured
@pytest.mark.asyncio
async def test_add_scripts_read_error(self, mock_context, caplog):
"""Test error while reading script file"""
with patch("config.settings.USERSCRIPT_PATH", "/fake/path/script.js"):
with patch(
"browser_utils.initialization.scripts.os.path.exists", return_value=True
):
with patch(
"browser_utils.initialization.scripts.open",
side_effect=IOError("Read error"),
):
await add_init_scripts_to_context(mock_context)
# Verify add_init_script not called
mock_context.add_init_script.assert_not_called()
# Should log error (but not throw exception)
@pytest.mark.asyncio
async def test_add_scripts_injection_error(self, mock_context, caplog):
"""Test error during script injection"""
script_content = "console.log('Test');"
mock_context.add_init_script = AsyncMock(
side_effect=Exception("Injection error")
)
with patch("config.settings.USERSCRIPT_PATH", "/fake/path/script.js"):
with patch(
"browser_utils.initialization.scripts.os.path.exists", return_value=True
):
with patch(
"browser_utils.initialization.scripts.open",
mock_open(read_data=script_content),
):
await add_init_scripts_to_context(mock_context)
# Should not throw exception (already caught)
@pytest.mark.asyncio
async def test_add_scripts_empty_file(self, mock_context):
"""Test empty script file"""
with patch("config.settings.USERSCRIPT_PATH", "/fake/path/script.js"):
with patch(
"browser_utils.initialization.scripts.os.path.exists", return_value=True
):
with patch(
"browser_utils.initialization.scripts.open", mock_open(read_data="")
):
await add_init_scripts_to_context(mock_context)
# Even empty script should be added
mock_context.add_init_script.assert_called_once_with("")
@pytest.mark.asyncio
async def test_add_scripts_import_error(self, mock_context):
"""Test case where config import fails"""
# Simulate USERSCRIPT_PATH import failure
with patch(
"browser_utils.initialization.scripts.os.path.exists",
side_effect=ImportError("Config error"),
):
await add_init_scripts_to_context(mock_context)
# Should catch exception, not call add_init_script
mock_context.add_init_script.assert_not_called()
@pytest.mark.asyncio
async def test_add_scripts_with_unicode(self, mock_context):
"""Test script containing Unicode characters"""
script_content = """// ==UserScript==
// @name test script
// ==/UserScript==
console.log('hello, world! 🌍');"""
with patch("config.settings.USERSCRIPT_PATH", "/fake/path/script.js"):
with patch(
"browser_utils.initialization.scripts.os.path.exists", return_value=True
):
with patch(
"browser_utils.initialization.scripts.open",
mock_open(read_data=script_content),
):
await add_init_scripts_to_context(mock_context)
mock_context.add_init_script.assert_called_once()
called_script = mock_context.add_init_script.call_args[0][0]
assert "hello, world! 🌍" in called_script
assert "// @name test script" not in called_script
@pytest.mark.asyncio
async def test_add_scripts_large_file(self, mock_context):
"""Test large file handling"""
# Create a large script content
large_script = "// ==UserScript==\n// @name Test\n// ==/UserScript==\n"
large_script += "console.log('line');\n" * 10000
with patch("config.settings.USERSCRIPT_PATH", "/fake/path/script.js"):
with patch(
"browser_utils.initialization.scripts.os.path.exists", return_value=True
):
with patch(
"browser_utils.initialization.scripts.open",
mock_open(read_data=large_script),
):
await add_init_scripts_to_context(mock_context)
mock_context.add_init_script.assert_called_once()
called_script = mock_context.add_init_script.call_args[0][0]
# Verify large file correctly handled
assert "console.log('line');" in called_script
assert called_script.count("console.log('line');") == 10000