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
24 changes: 15 additions & 9 deletions application/single_app/templates/group_workspaces.html
Original file line number Diff line number Diff line change
Expand Up @@ -655,17 +655,15 @@ <h2>Group Workspace</h2>
<!-- Shown when user can't manage prompts -->
<div
id="group-prompts-role-warning"
class="alert alert-warning"
style="display: none"
class="alert alert-warning d-none"
>
You do not have permission to manage group prompts.
</div>

<!-- Button to open modal -->
<div
class="mb-3"
id="create-group-prompt-section"
style="display: none"
class="mb-3 d-none"
>
<button id="create-group-prompt-btn" class="btn btn-success btn-sm">
New Prompt
Expand Down Expand Up @@ -4407,11 +4405,19 @@ <h5 class="alert-heading">
const canManage = ["Owner", "Admin", "PromptManager"].includes(
userRoleInActiveGroup
);
// show/hide “New Prompt” button & edit/delete
document.getElementById("create-group-prompt-section").style.display =
canManage ? "block" : "none";
document.getElementById("group-prompts-role-warning").style.display =
canManage ? "none" : "block";
const createGroupPromptSection = document.getElementById(
"create-group-prompt-section"
);
const groupPromptsRoleWarning = document.getElementById(
"group-prompts-role-warning"
);

if (!createGroupPromptSection || !groupPromptsRoleWarning) {
return;
}

createGroupPromptSection.classList.toggle("d-none", !canManage);
groupPromptsRoleWarning.classList.toggle("d-none", canManage);
}

// — Expose & initial load —
Expand Down
6 changes: 6 additions & 0 deletions docs/explanation/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Ver
* General tabular analysis now detects full versus partial result coverage from tool metadata, retries incomplete synthesis when necessary, and adds stronger prompt guidance so the final answer uses the returned analytical results directly.
* (Ref: `route_backend_chats.py`, `test_tabular_exhaustive_result_synthesis_fix.py`, `TABULAR_EXHAUSTIVE_RESULT_SYNTHESIS_FIX.md`)

* **Group Workspace Documents and Prompts Load Recovery**
* Fixed a Group Workspace page-load regression where active-group initialization could fail on a missing prompt-role UI container and stop the rest of the page from rendering correctly.
* Group document and prompt content now continue loading even if the prompt permission banner or create-button container is unavailable during startup, preventing blank content areas caused by a JavaScript null-reference error.
* Added functional and UI regression coverage for the guarded prompt-role path so future changes do not reintroduce the same startup failure.
* (Ref: `group_workspaces.html`, `test_group_workspace_prompt_role_ui_guard.py`, `test_group_workspace_prompt_role_containers_ui.py`)

### **(v0.241.002)**

#### Bug Fixes
Expand Down
87 changes: 87 additions & 0 deletions functional_tests/test_group_workspace_prompt_role_ui_guard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# test_group_workspace_prompt_role_ui_guard.py
"""
Functional test for group workspace prompt role UI guard.
Version: 0.241.007
Implemented in: 0.241.007

This test ensures that the group workspace prompt role UI safely handles
missing prompt containers so active-group loading can continue.
"""

import os
import sys


ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
GROUP_WORKSPACE_TEMPLATE = os.path.join(
ROOT_DIR,
"application",
"single_app",
"templates",
"group_workspaces.html",
)
CONFIG_FILE = os.path.join(
ROOT_DIR,
"application",
"single_app",
"config.py",
)


def read_file(path):
with open(path, "r", encoding="utf-8") as file_handle:
return file_handle.read()


def test_group_workspace_prompt_role_ui_uses_guarded_dom_access():
"""Verify prompt role UI updates tolerate missing containers."""
print("Testing group workspace prompt role UI guard...")

content = read_file(GROUP_WORKSPACE_TEMPLATE)

required_snippets = [
'const createGroupPromptSection = document.getElementById(',
'"create-group-prompt-section"',
'const groupPromptsRoleWarning = document.getElementById(',
'"group-prompts-role-warning"',
'if (!createGroupPromptSection || !groupPromptsRoleWarning) {',
'createGroupPromptSection.classList.toggle("d-none", !canManage);',
'groupPromptsRoleWarning.classList.toggle("d-none", canManage);',
]
missing = [snippet for snippet in required_snippets if snippet not in content]
assert not missing, f"Missing guarded prompt role UI snippets: {missing}"

