feat: lazy-load issue capture workflow (Closes #537)
CI / lint (pull_request) Successful in 1m14s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-11 03:30:07 +00:00
1 parent 3bad8a0198
commit 820da7130d
10 files changed
+268 -16

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
import json
import subprocess
from pathlib import Path
LOADER = Path(__file__).parents[1] / "frontend" / "feature-loader.js"
def run_loader(scenario: str) -> dict:
harness = f"""
const fs=require('fs'); const vm=require('vm');
const state={{appends:0, removed:0}};
const document={{
head:{{appendChild(node){{ state.appends++; state.node=node; }} }},
createElement(){{ return {{remove(){{state.removed++;}}}}; }},
}};
const context={{document, setTimeout, clearTimeout, console}};
context.globalThis=context;
vm.createContext(context);
vm.runInContext(fs.readFileSync({json.dumps(str(LOADER))},'utf8'),context);
const createFeatureLoader=context.createFeatureLoader;
(async()=>{{ {scenario} }})().catch(error=>{{console.error(error);process.exit(1);}});
"""
completed = subprocess.run(["node", "-e", harness], check=True, capture_output=True, text=True)
return json.loads(completed.stdout)
def test_concurrent_feature_requests_share_one_script():
result = run_loader("""
const loader=createFeatureLoader({document, urls:{'issue-capture':'feature-a.js'}, timeoutMs:100});
const first=loader.load('issue-capture'); const second=loader.load('issue-capture');
state.node.onload(); await Promise.all([first,second]);
console.log(JSON.stringify({appends:state.appends,same:first===second,ready:loader.ready('issue-capture')}));
""")
assert result == {"appends": 1, "same": True, "ready": True}
def test_failed_feature_request_can_retry_without_reload():
result = run_loader("""
const loader=createFeatureLoader({document, urls:{'issue-capture':'feature-a.js'}, timeoutMs:100});
const failed=loader.load('issue-capture').catch(error=>error.message); state.node.onerror();
const message=await failed; const retry=loader.load('issue-capture'); state.node.onload(); await retry;
console.log(JSON.stringify({appends:state.appends,removed:state.removed,message,ready:loader.ready('issue-capture')}));
""")
assert result == {"appends": 2, "removed": 1, "message": "Could not load issue capture.", "ready": True}
def test_feature_gate_exposes_loading_failure_and_success_to_mobile_controls():
result = run_loader("""
const trigger={disabled:false}; const status={textContent:''}; let opened=0;
const loader=createFeatureLoader({document, urls:{'issue-capture':'feature-a.js'}, timeoutMs:100});
const failed=loader.run('issue-capture',{trigger,status},()=>{opened++;});
const loading={disabled:trigger.disabled,status:status.textContent}; state.node.onerror();
const first=await failed;
const failedState={disabled:trigger.disabled,status:status.textContent,opened};
const retry=loader.run('issue-capture',{trigger,status},()=>{opened++;}); state.node.onload();
const second=await retry;
console.log(JSON.stringify({loading,failedState,first,second,opened,finalStatus:status.textContent}));
""")
assert result == {
"loading": {"disabled": True, "status": "Loading issue capture…"},
"failedState": {
"disabled": False,
"status": "Issue capture could not load. Tap New issue to retry.",
"opened": 0,
},
"first": False,
"second": True,
"opened": 1,
"finalStatus": "",
}
+39 -1
View File
@@ -9,7 +9,7 @@ from starlette.requests import Request
from src import main
from src.frontend_bundle import build_frontend
from src.views import dashboard, runtime_bundle, service_worker
from src.views import dashboard, feature_bundle, runtime_bundle, service_worker
FRONTEND = Path(__file__).resolve().parents[1] / "frontend"
@@ -42,6 +42,44 @@ def test_page_runtime_is_one_deterministic_content_addressed_bundle(tmp_path):
assert changed.runtime_bytes != first.runtime_bytes
def test_issue_capture_is_a_stable_lazy_feature_chunk(tmp_path):
first = build_frontend(FRONTEND)
assert set(first.feature_bundles) == {"issue-capture"}
capture = first.feature_bundles["issue-capture"]
assert b"function createIssueCapture" not in first.runtime_bytes
assert b"function createIssueCapture" in capture.runtime_bytes
assert f'name="stackchain-feature-issue-capture" content="{capture.runtime_name}"' in first.dashboard_html
assert f"BASE + '{capture.runtime_name}'" in first.service_worker_source
changed_frontend = tmp_path / "frontend"
shutil.copytree(FRONTEND, changed_frontend)
capture_source = changed_frontend / "create-issue-sheet.js"
capture_source.write_text(capture_source.read_text() + "\n// feature-only change\n")
changed = build_frontend(changed_frontend)
assert changed.runtime_name == first.runtime_name
assert changed.feature_bundles["issue-capture"].runtime_name != capture.runtime_name
@pytest.mark.anyio
async def test_feature_chunk_is_immutable_and_rejects_unknown_revision():
build = build_frontend(FRONTEND)
capture = build.feature_bundles["issue-capture"]
request = Request(
{"type": "http", "method": "GET", "path": f"/{capture.runtime_name}",
"headers": [(b"accept-encoding", b"identity")]}
)
response = await feature_bundle("issue-capture", capture.runtime_digest, request)
assert response.body == capture.runtime_bytes
assert response.headers["cache-control"] == "public, max-age=31536000, immutable"
with pytest.raises(Exception) as rejected:
await feature_bundle("issue-capture", "missing", request)
assert rejected.value.status_code == 404
def test_css_only_release_gets_a_new_shell_generation(tmp_path):
first = build_frontend(FRONTEND)
changed_frontend = tmp_path / "frontend"
+8
View File
@@ -4337,6 +4337,14 @@ async def test_mobile_my_work_captures_new_issue_in_accessible_draft_safe_sheet(
assert '.create-issue-label-option' in html and 'min-height:44px' in html
def test_new_issue_capture_waits_for_retryable_feature_before_opening():
dashboard_source = (Path(__file__).parents[1] / "frontend" / "dashboard.js").read_text()
assert "await issueCaptureFeatures.run('issue-capture'" in dashboard_source
assert "if (!issueCapture)" in dashboard_source
assert "async function openCreateIssueSheet" in dashboard_source
@pytest.mark.anyio
async def test_mobile_issue_capture_warns_before_queuing_a_possible_duplicate():
html = await dashboard()
+1
View File
@@ -419,6 +419,7 @@ def test_install_precaches_complete_subpath_scoped_app_shell():
"/dashboard/static/icons/stackchain-192.png",
"/dashboard/static/icons/stackchain-512.png",
"/dashboard/static/session.js",
"/dashboard/static/feature-loader.js",
"/dashboard/static/markdown.js",
"/dashboard/static/commands.js",
"/dashboard/static/search-preview.js",