diff --git a/cli/cli/commands/ui.py b/cli/cli/commands/ui.py index 8ca59aa2..44802332 100644 --- a/cli/cli/commands/ui.py +++ b/cli/cli/commands/ui.py @@ -15,11 +15,16 @@ @click.command() @click.argument("file_id", type=int) -@click.option("--playwright", is_flag=True, help="Output Playwright script instead of opening browser") -def ui(file_id: int, playwright: bool) -> None: +@click.option( + "--playwright", is_flag=True, help="Output Playwright script instead of opening browser" +) +@click.option("--with-editor", is_flag=True, help="Open the editor panel (enables Y.js pipeline)") +def ui(file_id: int, playwright: bool, with_editor: bool) -> None: """Open browser to file with logged-in session, or output Playwright script.""" if sync_playwright is None: - console.print("[red]✗ playwright is not installed. Run: pip install rsm-studio-cli[dev][/red]") + console.print( + "[red]✗ playwright is not installed. Run: pip install rsm-studio-cli[dev][/red]" + ) sys.exit(1) api = StudioAPI() @@ -48,6 +53,8 @@ def ui(file_id: int, playwright: bool) -> None: f"localStorage.setItem('refreshToken', '{refresh_token}'); " f"localStorage.setItem('user', '{user_json}');" ) + if with_editor: + js_code += " localStorage.setItem('ws-panel-editor', 'true');" script = f"""# Auto-generated Playwright script for file {file_id} # Generated by: studio ui {file_id} --playwright @@ -89,18 +96,23 @@ def ui(file_id: int, playwright: bool) -> None: page.goto(frontend_url) - page.evaluate( - """({ accessToken, refreshToken, user }) => { - localStorage.setItem('accessToken', accessToken); - localStorage.setItem('refreshToken', refreshToken); - localStorage.setItem('user', JSON.stringify(user)); - }""", - { - "accessToken": session_data["access_token"], - "refreshToken": session_data["refresh_token"], - "user": session_data["user"], - }, + js_inject = ( + "({ accessToken, refreshToken, user, editorPanel }) => {" + " localStorage.setItem('accessToken', accessToken);" + " localStorage.setItem('refreshToken', refreshToken);" + " localStorage.setItem('user', JSON.stringify(user));" ) + params: dict = { + "accessToken": session_data["access_token"], + "refreshToken": session_data["refresh_token"], + "user": session_data["user"], + } + if with_editor: + js_inject += " localStorage.setItem('ws-panel-editor', editorPanel);" + params["editorPanel"] = "true" + js_inject += "}" + + page.evaluate(js_inject, params) page.goto(file_url) diff --git a/cli/tests/test_ui.py b/cli/tests/test_ui.py index 1a15980e..eb28cbc0 100644 --- a/cli/tests/test_ui.py +++ b/cli/tests/test_ui.py @@ -39,7 +39,9 @@ def test_ui_opens_browser(self, tmp_path: Path) -> None: with patch("cli.core.SESSION_FILE", session_file): with patch("cli.commands.ui.sync_playwright") as mock_playwright: - mock_browser = mock_playwright.return_value.__enter__.return_value.chromium.launch.return_value + mock_browser = ( + mock_playwright.return_value.__enter__.return_value.chromium.launch.return_value + ) mock_page = mock_browser.new_page.return_value runner = CliRunner() @@ -77,3 +79,107 @@ def test_ui_playwright_flag_outputs_script(self, tmp_path: Path) -> None: assert f"page.goto('http://localhost:{get_frontend_port()}/file/200')" in result.output assert "localStorage.setItem('accessToken'" in result.output assert "# ADD YOUR TEST CODE BELOW" in result.output + + def test_ui_with_editor_sets_localstorage(self, tmp_path: Path) -> None: + """UI command with --with-editor sets ws-panel-editor in localStorage.""" + valid_token = jwt.encode({"sub": "1", "exp": 9999999999}, "secret", algorithm="HS256") + session_file = tmp_path / "session.json" + session_file.write_text( + json.dumps( + { + "access_token": valid_token, + "refresh_token": "refresh", + "user": {"id": 1, "email": "test@example.com"}, + } + ) + ) + + with patch("cli.core.SESSION_FILE", session_file): + with patch("cli.commands.ui.sync_playwright") as mock_playwright: + mock_browser = ( + mock_playwright.return_value.__enter__.return_value.chromium.launch.return_value + ) + mock_page = mock_browser.new_page.return_value + + runner = CliRunner() + result = runner.invoke(cli, ["ui", "200", "--with-editor"]) + + assert result.exit_code == 0 + # evaluate is called once with a dict containing the editor flag + call_args = mock_page.evaluate.call_args + js_code = call_args[0][0] + params = call_args[0][1] + assert "ws-panel-editor" in js_code + assert params.get("editorPanel") == "true" + + def test_ui_without_editor_flag_omits_panel(self, tmp_path: Path) -> None: + """UI command without --with-editor does not set ws-panel-editor.""" + valid_token = jwt.encode({"sub": "1", "exp": 9999999999}, "secret", algorithm="HS256") + session_file = tmp_path / "session.json" + session_file.write_text( + json.dumps( + { + "access_token": valid_token, + "refresh_token": "refresh", + "user": {"id": 1, "email": "test@example.com"}, + } + ) + ) + + with patch("cli.core.SESSION_FILE", session_file): + with patch("cli.commands.ui.sync_playwright") as mock_playwright: + mock_browser = ( + mock_playwright.return_value.__enter__.return_value.chromium.launch.return_value + ) + mock_page = mock_browser.new_page.return_value + + runner = CliRunner() + result = runner.invoke(cli, ["ui", "200"]) + + assert result.exit_code == 0 + call_args = mock_page.evaluate.call_args + js_code = call_args[0][0] + assert "ws-panel-editor" not in js_code + + def test_ui_playwright_with_editor_includes_panel(self, tmp_path: Path) -> None: + """Playwright script with --with-editor includes ws-panel-editor.""" + valid_token = jwt.encode({"sub": "1", "exp": 9999999999}, "secret", algorithm="HS256") + session_file = tmp_path / "session.json" + session_file.write_text( + json.dumps( + { + "access_token": valid_token, + "refresh_token": "refresh", + "user": {"id": 1, "email": "test@example.com", "name": "Test User"}, + } + ) + ) + + with patch("cli.core.SESSION_FILE", session_file): + runner = CliRunner() + result = runner.invoke(cli, ["ui", "200", "--playwright", "--with-editor"]) + + assert result.exit_code == 0 + assert "ws-panel-editor" in result.output + assert "true" in result.output + + def test_ui_playwright_without_editor_omits_panel(self, tmp_path: Path) -> None: + """Playwright script without --with-editor omits ws-panel-editor.""" + valid_token = jwt.encode({"sub": "1", "exp": 9999999999}, "secret", algorithm="HS256") + session_file = tmp_path / "session.json" + session_file.write_text( + json.dumps( + { + "access_token": valid_token, + "refresh_token": "refresh", + "user": {"id": 1, "email": "test@example.com", "name": "Test User"}, + } + ) + ) + + with patch("cli.core.SESSION_FILE", session_file): + runner = CliRunner() + result = runner.invoke(cli, ["ui", "200", "--playwright"]) + + assert result.exit_code == 0 + assert "ws-panel-editor" not in result.output