conftest.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. """Shared conftest for all integration tests."""
  2. import os
  3. import re
  4. from pathlib import Path
  5. import pytest
  6. import reflex.app
  7. from reflex.config import environment
  8. from reflex.testing import AppHarness, AppHarnessProd
  9. DISPLAY = None
  10. XVFB_DIMENSIONS = (800, 600)
  11. @pytest.fixture(scope="session", autouse=True)
  12. def xvfb():
  13. """Create virtual X display.
  14. This function is a no-op unless GITHUB_ACTIONS is set in the environment.
  15. Yields:
  16. the pyvirtualdisplay object that the browser will be open on
  17. """
  18. if os.environ.get("GITHUB_ACTIONS") and not environment.APP_HARNESS_HEADLESS.get():
  19. from pyvirtualdisplay.smartdisplay import ( # pyright: ignore [reportMissingImports]
  20. SmartDisplay,
  21. )
  22. global DISPLAY
  23. with SmartDisplay(visible=False, size=XVFB_DIMENSIONS) as DISPLAY:
  24. yield DISPLAY
  25. DISPLAY = None
  26. else:
  27. yield None
  28. def pytest_exception_interact(node, call, report):
  29. """Take and upload screenshot when tests fail.
  30. Args:
  31. node: The pytest item that failed.
  32. call: The pytest call describing when/where the test was invoked.
  33. report: The pytest log report object.
  34. """
  35. screenshot_dir = environment.SCREENSHOT_DIR.get()
  36. if DISPLAY is None or screenshot_dir is None:
  37. return
  38. screenshot_dir = Path(screenshot_dir)
  39. screenshot_dir.mkdir(parents=True, exist_ok=True)
  40. safe_filename = re.sub(
  41. r"(?u)[^-\w.]",
  42. "_",
  43. str(node.nodeid).strip().replace(" ", "_").replace(":", "_").replace(".py", ""),
  44. )
  45. try:
  46. DISPLAY.waitgrab().save(
  47. (Path(screenshot_dir) / safe_filename).with_suffix(".png"),
  48. )
  49. except Exception as e:
  50. print(f"Failed to take screenshot for {node}: {e}")
  51. @pytest.fixture(
  52. scope="session", params=[AppHarness, AppHarnessProd], ids=["dev", "prod"]
  53. )
  54. def app_harness_env(request):
  55. """Parametrize the AppHarness class to use for the test, either dev or prod.
  56. Args:
  57. request: The pytest fixture request object.
  58. Returns:
  59. The AppHarness class to use for the test.
  60. """
  61. return request.param
  62. @pytest.fixture(autouse=True)
  63. def raise_console_error(request, mocker):
  64. """Spy on calls to `console.error` used by the framework.
  65. Help catch spurious error conditions that might otherwise go unnoticed.
  66. If a test is marked with `ignore_console_error`, the spy will be ignored
  67. after the test.
  68. Args:
  69. request: The pytest request object.
  70. mocker: The pytest mocker object.
  71. Yields:
  72. control to the test function.
  73. """
  74. spy = mocker.spy(reflex.app.console, "error")
  75. yield
  76. if "ignore_console_error" not in request.keywords:
  77. spy.assert_not_called()