|
13 | 13 | # limitations under the License.
|
14 | 14 |
|
15 | 15 | import asyncio
|
| 16 | +from contextlib import asynccontextmanager |
| 17 | +from pathlib import Path |
16 | 18 | from typing import Any, AsyncGenerator, Awaitable, Callable, Dict, Generator
|
17 | 19 |
|
18 | 20 | import pytest
|
19 | 21 |
|
| 22 | +from playwright._impl._driver import compute_driver_executable |
20 | 23 | from playwright.async_api import (
|
21 | 24 | Browser,
|
22 | 25 | BrowserContext,
|
23 | 26 | BrowserType,
|
| 27 | + FrameLocator, |
| 28 | + Locator, |
24 | 29 | Page,
|
25 | 30 | Playwright,
|
26 | 31 | Selectors,
|
27 | 32 | async_playwright,
|
28 | 33 | )
|
| 34 | +from tests.server import HTTPServer |
29 | 35 |
|
30 | 36 | from .utils import Utils
|
31 | 37 | from .utils import utils as utils_object
|
@@ -131,3 +137,77 @@ async def page(context: BrowserContext) -> AsyncGenerator[Page, None]:
|
131 | 137 | @pytest.fixture(scope="session")
|
132 | 138 | def selectors(playwright: Playwright) -> Selectors:
|
133 | 139 | return playwright.selectors
|
| 140 | + |
| 141 | + |
| 142 | +class TraceViewerPage: |
| 143 | + def __init__(self, page: Page): |
| 144 | + self.page = page |
| 145 | + |
| 146 | + @property |
| 147 | + def actions_tree(self) -> Locator: |
| 148 | + return self.page.get_by_test_id("actions-tree") |
| 149 | + |
| 150 | + @property |
| 151 | + def action_titles(self) -> Locator: |
| 152 | + return self.page.locator(".action-title") |
| 153 | + |
| 154 | + @property |
| 155 | + def stack_frames(self) -> Locator: |
| 156 | + return self.page.get_by_test_id("stack-trace-list").locator(".list-view-entry") |
| 157 | + |
| 158 | + async def select_action(self, title: str, ordinal: int = 0) -> None: |
| 159 | + await self.page.locator(f'.action-title:has-text("{title}")').nth( |
| 160 | + ordinal |
| 161 | + ).click() |
| 162 | + |
| 163 | + async def select_snapshot(self, name: str) -> None: |
| 164 | + await self.page.click( |
| 165 | + f'.snapshot-tab .tabbed-pane-tab-label:has-text("{name}")' |
| 166 | + ) |
| 167 | + |
| 168 | + async def snapshot_frame( |
| 169 | + self, action_name: str, ordinal: int = 0, has_subframe: bool = False |
| 170 | + ) -> FrameLocator: |
| 171 | + await self.select_action(action_name, ordinal) |
| 172 | + expected_frames = 4 if has_subframe else 3 |
| 173 | + while len(self.page.frames) < expected_frames: |
| 174 | + await self.page.wait_for_event("frameattached") |
| 175 | + return self.page.frame_locator("iframe.snapshot-visible[name=snapshot]") |
| 176 | + |
| 177 | + async def show_source_tab(self) -> None: |
| 178 | + await self.page.click("text='Source'") |
| 179 | + |
| 180 | + async def expand_action(self, title: str, ordinal: int = 0) -> None: |
| 181 | + await self.actions_tree.locator(".tree-view-entry", has_text=title).nth( |
| 182 | + ordinal |
| 183 | + ).locator(".codicon-chevron-right").click() |
| 184 | + |
| 185 | + |
| 186 | +@pytest.fixture |
| 187 | +async def show_trace_viewer(browser: Browser) -> AsyncGenerator[Callable, None]: |
| 188 | + """Fixture that provides a function to show trace viewer for a trace file.""" |
| 189 | + |
| 190 | + @asynccontextmanager |
| 191 | + async def _show_trace_viewer( |
| 192 | + trace_path: Path, |
| 193 | + ) -> AsyncGenerator[TraceViewerPage, None]: |
| 194 | + trace_viewer_path = ( |
| 195 | + Path(compute_driver_executable()[0]) / "../package/lib/vite/traceViewer" |
| 196 | + ).resolve() |
| 197 | + |
| 198 | + server = HTTPServer() |
| 199 | + server.start(trace_viewer_path) |
| 200 | + server.set_route("/trace.zip", lambda request: request.serve_file(trace_path)) |
| 201 | + |
| 202 | + page = await browser.new_page() |
| 203 | + |
| 204 | + try: |
| 205 | + await page.goto( |
| 206 | + f"{server.PREFIX}/index.html?trace={server.PREFIX}/trace.zip" |
| 207 | + ) |
| 208 | + yield TraceViewerPage(page) |
| 209 | + finally: |
| 210 | + await page.close() |
| 211 | + server.stop() |
| 212 | + |
| 213 | + yield _show_trace_viewer |
0 commit comments