forbidden_snippets = [
'document.getElementById("create-group-prompt-section").style.display',
'document.getElementById("group-prompts-role-warning").style.display',
]
present = [snippet for snippet in forbidden_snippets if snippet in content]
assert not present, f"Unexpected direct prompt role UI DOM access found: {present}"

print("Prompt role UI guard is present")


def test_config_version_is_bumped_for_prompt_role_ui_guard_fix():
"""Verify config version was bumped for the prompt role UI guard fix."""
print("Testing config version bump...")

config_content = read_file(CONFIG_FILE)
assert 'VERSION = "0.241.007"' in config_content, "Expected config.py version 0.241.007"

print("Config version bump passed")


if __name__ == "__main__":
tests = [
test_group_workspace_prompt_role_ui_uses_guarded_dom_access,
test_config_version_is_bumped_for_prompt_role_ui_guard_fix,
]

results = []
for test in tests:
print(f"\nRunning {test.__name__}...")
results.append(test())

success = all(results)
print(f"\nResults: {sum(results)}/{len(results)} tests passed")
sys.exit(0 if success else 1)
126 changes: 126 additions & 0 deletions ui_tests/test_group_workspace_prompt_role_containers_ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# test_group_workspace_prompt_role_containers_ui.py
"""
UI test for group workspace prompt role UI guard.
Version: 0.241.007
Implemented in: 0.241.007

This test ensures that missing prompt role containers do not break the group
workspace documents tab on first load.
"""

import json
import os
from pathlib import Path

import pytest
from playwright.sync_api import expect


BASE_URL = os.getenv("SIMPLECHAT_UI_BASE_URL", "").rstrip("/")
STORAGE_STATE = os.getenv("SIMPLECHAT_UI_STORAGE_STATE", "")


def _require_ui_env():
if not BASE_URL:
pytest.skip("Set SIMPLECHAT_UI_BASE_URL to run this UI test.")
if not STORAGE_STATE or not Path(STORAGE_STATE).exists():
pytest.skip(
"Set SIMPLECHAT_UI_STORAGE_STATE to a valid authenticated Playwright storage state file."
)


def _fulfill_json(route, payload, status=200):
route.fulfill(
status=status,
content_type="application/json",
body=json.dumps(payload),
)


@pytest.mark.ui
def test_group_workspace_load_tolerates_missing_prompt_role_containers(playwright):
"""Validate documents still render when prompt role containers are absent."""
_require_ui_env()

browser = playwright.chromium.launch()
context = browser.new_context(
storage_state=STORAGE_STATE,
viewport={"width": 1440, "height": 900},
)
page = context.new_page()

page_errors = []
page.on("pageerror", lambda error: page_errors.append(str(error)))
page.add_init_script(
"""
window.addEventListener('DOMContentLoaded', () => {
document.getElementById('group-prompts-role-warning')?.remove();
document.getElementById('create-group-prompt-section')?.remove();
});
"""
)

page.route(
"**/api/groups?page_size=1000",
lambda route: _fulfill_json(
route,
{
"groups": [
{
"id": "group-alpha",
"name": "Alpha Team",
"isActive": True,
"userRole": "Owner",
"status": "active",
}
]
},
),
)
page.route(
"**/api/group_documents?*",
lambda route: _fulfill_json(
route,
{
"documents": [],
"page": 1,
"page_size": 10,
"total_count": 0,
},
),
)
page.route(
"**/api/group_documents/tags?*",
lambda route: _fulfill_json(route, {"tags": []}),
)

try:
response = page.goto(f"{BASE_URL}/group_workspaces", wait_until="networkidle")

assert response is not None, "Expected a navigation response when loading /group_workspaces."
assert response.ok, f"Expected /group_workspaces to load successfully, got HTTP {response.status}."

page.wait_for_function(
"""
() => {
const tbody = document.querySelector('#group-documents-table tbody');
return tbody && tbody.textContent.includes('No documents found in this group.');
}
"""
)

expect(page.locator("#group-documents-table tbody")).to_contain_text(
"No documents found in this group."
)

prompt_role_errors = [
error
for error in page_errors
if "Cannot read properties of null" in error
or "create-group-prompt-section" in error
or "group-prompts-role-warning" in error
]
assert not prompt_role_errors, f"Unexpected prompt role UI page errors: {prompt_role_errors}"
finally:
context.close()
browser.close()
Loading