Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions cli/cli/commands/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down
108 changes: 107 additions & 1 deletion cli/tests/test_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Loading