File size: 3,626 Bytes
648a32a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""Integration tests for Streamlit app.

These tests actually run the Streamlit app to catch API errors
that unit tests miss (like wrong parameter names).
"""

import pytest
import subprocess
import sys
import time
import os
import signal


class TestStreamlitAppRuns:
    """Test that the Streamlit app actually runs without errors."""

    def test_app_python_syntax_valid(self):
        """App has valid Python syntax."""
        app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        app_path = os.path.join(app_dir, "app.py")

        # Compile the file to check for syntax errors
        with open(app_path, "r") as f:
            source = f.read()

        try:
            compile(source, app_path, "exec")
        except SyntaxError as e:
            pytest.fail(f"Syntax error in app.py: {e}")


class TestStreamlitAPIUsage:
    """Check for common Streamlit API mistakes by parsing the code."""

    def test_no_invalid_dataframe_width(self):
        """st.dataframe should use use_container_width, not width="stretch"."""
        app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        app_path = os.path.join(app_dir, "app.py")

        with open(app_path, "r") as f:
            content = f.read()

        # Find all st.dataframe calls with wrong width parameter
        import re

        # Look for st.dataframe with width="stretch" (wrong)
        # But NOT use_container_width (correct)
        pattern = r'st\.dataframe\([^)]*[^_]width\s*=\s*["\']stretch["\']'
        matches = re.findall(pattern, content)

        if matches:
            pytest.fail(
                f"Found st.dataframe with 'width=\"stretch\"' parameter. "
                f"Use 'use_container_width=True' instead.\n"
                f"Matches: {matches}"
            )

    def test_no_undefined_variables_in_fstrings(self):
        """Check for common f-string variable errors."""
        app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        app_path = os.path.join(app_dir, "app.py")

        with open(app_path, "r") as f:
            lines = f.readlines()

        # Check each f-string for obvious issues
        import re
        for i, line in enumerate(lines, 1):
            # Find f-strings
            if 'f"' in line or "f'" in line:
                # Check for empty braces (common mistake)
                if "{}" in line:
                    pytest.fail(f"Line {i}: Empty braces in f-string: {line.strip()}")


class TestAppWithStreamlitTesting:
    """Use Streamlit's built-in testing framework if available."""

    @pytest.fixture
    def app(self):
        """Create AppTest instance."""
        try:
            from streamlit.testing.v1 import AppTest
        except ImportError:
            pytest.skip("streamlit.testing not available (requires streamlit>=1.28)")

        app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        app_path = os.path.join(app_dir, "app.py")

        # Run the app
        at = AppTest.from_file(app_path)
        at.run(timeout=30)
        return at

    def test_app_runs_without_exception(self, app):
        """App renders without raising exceptions."""
        assert not app.exception, f"App raised exception: {app.exception}"

    def test_app_has_header(self, app):
        """App has a header element."""
        # The app should have rendered some content
        assert len(app.markdown) > 0 or len(app.header) > 0, "App has no content"

    def test_app_has_sidebar(self, app):
        """App has sidebar elements."""
        assert len(app.sidebar) > 0, "App has no sidebar"