codeant-ai-for-open-source[bot] commented on code in PR #41097:
URL: https://github.com/apache/superset/pull/41097#discussion_r3484698277
##########
tests/unit_tests/utils/webdriver_test.py:
##########
@@ -964,13 +964,236 @@ def evaluate_side_effect(script):
driver = WebDriverPlaywright("chrome")
result = driver.get_screenshot(
- "http://example.com", "dashboard", mock_user
+ "http://example.com", "standalone", mock_user
)
- assert result is None
- mock_element.screenshot.assert_not_called()
- mock_logger.error.assert_any_call(
- "Tiled screenshot failed at url %s; "
- "not falling back to avoid sending a blank PDF",
- "http://example.com",
+ assert result == b"fallback_screenshot"
+ mock_take_tiled.assert_called_once()
+ mock_logger.warning.assert_any_call(
+ ("Tiled screenshot failed, falling back to standard screenshot"),
+ )
+
+
+class TestWebDriverPlaywrightAnimationWaitOrder:
+ """Animation wait must run after the spinner wait, not before."""
+
+ _base_config = {
+ "WEBDRIVER_OPTION_ARGS": [],
+ "WEBDRIVER_WINDOW": {"pixel_density": 1},
+ "SCREENSHOT_PLAYWRIGHT_DEFAULT_TIMEOUT": 30000,
+ "SCREENSHOT_PLAYWRIGHT_WAIT_EVENT": "networkidle",
+ "SCREENSHOT_SELENIUM_HEADSTART": 0,
+ "SCREENSHOT_SELENIUM_ANIMATION_WAIT": 2,
+ "SCREENSHOT_REPLACE_UNEXPECTED_ERRORS": False,
+ "SCREENSHOT_LOCATE_WAIT": 10,
+ "SCREENSHOT_LOAD_WAIT": 30,
+ "SCREENSHOT_WAIT_FOR_ERROR_MODAL_VISIBLE": 10,
+ "SCREENSHOT_WAIT_FOR_ERROR_MODAL_INVISIBLE": 10,
+ }
+
+ def _make_pw_mocks(self, mock_browser_manager):
+ mock_browser = MagicMock()
+ mock_context = MagicMock()
+ mock_page = MagicMock()
+ mock_element = MagicMock()
+
+ mock_browser_manager.get_browser.return_value = mock_browser
+ mock_browser.new_context.return_value = mock_context
+ mock_context.new_page.return_value = mock_page
+ mock_page.locator.return_value = mock_element
+ mock_element.screenshot.return_value = b"screenshot"
+ return mock_context, mock_page
+
+ @patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
+ @patch("superset.utils.webdriver._browser_manager")
+ @patch("superset.utils.webdriver.app")
+ def test_animation_wait_after_spinner_wait_tiled_disabled(
+ self, mock_app, mock_browser_manager
+ ):
+ """Non-tiled path: animation wait runs after spinner
wait_for_function."""
+ mock_user = MagicMock()
+ mock_user.username = "test_user"
+ mock_app.config = {**self._base_config, "SCREENSHOT_TILED_ENABLED":
False}
+
+ mock_context, mock_page = self._make_pw_mocks(mock_browser_manager)
+
+ call_order: list[str] = []
+
+ def record_wait_for_function(*args, **kwargs):
Review Comment:
**Suggestion:** Add parameter and return type hints for this newly added
nested function. [custom_rule]
**Severity Level:** Minor ⚠️
<details>
<summary><b>Why it matters? 🤔 </b></summary>
This newly added nested function has no type annotations for its parameters
or return value, so it violates the typing rule for new Python functions.
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=d4d822c9f05c4d549ecf97ded04fbca4&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=d4d822c9f05c4d549ecf97ded04fbca4&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** tests/unit_tests/utils/webdriver_test.py
**Line:** 1022:1022
**Comment:**
*Custom Rule: Add parameter and return type hints for this newly added
nested function.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41097&comment_hash=340e2ff48dac6d8ba7570b1f27c5c5ef80bb38c39853249e84c39a4814a7fb7a&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41097&comment_hash=340e2ff48dac6d8ba7570b1f27c5c5ef80bb38c39853249e84c39a4814a7fb7a&reaction=dislike'>👎</a>
##########
tests/unit_tests/utils/webdriver_test.py:
##########
@@ -964,13 +964,236 @@ def evaluate_side_effect(script):
driver = WebDriverPlaywright("chrome")
result = driver.get_screenshot(
- "http://example.com", "dashboard", mock_user
+ "http://example.com", "standalone", mock_user
)
- assert result is None
- mock_element.screenshot.assert_not_called()
- mock_logger.error.assert_any_call(
- "Tiled screenshot failed at url %s; "
- "not falling back to avoid sending a blank PDF",
- "http://example.com",
+ assert result == b"fallback_screenshot"
+ mock_take_tiled.assert_called_once()
+ mock_logger.warning.assert_any_call(
+ ("Tiled screenshot failed, falling back to standard screenshot"),
+ )
+
+
+class TestWebDriverPlaywrightAnimationWaitOrder:
+ """Animation wait must run after the spinner wait, not before."""
+
+ _base_config = {
+ "WEBDRIVER_OPTION_ARGS": [],
+ "WEBDRIVER_WINDOW": {"pixel_density": 1},
+ "SCREENSHOT_PLAYWRIGHT_DEFAULT_TIMEOUT": 30000,
+ "SCREENSHOT_PLAYWRIGHT_WAIT_EVENT": "networkidle",
+ "SCREENSHOT_SELENIUM_HEADSTART": 0,
+ "SCREENSHOT_SELENIUM_ANIMATION_WAIT": 2,
+ "SCREENSHOT_REPLACE_UNEXPECTED_ERRORS": False,
+ "SCREENSHOT_LOCATE_WAIT": 10,
+ "SCREENSHOT_LOAD_WAIT": 30,
+ "SCREENSHOT_WAIT_FOR_ERROR_MODAL_VISIBLE": 10,
+ "SCREENSHOT_WAIT_FOR_ERROR_MODAL_INVISIBLE": 10,
+ }
+
+ def _make_pw_mocks(self, mock_browser_manager):
+ mock_browser = MagicMock()
+ mock_context = MagicMock()
+ mock_page = MagicMock()
+ mock_element = MagicMock()
+
+ mock_browser_manager.get_browser.return_value = mock_browser
+ mock_browser.new_context.return_value = mock_context
+ mock_context.new_page.return_value = mock_page
+ mock_page.locator.return_value = mock_element
+ mock_element.screenshot.return_value = b"screenshot"
+ return mock_context, mock_page
+
+ @patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
+ @patch("superset.utils.webdriver._browser_manager")
+ @patch("superset.utils.webdriver.app")
+ def test_animation_wait_after_spinner_wait_tiled_disabled(
+ self, mock_app, mock_browser_manager
+ ):
Review Comment:
**Suggestion:** Add type annotations for all parameters and an explicit
return type for this newly added test method. [custom_rule]
**Severity Level:** Minor ⚠️
<details>
<summary><b>Why it matters? 🤔 </b></summary>
This newly added test method omits type hints for its parameters and return
value, which matches the typing rule for new Python methods.
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=8efb54f7a615444f8c7c1649398f222a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=8efb54f7a615444f8c7c1649398f222a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** tests/unit_tests/utils/webdriver_test.py
**Line:** 1010:1012
**Comment:**
*Custom Rule: Add type annotations for all parameters and an explicit
return type for this newly added test method.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41097&comment_hash=478bc5242211ff6207ec0cd43862edff0c1ded4583bc6426052872ffc5abf67a&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41097&comment_hash=478bc5242211ff6207ec0cd43862edff0c1ded4583bc6426052872ffc5abf67a&reaction=dislike'>👎</a>
##########
tests/unit_tests/utils/webdriver_test.py:
##########
@@ -964,13 +964,236 @@ def evaluate_side_effect(script):
driver = WebDriverPlaywright("chrome")
result = driver.get_screenshot(
- "http://example.com", "dashboard", mock_user
+ "http://example.com", "standalone", mock_user
)
- assert result is None
- mock_element.screenshot.assert_not_called()
- mock_logger.error.assert_any_call(
- "Tiled screenshot failed at url %s; "
- "not falling back to avoid sending a blank PDF",
- "http://example.com",
+ assert result == b"fallback_screenshot"
+ mock_take_tiled.assert_called_once()
+ mock_logger.warning.assert_any_call(
+ ("Tiled screenshot failed, falling back to standard screenshot"),
+ )
+
+
+class TestWebDriverPlaywrightAnimationWaitOrder:
+ """Animation wait must run after the spinner wait, not before."""
+
+ _base_config = {
+ "WEBDRIVER_OPTION_ARGS": [],
+ "WEBDRIVER_WINDOW": {"pixel_density": 1},
+ "SCREENSHOT_PLAYWRIGHT_DEFAULT_TIMEOUT": 30000,
+ "SCREENSHOT_PLAYWRIGHT_WAIT_EVENT": "networkidle",
+ "SCREENSHOT_SELENIUM_HEADSTART": 0,
+ "SCREENSHOT_SELENIUM_ANIMATION_WAIT": 2,
+ "SCREENSHOT_REPLACE_UNEXPECTED_ERRORS": False,
+ "SCREENSHOT_LOCATE_WAIT": 10,
+ "SCREENSHOT_LOAD_WAIT": 30,
+ "SCREENSHOT_WAIT_FOR_ERROR_MODAL_VISIBLE": 10,
+ "SCREENSHOT_WAIT_FOR_ERROR_MODAL_INVISIBLE": 10,
+ }
+
+ def _make_pw_mocks(self, mock_browser_manager):
+ mock_browser = MagicMock()
+ mock_context = MagicMock()
+ mock_page = MagicMock()
+ mock_element = MagicMock()
+
+ mock_browser_manager.get_browser.return_value = mock_browser
+ mock_browser.new_context.return_value = mock_context
+ mock_context.new_page.return_value = mock_page
+ mock_page.locator.return_value = mock_element
+ mock_element.screenshot.return_value = b"screenshot"
+ return mock_context, mock_page
+
+ @patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
+ @patch("superset.utils.webdriver._browser_manager")
+ @patch("superset.utils.webdriver.app")
+ def test_animation_wait_after_spinner_wait_tiled_disabled(
+ self, mock_app, mock_browser_manager
+ ):
+ """Non-tiled path: animation wait runs after spinner
wait_for_function."""
+ mock_user = MagicMock()
+ mock_user.username = "test_user"
+ mock_app.config = {**self._base_config, "SCREENSHOT_TILED_ENABLED":
False}
+
+ mock_context, mock_page = self._make_pw_mocks(mock_browser_manager)
+
+ call_order: list[str] = []
+
+ def record_wait_for_function(*args, **kwargs):
+ call_order.append("spinner_wait")
+
+ def record_wait_for_timeout(ms):
+ if ms == 2 * 1000:
+ call_order.append("animation_wait")
+
+ mock_page.wait_for_function.side_effect = record_wait_for_function
+ mock_page.wait_for_timeout.side_effect = record_wait_for_timeout
+
+ with patch.object(WebDriverPlaywright, "auth",
return_value=mock_context):
+ WebDriverPlaywright("chrome").get_screenshot(
+ "http://example.com", "test-element", mock_user
+ )
+
+ assert "spinner_wait" in call_order
+ assert "animation_wait" in call_order
+ spinner_idx = call_order.index("spinner_wait")
+ anim_idx = call_order.index("animation_wait")
+ assert spinner_idx < anim_idx, (
+ "spinner wait must precede animation wait in non-tiled path"
+ )
+
+ @patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
+ @patch("superset.utils.webdriver._browser_manager")
+ @patch("superset.utils.webdriver.app")
+ def test_animation_wait_after_spinner_wait_tiled_enabled_small_dashboard(
+ self, mock_app, mock_browser_manager
+ ):
+ """Non-tiled path (tiled on, small dashboard): animation after
spinner."""
+ mock_user = MagicMock()
+ mock_user.username = "test_user"
+ mock_app.config = {
+ **self._base_config,
+ "SCREENSHOT_TILED_ENABLED": True,
+ "SCREENSHOT_TILED_CHART_THRESHOLD": 20,
+ "SCREENSHOT_TILED_HEIGHT_THRESHOLD": 5000,
+ "SCREENSHOT_TILED_VIEWPORT_HEIGHT": 600,
+ }
+
+ mock_context, mock_page = self._make_pw_mocks(mock_browser_manager)
+
+ # Small dashboard: 3 charts, 1000px height — below both thresholds
+ mock_page.evaluate.side_effect = [3, 1000]
+
+ call_order: list[str] = []
+
+ def record_wait_for_function(*args, **kwargs):
+ call_order.append("spinner_wait")
+
+ def record_wait_for_timeout(ms):
+ if ms == 2 * 1000:
+ call_order.append("animation_wait")
+
+ mock_page.wait_for_function.side_effect = record_wait_for_function
+ mock_page.wait_for_timeout.side_effect = record_wait_for_timeout
+
+ with patch.object(WebDriverPlaywright, "auth",
return_value=mock_context):
+ WebDriverPlaywright("chrome").get_screenshot(
+ "http://example.com", "test-element", mock_user
+ )
+
+ assert "spinner_wait" in call_order
+ assert "animation_wait" in call_order
+ assert call_order.index("spinner_wait") <
call_order.index("animation_wait")
+
+ @patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
+ @patch("superset.utils.webdriver._browser_manager")
+ @patch("superset.utils.webdriver.take_tiled_screenshot")
+ @patch("superset.utils.webdriver.app")
+ def test_tiled_path_passes_animation_wait_per_tile_no_global_wait(
+ self, mock_app, mock_take_tiled, mock_browser_manager
+ ):
+ """Tiled path delegates animation_wait to take_tiled_screenshot; no
global."""
+ mock_user = MagicMock()
+ mock_user.username = "test_user"
+ mock_app.config = {
+ **self._base_config,
+ "SCREENSHOT_TILED_ENABLED": True,
+ "SCREENSHOT_TILED_CHART_THRESHOLD": 20,
+ "SCREENSHOT_TILED_HEIGHT_THRESHOLD": 5000,
+ "SCREENSHOT_TILED_VIEWPORT_HEIGHT": 600,
+ }
+
+ mock_context, mock_page = self._make_pw_mocks(mock_browser_manager)
+
+ # Large dashboard: 25 charts, 6000px height
+ mock_page.evaluate.side_effect = [25, 6000]
+ mock_take_tiled.return_value = b"tiled_screenshot"
+
+ with patch.object(WebDriverPlaywright, "auth",
return_value=mock_context):
+ result = WebDriverPlaywright("chrome").get_screenshot(
+ "http://example.com", "standalone", mock_user
+ )
+
+ assert result == b"tiled_screenshot"
+ mock_take_tiled.assert_called_once_with(
+ mock_page,
+ "standalone",
+ 600,
+ load_wait=30,
+ animation_wait=2,
+ )
+ # The only wait_for_timeout call should be the 0ms headstart; no global
+ # animation wait should be issued (handled per-tile by
take_tiled_screenshot)
+ animation_waits = [
+ call[0][0]
+ for call in mock_page.wait_for_timeout.call_args_list
+ if call[0][0] == 2 * 1000
+ ]
+ assert animation_waits == [], (
+ "No global 2s animation wait_for_timeout should fire on the tiled
path"
+ )
+
+ @patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
+ @patch("superset.utils.webdriver._browser_manager")
+ @patch("superset.utils.webdriver.take_tiled_screenshot")
+ @patch("superset.utils.webdriver.app")
+ def test_tiled_fallback_triggered_on_empty_bytes(
+ self, mock_app, mock_take_tiled, mock_browser_manager
+ ):
+ """Tiled fallback fires when take_tiled_screenshot returns b"" (not
None)."""
+ mock_user = MagicMock()
+ mock_user.username = "test_user"
+ mock_app.config = {
+ **self._base_config,
+ "SCREENSHOT_TILED_ENABLED": True,
+ "SCREENSHOT_TILED_CHART_THRESHOLD": 20,
+ "SCREENSHOT_TILED_HEIGHT_THRESHOLD": 5000,
+ "SCREENSHOT_TILED_VIEWPORT_HEIGHT": 600,
+ }
+
+ mock_context, mock_page = self._make_pw_mocks(mock_browser_manager)
+ mock_page.evaluate.side_effect = [25, 6000]
+ # Empty bytes — falsy but not None; was silently passed through before
the fix
+ mock_take_tiled.return_value = b""
+ # _get_screenshot("standalone") calls page.screenshot(full_page=True);
+ # configure that return value so we can assert the fallback was reached
+ mock_page.screenshot.return_value = b"fallback"
+
+ with patch.object(WebDriverPlaywright, "auth",
return_value=mock_context):
+ result = WebDriverPlaywright("chrome").get_screenshot(
+ "http://example.com", "standalone", mock_user
+ )
+
+ assert result == b"fallback"
+ # Tiled path was taken (take_tiled_screenshot was called)
+ mock_take_tiled.assert_called_once()
+ # Standard screenshot was called as fallback (full_page=True for
"standalone")
+ mock_page.screenshot.assert_called_with(full_page=True)
+
+ @patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
+ @patch("superset.utils.webdriver._browser_manager")
+ @patch("superset.utils.webdriver.app")
+ def test_animation_wait_skipped_when_zero(self, mock_app,
mock_browser_manager):
Review Comment:
**Suggestion:** Add type annotations for all parameters and a `None` return
type on this newly added test method. [custom_rule]
**Severity Level:** Minor ⚠️
<details>
<summary><b>Why it matters? 🤔 </b></summary>
This newly added test method omits type hints for its parameters and return
type, so it is a real violation of the new-code typing rule.
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=d42778d00f6d46569d9e57568341e47f&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=d42778d00f6d46569d9e57568341e47f&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** tests/unit_tests/utils/webdriver_test.py
**Line:** 1176:1176
**Comment:**
*Custom Rule: Add type annotations for all parameters and a `None`
return type on this newly added test method.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41097&comment_hash=9d3411958f1e32712483265cfd0885c377bd366005acc6626f62602eb3d8c90d&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41097&comment_hash=9d3411958f1e32712483265cfd0885c377bd366005acc6626f62602eb3d8c90d&reaction=dislike'>👎</a>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]