System test cases
Canonical specifications at the system verification level. Each case retains its own Need ID, requirement links and execution history. See Test-case specification for classification and evidence semantics.
STV_TC_SY_001: usability smoke
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_usability_smoke(browser, short_output, protocol):
"""Viewport, hover lifecycle and explicit activation on both transports."""
output = short_output / 'usability'
result = build(FIXTURE, output)
assert result.returncode == 0, result.stderr
with published_url(output,protocol) as url:
browser.get(url)
_exercise_usability(browser)
Interaction procedure and observable outcomes: def _exercise_usability(browser):
wait = WebDriverWait(browser, 10)
wait.until(lambda d: d.find_elements(By.CSS_SELECTOR, '.stv-view[data-stv-revision="0"]'))
root = browser.find_element(By.CSS_SELECTOR, '.stv-view')
viewport = root.find_element(By.CSS_SELECTOR, '.stv-viewport')
assert browser.execute_script('return getComputedStyle(arguments[0]).height', viewport) == '975px'
node = root.find_element(By.CSS_SELECTOR, '.stv-node')
details = node.find_element(By.CSS_SELECTOR, '[data-focus="details"]')
toolbar = root.find_element(By.CSS_SELECTOR, '.stv-toolbar button')
ActionChains(browser).move_to_element(node).perform()
time.sleep(.3)
assert not browser.find_elements(By.CSS_SELECTOR, '.stv-detail')
ActionChains(browser).move_to_element(toolbar).perform()
time.sleep(1.1)
assert not browser.find_elements(By.CSS_SELECTOR, '.stv-detail')
# Measure the actual deadline, independently of WebDriver command latency.
browser.execute_script("""
window.hoverTimes={};
arguments[0].addEventListener('mouseenter',()=>{hoverTimes.node=performance.now()},{once:true});
arguments[1].addEventListener('mouseenter',()=>{hoverTimes.button=performance.now()},{once:true});
const observer=new MutationObserver(()=>{if(document.querySelector('.stv-detail')){
hoverTimes.open=performance.now();observer.disconnect();}});
observer.observe(document.body,{childList:true});
""",node,details)
ActionChains(browser).move_to_element_with_offset(node,-node.size['width']/2+8,-node.size['height']/2+8).perform()
time.sleep(.5)
ActionChains(browser).move_to_element(details).perform()
wait.until(lambda d: d.find_elements(By.CSS_SELECTOR, '.stv-detail'))
times=browser.execute_script('return hoverTimes')
assert 950 <= times['open']-times['node'] < 1500, times
assert 0 <= times['open']-times['button'] < 850, times
# Hover did not focus the view: real Escape must still dismiss it.
browser.execute_script('document.body.tabIndex=-1;document.body.focus()')
browser.switch_to.active_element.send_keys(Keys.ESCAPE)
assert not browser.find_elements(By.CSS_SELECTOR,'.stv-detail')
ActionChains(browser).move_to_element(toolbar).perform()
ActionChains(browser).move_to_element(node).perform()
time.sleep(.2)
browser.switch_to.active_element.send_keys(Keys.ESCAPE)
time.sleep(1.1)
assert not browser.find_elements(By.CSS_SELECTOR,'.stv-detail')
browser.execute_script('arguments[0].focus()', details)
details.send_keys(Keys.ENTER)
assert browser.find_elements(By.CSS_SELECTOR, '.stv-persistent-detail')
browser.switch_to.active_element.send_keys(Keys.ESCAPE)
assert browser.find_elements(By.CSS_SELECTOR, '.stv-persistent-detail')
browser.find_element(By.CSS_SELECTOR,'.stv-persistent-detail button').send_keys(Keys.ENTER)
assert not browser.find_elements(By.CSS_SELECTOR, '.stv-detail')
assert browser.switch_to.active_element == details
# Last, test each cancellation from the same reset viewport, with no
# pointer departure after the action masking whether it cancels the timer.
for selector in ('[aria-label="Pan right"]','.stv-groups button'):
browser.execute_script("arguments[0].querySelector('[aria-label=\"Reset viewport\"]').click()",root)
ActionChains(browser).move_to_element(toolbar).perform()
ActionChains(browser).move_to_element(node).perform()
time.sleep(.2)
browser.execute_script('arguments[0].querySelector(arguments[1]).click()',root,selector)
wait.until(lambda d:root.get_attribute('aria-busy')=='false')
time.sleep(1.1)
assert not browser.find_elements(By.CSS_SELECTOR,'.stv-detail')
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_002: control order smoke
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_control_order_smoke(browser,protocol):
published=Path(__file__).parents[1]/'docs/_build/html'
with published_url(published,protocol) as url:
browser.get(url.rsplit('/',1)[0]+'/demo.html')
_exercise_control_order(browser)
Interaction procedure and observable outcomes: def _exercise_control_order(browser):
wait=WebDriverWait(browser,20)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==2)
root=browser.find_elements(By.CSS_SELECTOR,'.stv-view')[1]
def buttons():
node=next(n for n in root.find_elements(By.CSS_SELECTOR,'.stv-node')
if n.find_element(By.CSS_SELECTOR,'.stv-id').text=='STV_SWR_001')
return node.find_elements(By.CSS_SELECTOR,'.stv-groups button')
def identities():
return [json.loads(b.get_attribute('data-focus'))[1:] for b in buttons()]
expected=[['derives_from','outgoing'],['verifies','incoming'],['allocated_to','outgoing']]
assert identities()==expected
buttons()[0].click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
assert identities()==expected
assert buttons()[0].get_attribute('aria-expanded')=='true'
buttons()[0].click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='2')
assert identities()==expected
assert buttons()[0].get_attribute('aria-expanded')=='false'
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_003: missing placeholder and detail error
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_missing_placeholder_and_detail_error(browser,short_output,protocol):
import shutil
source=short_output/'source';shutil.copytree(FIXTURE,source)
path=source/'requirements.rst';path.write_text(path.read_text(encoding='utf-8').replace(':traces: LEAF_B',':traces: MISSING_A'),encoding='utf-8')
output=short_output/'html';result=build(source,output)
assert result.returncode!=0 and 'MISSING_A' in result.stderr
saved={script:script.read_bytes() for script in (output/'_static/stv/details').glob('*.js')}
for script in saved:script.unlink()
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,20)
wait.until(lambda d:d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))
root=browser.find_element(By.CSS_SELECTOR,'.stv-view')
control=root.find_element(By.CSS_SELECTOR,'[aria-label="Expand Traces (outgoing) for ROOT_A"]')
browser.execute_script('arguments[0].focus()',control);control.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
missing=root.find_element(By.CSS_SELECTOR,'.stv-missing')
# Stable navigation can discover a node left of the current viewport.
# Check its identity, then explicitly fit before testing visible content.
assert 'MISSING_A' in missing.get_attribute('textContent')
geometry=browser.execute_script('return [...arguments[0].querySelectorAll(".stv-node")].map(n=>[n.dataset.key,n.style.left,n.style.top])',root)
browser.execute_script('[...arguments[0].querySelectorAll(".stv-toolbar button")].find(b=>b.textContent==="Reset viewport").click()',root)
assert browser.execute_script('return [...arguments[0].querySelectorAll(".stv-node")].map(n=>[n.dataset.key,n.style.left,n.style.top])',root)==geometry
assert 'MISSING_A' in missing.text and 'Missing object' in missing.text
assert not missing.find_elements(By.CSS_SELECTOR,'a,button')
inspect=root.find_element(By.CSS_SELECTOR,'[aria-label="Details for ROOT_A"]')
browser.execute_script('arguments[0].focus()',inspect);inspect.send_keys(Keys.ENTER)
wait.until(lambda d:'Local detail asset unavailable' in d.find_element(By.CSS_SELECTOR,'.stv-detail').text)
browser.find_element(By.CSS_SELECTOR,'.stv-persistent-detail button').send_keys(Keys.ENTER)
wait.until(lambda d:not d.find_elements(By.CSS_SELECTOR,'.stv-detail'))
for script,content in saved.items():script.write_bytes(content)
inspect.send_keys(Keys.ENTER)
wait.until(lambda d:'formatted body' in d.find_element(By.CSS_SELECTOR,'.stv-detail').text)
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_004: failed finalization retains pending page and previous assets
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_failed_finalization_retains_pending_page_and_previous_assets(tmp_path):
from types import SimpleNamespace
from sphinx.errors import SphinxError
from sphinx_traceability_view.directive.lifecycle import finish
pending='<section data-stv-build="STV_PENDING"></section>'
(tmp_path/'index.html').write_text(pending,encoding='utf-8')
(tmp_path/'requirements.html').write_text('<p>Expected Need body missing</p>',encoding='utf-8')
previous=tmp_path/'_static/stv/graph-old.js';previous.parent.mkdir(parents=True);previous.write_text('previous complete asset',encoding='utf-8')
graph={'model':{'relations':{}},'objects':{'A':{'url':'requirements.html#A','external':False,'parent':None}}}
app=SimpleNamespace(_stv_graph=graph,_stv_hidden=set(),outdir=str(tmp_path),env=SimpleNamespace(stv_views={'index':[{}]}))
with pytest.raises(SphinxError,match='Rendered Need.*missing'):
finish(app,None)
assert (tmp_path/'index.html').read_text(encoding='utf-8')==pending
assert previous.read_text(encoding='utf-8')=='previous complete asset'
assert list(previous.parent.glob('graph-*.js'))==[previous]
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_005: seed layout replacement failure and staleness
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_seed_layout_replacement_failure_and_staleness(browser,short_output,protocol):
from pathlib import Path
project=Path(__file__).parents[1]
script="import{build}from'esbuild';await build({entryPoints:[process.argv[1]],bundle:true,format:'iife',outfile:process.argv[2]})"
result=subprocess.run(['node','--input-type=module','-e',script,str(project/'docs/test-scripts/fixtures/browser-harness.js'),str(short_output/'harness.js')],cwd=project/'frontend',capture_output=True,text=True)
assert result.returncode==0,result.stderr
(short_output/'index.html').write_text('<link rel="stylesheet" href="'+(project/'src/sphinx_traceability_view/static/stv.css').as_uri()+'"><div class="stv-view" id="test-view" data-stv-seeds=\'["A"]\'></div><script src="harness.js"></script>',encoding='utf-8')
with published_url(short_output,protocol) as url:
browser.get(url);_assert_async_layout(browser)
wait=WebDriverWait(browser,20);root=browser.find_element(By.CSS_SELECTOR,'.stv-view')
field=root.find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
previous=root.find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style')
field.send_keys('B');field.send_keys(Keys.ENTER)
wait.until(lambda d:d.execute_script('return calls.length')==7)
browser.execute_script('calls[6].reject(Error("Seed layout failure"))')
wait.until(lambda d:'Seed layout failure' in root.find_element(By.CSS_SELECTOR,'.stv-status').text)
assert root.get_attribute('data-stv-revision')=='2'
assert root.find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style')==previous
assert browser.switch_to.active_element==field
for identity,count in [('A',8),('B',9)]:
field.send_keys(Keys.CONTROL,'a');field.send_keys(Keys.BACKSPACE);field.send_keys(identity);field.send_keys(Keys.ENTER)
wait.until(lambda d:d.execute_script('return calls.length')==count)
browser.execute_script('stvHarness.compute(...calls[8].args).then(calls[8].resolve)')
wait.until(lambda d:root.get_attribute('data-stv-revision')=='3')
browser.execute_script('stvHarness.compute(...calls[7].args).then(calls[7].resolve)')
wait.until(lambda d:d.execute_script('return document.querySelectorAll(".stv-measure").length')==0)
assert root.get_attribute('data-stv-revision')=='3'
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['B']
assert browser.switch_to.active_element==field
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_006: alternate host schema external metadata and parts
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_alternate_host_schema_external_metadata_and_parts(tmp_path):
result=build(FIXTURE.parent/'alternate_model',tmp_path)
assert result.returncode==0,result.stdout+result.stderr
data=manifest(tmp_path)
assert data['model']['types']['hardware']['level']=='hardware'
assert data['model']['relations']['traces']['outgoing']=='Traces to'
assert data['objects']['EXTERNAL_A']['metadata']['owner']=='External owner'
assert data['objects']['EXTERNAL_A']['docname'] is None
assert 'GOAL_A.fragment' in data['objects']
assert 'GOAL_A.fragment' not in data['entries']
assert 'HIDDEN_A' not in data['objects']
assert not any('HIDDEN_A' in edge for edge in data['edges'])
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_007: documentation visuals
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_documentation_visuals(browser):
published=Path(__file__).parents[1]/'docs/_build/html'
assert (published/'demo.html').is_file(),'Build project documentation before publication checks'
browser.set_window_size(1500,1300)
browser.get((published/'demo.html').as_uri())
wait=WebDriverWait(browser,20)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==2)
assert 'Traceability view failed' not in browser.find_element(By.TAG_NAME,'body').text
screenshots=Path(os.environ.get('STV_SCREENSHOTS','docs/_build/browser'));screenshots.mkdir(parents=True,exist_ok=True)
browser.save_screenshot(str(screenshots/'project-demo.png'))
browser.get((published/'state-machine.html').as_uri())
assert browser.find_element(By.CSS_SELECTOR,'object[type="image/svg+xml"]').get_attribute('data').endswith('state-machine.svg')
browser.save_screenshot(str(screenshots/'state-page.png'))
browser.get((published/'state-machine.svg').as_uri())
assert browser.find_elements(By.TAG_NAME,'svg')
assert len(browser.find_elements(By.TAG_NAME,'a'))>=10
browser.save_screenshot(str(screenshots/'state-diagram.png'))
for name in ('system-architecture','software-architecture'):
browser.get((published/(name+'.svg')).as_uri())
assert browser.find_elements(By.TAG_NAME,'svg')
assert browser.find_elements(By.TAG_NAME,'a')
browser.save_screenshot(str(screenshots/(name+'.png')))
page='architecture-system' if name.startswith('system') else 'architecture-software'
browser.get((published/(page+'.html')).as_uri())
assert browser.find_element(By.CSS_SELECTOR,'object[type="image/svg+xml"]')
browser.save_screenshot(str(screenshots/(page+'-page.png')))
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_008: invalid seed source location
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_invalid_seed_source_location(tmp_path, seed):
source = tmp_path / "source"
shutil.copytree(FIXTURE, source)
(source / "index.rst").write_text(f"Invalid\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view:: {seed}\n", encoding="utf-8")
with (source / "requirements.rst").open("a", encoding="utf-8") as stream:
stream.write("\n.. shr:: Hidden\n :id: HIDDEN_A\n :hide:\n")
result = build(source, tmp_path / "html")
assert result.returncode != 0
assert "index.rst:8" in result.stderr
assert "unknown or hidden starting ID" in result.stderr
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_009: control order smoke
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_control_order_smoke(browser,protocol):
published=Path(__file__).parents[1]/'docs/_build/html'
with published_url(published,protocol) as url:
browser.get(url.rsplit('/',1)[0]+'/demo.html')
_exercise_control_order(browser)
Interaction procedure and observable outcomes: def _exercise_control_order(browser):
wait=WebDriverWait(browser,20)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==2)
root=browser.find_elements(By.CSS_SELECTOR,'.stv-view')[1]
def buttons():
node=next(n for n in root.find_elements(By.CSS_SELECTOR,'.stv-node')
if n.find_element(By.CSS_SELECTOR,'.stv-id').text=='STV_SWR_001')
return node.find_elements(By.CSS_SELECTOR,'.stv-groups button')
def identities():
return [json.loads(b.get_attribute('data-focus'))[1:] for b in buttons()]
expected=[['derives_from','outgoing'],['verifies','incoming'],['allocated_to','outgoing']]
assert identities()==expected
buttons()[0].click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
assert identities()==expected
assert buttons()[0].get_attribute('aria-expanded')=='true'
buttons()[0].click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='2')
assert identities()==expected
assert buttons()[0].get_attribute('aria-expanded')=='false'
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_010: interactive seed smoke
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_interactive_seed_smoke(browser,short_output,protocol):
output=short_output/'seed-picker';result=build(FIXTURE,output)
assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,20)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==2)
roots=browser.find_elements(By.CSS_SELECTOR,'.stv-view');root=roots[0]
field=root.find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
field.send_keys('missing');field.send_keys(Keys.ENTER)
assert field.get_attribute('aria-invalid')=='true'
assert root.get_attribute('data-stv-revision')=='0'
field.clear();field.send_keys('software detail')
wait.until(lambda d:root.find_element(By.CSS_SELECTOR,'.stv-seed-matches').is_displayed())
assert 'LEAF_B' in root.find_element(By.CSS_SELECTOR,'.stv-seed-matches').text
field.send_keys(Keys.ARROW_DOWN);field.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['LEAF_B']
assert roots[1].get_attribute('data-stv-revision')=='0'
root.find_element(By.XPATH,'.//button[text()="Project overview"]').click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='2')
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-project'))==1
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['ROOT_A']
centers=browser.execute_script("return [...arguments[0].querySelectorAll('.stv-node')].map(n=>parseFloat(n.style.left)+n.offsetWidth/2)",root)
assert abs(centers[0]-centers[1])<1
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_011: seed capacity at directive
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_seed_capacity_at_directive(tmp_path, count, success):
source = tmp_path / "source"
shutil.copytree(FIXTURE, source)
ids = [f"NEED_{i:03}" for i in range(count)]
(source / "requirements.rst").write_text("Requirements\n============\n\n" + "\n".join(f".. shr:: Item {i}\n :id: {identity}\n" for i, identity in enumerate(ids)), encoding="utf-8")
(source / "index.rst").write_text("Capacity\n========\n\n.. toctree::\n\n requirements\n\n.. traceability-view:: " + " ".join(ids) + "\n", encoding="utf-8")
result = build(source, tmp_path / "html")
assert (result.returncode == 0) is success, result.stderr
if not success:
assert "index.rst:8" in result.stderr
assert "200 distinct starting IDs" in result.stderr
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_012: missing placeholder and detail error
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_missing_placeholder_and_detail_error(browser,short_output,protocol):
import shutil
source=short_output/'source';shutil.copytree(FIXTURE,source)
path=source/'requirements.rst';path.write_text(path.read_text(encoding='utf-8').replace(':traces: LEAF_B',':traces: MISSING_A'),encoding='utf-8')
output=short_output/'html';result=build(source,output)
assert result.returncode!=0 and 'MISSING_A' in result.stderr
saved={script:script.read_bytes() for script in (output/'_static/stv/details').glob('*.js')}
for script in saved:script.unlink()
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,20)
wait.until(lambda d:d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))
root=browser.find_element(By.CSS_SELECTOR,'.stv-view')
control=root.find_element(By.CSS_SELECTOR,'[aria-label="Expand Traces (outgoing) for ROOT_A"]')
browser.execute_script('arguments[0].focus()',control);control.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
missing=root.find_element(By.CSS_SELECTOR,'.stv-missing')
# Stable navigation can discover a node left of the current viewport.
# Check its identity, then explicitly fit before testing visible content.
assert 'MISSING_A' in missing.get_attribute('textContent')
geometry=browser.execute_script('return [...arguments[0].querySelectorAll(".stv-node")].map(n=>[n.dataset.key,n.style.left,n.style.top])',root)
browser.execute_script('[...arguments[0].querySelectorAll(".stv-toolbar button")].find(b=>b.textContent==="Reset viewport").click()',root)
assert browser.execute_script('return [...arguments[0].querySelectorAll(".stv-node")].map(n=>[n.dataset.key,n.style.left,n.style.top])',root)==geometry
assert 'MISSING_A' in missing.text and 'Missing object' in missing.text
assert not missing.find_elements(By.CSS_SELECTOR,'a,button')
inspect=root.find_element(By.CSS_SELECTOR,'[aria-label="Details for ROOT_A"]')
browser.execute_script('arguments[0].focus()',inspect);inspect.send_keys(Keys.ENTER)
wait.until(lambda d:'Local detail asset unavailable' in d.find_element(By.CSS_SELECTOR,'.stv-detail').text)
browser.find_element(By.CSS_SELECTOR,'.stv-persistent-detail button').send_keys(Keys.ENTER)
wait.until(lambda d:not d.find_elements(By.CSS_SELECTOR,'.stv-detail'))
for script,content in saved.items():script.write_bytes(content)
inspect.send_keys(Keys.ENTER)
wait.until(lambda d:'formatted body' in d.find_element(By.CSS_SELECTOR,'.stv-detail').text)
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_013: cross page bundle
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_cross_page_bundle(tmp_path):
result = build(FIXTURE, tmp_path)
assert result.returncode == 0, result.stdout + result.stderr
html = (tmp_path / "index.html").read_text(encoding="utf-8")
assert "STV_PENDING" not in html
soup = BeautifulSoup(html, "html.parser")
roots = soup.select(".stv-view")
assert len(roots) == 2
assert json.loads(roots[0]["data-stv-seeds"]) == ["ROOT_A", "LEAF_B"]
assert json.loads(roots[1]["data-stv-seeds"]) == []
script = soup.select_one("script[data-stv-graph]")
assert (tmp_path / script["src"]).is_file()
fragments = list((tmp_path / "_static/stv/details").glob("*.js"))
assert len(fragments) == 2
assert any("formatted body" in p.read_text() for p in fragments)
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_014: invalid seed source location
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_invalid_seed_source_location(tmp_path, seed):
source = tmp_path / "source"
shutil.copytree(FIXTURE, source)
(source / "index.rst").write_text(f"Invalid\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view:: {seed}\n", encoding="utf-8")
with (source / "requirements.rst").open("a", encoding="utf-8") as stream:
stream.write("\n.. shr:: Hidden\n :id: HIDDEN_A\n :hide:\n")
result = build(source, tmp_path / "html")
assert result.returncode != 0
assert "index.rst:8" in result.stderr
assert "unknown or hidden starting ID" in result.stderr
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_015: alternate model geometry details and external
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_alternate_model_geometry_details_and_external(browser,short_output,protocol):
output=short_output/'html';result=build(FIXTURE.parent/'alternate_model',output);assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,30)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==3)
roots=browser.find_elements(By.CSS_SELECTOR,'.stv-view');root=roots[0]
assert browser.execute_script('''const n=arguments[0].querySelector('.stv-node').getBoundingClientRect(),v=arguments[0].querySelector('.stv-viewport').getBoundingClientRect();return n.top>=v.top&&n.bottom<=v.bottom''',root)
def group(identity,direction):
return root.find_element(By.CSS_SELECTOR,f'[aria-label="Expand Traces {"to" if direction=="outgoing" else "from"} ({direction}) for {identity}"]')
control=group('SOFTWARE_A','outgoing');browser.execute_script('arguments[0].focus()',control);control.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
control=group('GOAL_A','outgoing');browser.execute_script('arguments[0].focus()',control);control.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='2')
boxes=browser.execute_script('''return Object.fromEntries([...arguments[0].querySelectorAll('.stv-nodes>.stv-node')].map(n=>[n.querySelector('.stv-id').textContent,{x:parseFloat(n.style.left),y:parseFloat(n.style.top),width:n.offsetWidth,height:n.offsetHeight}]))''',root)
assert len(boxes)==6
assert 'HIDDEN_A' not in boxes
for a,b in zip(['GOAL_A','HARDWARE_A','SYSTEM_A','SOFTWARE_A'],['HARDWARE_A','SYSTEM_A','SOFTWARE_A','MODULE_A']):assert boxes[a]['y']+boxes[a]['height']<boxes[b]['y']
assert boxes['CHECK_A']['x']>=max(v['x']+v['width'] for k,v in boxes.items() if k!='CHECK_A')+96
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-edges polyline'))==7
assert browser.execute_script('''const title=arguments[0].querySelector('[data-key*="GOAL_A"] .stv-title');return title.scrollHeight>title.clientHeight&&title.clientHeight<=2*parseFloat(getComputedStyle(title).lineHeight)+1''',root)
long_id=roots[1].find_element(By.CSS_SELECTOR,'[data-key*=LONG_IDENTIFIER] .stv-id')
assert long_id.get_attribute('textContent')=='LONG_IDENTIFIER_WITH_MANY_SEGMENTS_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789'
assert browser.execute_script('return arguments[0].scrollWidth<=arguments[0].clientWidth',long_id)
assert len(roots[1].find_elements(By.CSS_SELECTOR,'.stv-unaccepted'))==2
# A part seed inspects the complete published containing Need.
part=roots[2].find_element(By.CSS_SELECTOR,'[aria-label="Details for GOAL_A.fragment"]')
browser.execute_script('arguments[0].focus()',part);part.send_keys(Keys.ENTER)
wait.until(lambda d:'Static widget text' in d.find_element(By.CSS_SELECTOR,'.stv-detail').text)
panel=browser.find_element(By.CSS_SELECTOR,'.stv-detail')
metadata=panel.find_element(By.CSS_SELECTOR,'.needs_collapse')
assert metadata.get_attribute('aria-expanded')=='false'
browser.execute_script('arguments[0].click()',metadata)
assert 'Fixture owner' in panel.text
assert 'Resource unavailable offline' in panel.text
assert not panel.find_elements(By.CSS_SELECTOR,'script,iframe,object,[onerror]')
assert not browser.execute_script('return window.STV_WIDGET_EXECUTED===true')
assert panel.find_element(By.CSS_SELECTOR,'img[data-stv-src]').get_attribute('complete')=='true'
assert browser.execute_script('return arguments[0].naturalWidth',panel.find_element(By.CSS_SELECTOR,'img[data-stv-src]'))>0
assert browser.execute_script('return arguments[0].naturalWidth',panel.find_element(By.CSS_SELECTOR,'.fixture-widget img'))>0
label=panel.find_element(By.CSS_SELECTOR,'label');field=panel.find_element(By.CSS_SELECTOR,'input')
assert label.get_attribute('for')==field.get_attribute('id')
assert field.get_attribute('id').startswith(panel.get_attribute('id'))
assert 'HIDDEN_A' not in panel.text
assert not browser.execute_script("return performance.getEntriesByType('resource').some(e=>e.name.includes('example.invalid'))")
assert browser.execute_script('return arguments[0].scrollHeight>arguments[0].clientHeight',panel)
browser.execute_script('arguments[0].focus()',panel);panel.send_keys(Keys.END)
wait.until(lambda d:browser.execute_script('return arguments[0].scrollTop>0',panel))
original=panel.find_element(By.CSS_SELECTOR,'a[href*="example.invalid/remote.png"]')
assert original.text=='Original resource'
panel.find_element(By.CSS_SELECTOR,'button').click()
external=roots[1].find_element(By.CSS_SELECTOR,'[aria-label="Details for EXTERNAL_A"]')
browser.execute_script('arguments[0].focus()',external);external.send_keys(Keys.ENTER)
wait.until(lambda d:'External owner' in d.find_element(By.CSS_SELECTOR,'.stv-detail').text)
assert 'full body unavailable offline' in browser.find_element(By.CSS_SELECTOR,'.stv-detail').text
browser.find_element(By.CSS_SELECTOR,'.stv-detail button').click()
isolated=roots[1].find_element(By.CSS_SELECTOR,'.stv-unaccepted')
browser.execute_script('arguments[0].focus()',isolated.find_element(By.CSS_SELECTOR,'[data-focus="details"]'))
wait.until(lambda d:isolated.find_element(By.CSS_SELECTOR,'.stv-id').text=='ISOLATED_A')
assert isolated.find_element(By.CSS_SELECTOR,'.stv-id').text=='ISOLATED_A'
assert 'No relationships' in isolated.text
assert not isolated.find_elements(By.CSS_SELECTOR,'.stv-groups button')
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_016: usability smoke
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_usability_smoke(browser, short_output, protocol):
"""Viewport, hover lifecycle and explicit activation on both transports."""
output = short_output / 'usability'
result = build(FIXTURE, output)
assert result.returncode == 0, result.stderr
with published_url(output,protocol) as url:
browser.get(url)
_exercise_usability(browser)
Interaction procedure and observable outcomes: def _exercise_usability(browser):
wait = WebDriverWait(browser, 10)
wait.until(lambda d: d.find_elements(By.CSS_SELECTOR, '.stv-view[data-stv-revision="0"]'))
root = browser.find_element(By.CSS_SELECTOR, '.stv-view')
viewport = root.find_element(By.CSS_SELECTOR, '.stv-viewport')
assert browser.execute_script('return getComputedStyle(arguments[0]).height', viewport) == '975px'
node = root.find_element(By.CSS_SELECTOR, '.stv-node')
details = node.find_element(By.CSS_SELECTOR, '[data-focus="details"]')
toolbar = root.find_element(By.CSS_SELECTOR, '.stv-toolbar button')
ActionChains(browser).move_to_element(node).perform()
time.sleep(.3)
assert not browser.find_elements(By.CSS_SELECTOR, '.stv-detail')
ActionChains(browser).move_to_element(toolbar).perform()
time.sleep(1.1)
assert not browser.find_elements(By.CSS_SELECTOR, '.stv-detail')
# Measure the actual deadline, independently of WebDriver command latency.
browser.execute_script("""
window.hoverTimes={};
arguments[0].addEventListener('mouseenter',()=>{hoverTimes.node=performance.now()},{once:true});
arguments[1].addEventListener('mouseenter',()=>{hoverTimes.button=performance.now()},{once:true});
const observer=new MutationObserver(()=>{if(document.querySelector('.stv-detail')){
hoverTimes.open=performance.now();observer.disconnect();}});
observer.observe(document.body,{childList:true});
""",node,details)
ActionChains(browser).move_to_element_with_offset(node,-node.size['width']/2+8,-node.size['height']/2+8).perform()
time.sleep(.5)
ActionChains(browser).move_to_element(details).perform()
wait.until(lambda d: d.find_elements(By.CSS_SELECTOR, '.stv-detail'))
times=browser.execute_script('return hoverTimes')
assert 950 <= times['open']-times['node'] < 1500, times
assert 0 <= times['open']-times['button'] < 850, times
# Hover did not focus the view: real Escape must still dismiss it.
browser.execute_script('document.body.tabIndex=-1;document.body.focus()')
browser.switch_to.active_element.send_keys(Keys.ESCAPE)
assert not browser.find_elements(By.CSS_SELECTOR,'.stv-detail')
ActionChains(browser).move_to_element(toolbar).perform()
ActionChains(browser).move_to_element(node).perform()
time.sleep(.2)
browser.switch_to.active_element.send_keys(Keys.ESCAPE)
time.sleep(1.1)
assert not browser.find_elements(By.CSS_SELECTOR,'.stv-detail')
browser.execute_script('arguments[0].focus()', details)
details.send_keys(Keys.ENTER)
assert browser.find_elements(By.CSS_SELECTOR, '.stv-persistent-detail')
browser.switch_to.active_element.send_keys(Keys.ESCAPE)
assert browser.find_elements(By.CSS_SELECTOR, '.stv-persistent-detail')
browser.find_element(By.CSS_SELECTOR,'.stv-persistent-detail button').send_keys(Keys.ENTER)
assert not browser.find_elements(By.CSS_SELECTOR, '.stv-detail')
assert browser.switch_to.active_element == details
# Last, test each cancellation from the same reset viewport, with no
# pointer departure after the action masking whether it cancels the timer.
for selector in ('[aria-label="Pan right"]','.stv-groups button'):
browser.execute_script("arguments[0].querySelector('[aria-label=\"Reset viewport\"]').click()",root)
ActionChains(browser).move_to_element(toolbar).perform()
ActionChains(browser).move_to_element(node).perform()
time.sleep(.2)
browser.execute_script('arguments[0].querySelector(arguments[1]).click()',root,selector)
wait.until(lambda d:root.get_attribute('aria-busy')=='false')
time.sleep(1.1)
assert not browser.find_elements(By.CSS_SELECTOR,'.stv-detail')
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_017: large project at visible capacity
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_large_project_at_visible_capacity(browser,protocol):
output=Path(os.environ.get('STV_CAPACITY_OUTPUT','docs/_build/benchmark-v2/html')).resolve()
assert (output/'index.html').is_file(),'Run scripts/benchmark.py before capacity verification'
runs=[]
browser.set_script_timeout(35)
temporary=tempfile.TemporaryDirectory(prefix='stv-cap-') if protocol=='file' else None
if temporary:
assert Path(temporary.name).resolve().is_relative_to(Path(tempfile.gettempdir()).resolve())
# Full hash names need a short physical output root in Windows Firefox.
short=Path(temporary.name)/'html';shutil.copytree(output,short);output=short
try:
_measure_capacity(browser,output,protocol,runs)
finally:
if temporary:
assert Path(temporary.name).resolve().is_relative_to(Path(tempfile.gettempdir()).resolve())
temporary.cleanup()
result={'browser':browser.capabilities['browserVersion'],'geckodriver':browser.capabilities.get('moz:geckodriverVersion'),
'memory_scope':'Sum of working sets of the isolated Firefox process tree, including content/GPU helpers; shared pages may be counted more than once',
'runs':runs,'startup_median_ms':statistics.median(r['startup_ms'] for r in runs),
'startup_max_ms':max(r['startup_ms'] for r in runs),
'expansion_median_ms':statistics.median(v for r in runs for v in r['expansion_ms']),
'expansion_max_ms':max(v for r in runs for v in r['expansion_ms'])}
target=Path(os.environ.get('STV_CAPACITY_METRICS','docs/_build/capacity-browser.json'));target.parent.mkdir(parents=True,exist_ok=True)
aggregate=json.loads(target.read_text(encoding='utf-8')) if target.exists() else {}
aggregate[protocol]=result
target.write_text(json.dumps(aggregate,indent=2),encoding='utf-8')
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_018: invalid mapping has entry path and location
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_invalid_mapping_has_entry_path_and_location(tmp_path):
source=tmp_path/'source';shutil.copytree(FIXTURE,source)
with (source/'conf.py').open('a',encoding='utf-8') as stream:
stream.write('\ntraceability_view_model["types"]["shr"]["level"]="unknown"\n')
result=build(source,tmp_path/'html')
assert result.returncode!=0
assert 'index.rst:8' in result.stderr
assert 'types.shr' in result.stderr
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_019: seed selection release
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_seed_selection_release(browser,short_output,protocol):
source=short_output/'source';shutil.copytree(FIXTURE,source)
requirements='Requirements\n============\n\n'
for i in range(12):
requirements+=f'.. shr:: Goal {i}\n :id: GOAL_{i:03}\n'
if i==0:
requirements+=' :traces: '+', '.join(f'CHILD_{j}' for j in range(5))+'\n'
requirements+='\n'
requirements+='.. shr:: Secret hidden title\n :id: HIDDEN_A\n :hide:\n\n'
requirements+='\n'.join(f'.. swr:: Software child {j}\n :id: CHILD_{j}\n' for j in range(5))
(source/'requirements.rst').write_text(requirements,encoding='utf-8')
(source/'index.rst').write_text('Project\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view::\n\n.. traceability-view:: GOAL_000\n',encoding='utf-8')
output=short_output/'html';result=build(source,output);assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,20)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==2)
roots=browser.find_elements(By.CSS_SELECTOR,'.stv-view');root=roots[0]
field=root.find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==[f'GOAL_{i:03}' for i in range(5)]
field.send_keys('software child')
assert root.get_attribute('data-stv-revision')=='0'
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-seed-matches [role="option"]'))==5
field.send_keys(Keys.ESCAPE)
assert field.get_attribute('aria-expanded')=='false'
field.send_keys(Keys.CONTROL,'a');field.send_keys(Keys.BACKSPACE);field.send_keys('software child')
root.find_elements(By.CSS_SELECTOR,'.stv-seed-matches [role="option"]')[2].click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['CHILD_2']
assert browser.switch_to.active_element==field
for invalid in ['', 'HIDDEN_A', 'Secret hidden', 'goal_000']:
field.send_keys(Keys.CONTROL,'a');field.send_keys(Keys.BACKSPACE)
if invalid:field.send_keys(invalid)
if invalid!='goal_000':
assert not root.find_elements(By.CSS_SELECTOR,'.stv-seed-matches [role="option"]')
field.send_keys(Keys.ENTER)
assert field.get_attribute('aria-invalid')=='true'
assert root.get_attribute('data-stv-revision')=='1'
field.send_keys(Keys.CONTROL,'a');field.send_keys(Keys.BACKSPACE);field.send_keys(' GOAL_000 ');field.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='2')
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['GOAL_000']
control=root.find_element(By.CSS_SELECTOR,'[aria-label="Expand Traces (outgoing) for GOAL_000"]')
browser.execute_script('arguments[0].click()',control)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='3')
boxes=browser.execute_script("""return Object.fromEntries([...arguments[0].querySelectorAll('.stv-node')].map(n=>[n.querySelector('.stv-id').textContent,{x:parseFloat(n.style.left),w:n.offsetWidth}]))""",root)
children=[boxes[f'CHILD_{i}'] for i in range(5)]
midpoint=(min(n['x'] for n in children)+max(n['x']+n['w'] for n in children))/2
assert abs(boxes['GOAL_000']['x']+boxes['GOAL_000']['w']/2-midpoint)<1
root.find_element(By.XPATH,'.//button[text()="Project overview"]').click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='4')
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-node'))==6
assert roots[1].get_attribute('data-stv-revision')=='0'
other=roots[1].find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
other.send_keys('CHILD_4');other.send_keys(Keys.ENTER)
wait.until(lambda d:roots[1].get_attribute('data-stv-revision')=='1')
assert root.get_attribute('data-stv-revision')=='4'
assert [n.text for n in roots[1].find_elements(By.CSS_SELECTOR,'.stv-id')]==['CHILD_4']
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_020: async layout failure and stale completion
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_async_layout_failure_and_stale_completion(browser,short_output,protocol):
project=Path(__file__).parents[1]
script="import{build}from'esbuild';await build({entryPoints:[process.argv[1]],bundle:true,format:'iife',outfile:process.argv[2]})"
result=subprocess.run(['node','--input-type=module','-e',script,str(project/'docs/test-scripts/fixtures/browser-harness.js'),str(short_output/'harness.js')],cwd=project/'frontend',capture_output=True,text=True)
assert result.returncode==0,result.stderr
(short_output/'index.html').write_text('<link rel="stylesheet" href="'+(project/'src/sphinx_traceability_view/static/stv.css').as_uri()+'"><div class="stv-view" id="test-view" data-stv-seeds=\'["A"]\'></div><script src="harness.js"></script>',encoding='utf-8')
# Keep the host alive across all injected completions.
with published_url(short_output,protocol) as url:
browser.get(url)
_assert_async_layout(browser)
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_021: interactive seed smoke
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_interactive_seed_smoke(browser,short_output,protocol):
output=short_output/'seed-picker';result=build(FIXTURE,output)
assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,20)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==2)
roots=browser.find_elements(By.CSS_SELECTOR,'.stv-view');root=roots[0]
field=root.find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
field.send_keys('missing');field.send_keys(Keys.ENTER)
assert field.get_attribute('aria-invalid')=='true'
assert root.get_attribute('data-stv-revision')=='0'
field.clear();field.send_keys('software detail')
wait.until(lambda d:root.find_element(By.CSS_SELECTOR,'.stv-seed-matches').is_displayed())
assert 'LEAF_B' in root.find_element(By.CSS_SELECTOR,'.stv-seed-matches').text
field.send_keys(Keys.ARROW_DOWN);field.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['LEAF_B']
assert roots[1].get_attribute('data-stv-revision')=='0'
root.find_element(By.XPATH,'.//button[text()="Project overview"]').click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='2')
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-project'))==1
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['ROOT_A']
centers=browser.execute_script("return [...arguments[0].querySelectorAll('.stv-node')].map(n=>parseFloat(n.style.left)+n.offsetWidth/2)",root)
assert abs(centers[0]-centers[1])<1
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_022: invalid seed source location
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_invalid_seed_source_location(tmp_path, seed):
source = tmp_path / "source"
shutil.copytree(FIXTURE, source)
(source / "index.rst").write_text(f"Invalid\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view:: {seed}\n", encoding="utf-8")
with (source / "requirements.rst").open("a", encoding="utf-8") as stream:
stream.write("\n.. shr:: Hidden\n :id: HIDDEN_A\n :hide:\n")
result = build(source, tmp_path / "html")
assert result.returncode != 0
assert "index.rst:8" in result.stderr
assert "unknown or hidden starting ID" in result.stderr
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_023: seed capacity at directive
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_seed_capacity_at_directive(tmp_path, count, success):
source = tmp_path / "source"
shutil.copytree(FIXTURE, source)
ids = [f"NEED_{i:03}" for i in range(count)]
(source / "requirements.rst").write_text("Requirements\n============\n\n" + "\n".join(f".. shr:: Item {i}\n :id: {identity}\n" for i, identity in enumerate(ids)), encoding="utf-8")
(source / "index.rst").write_text("Capacity\n========\n\n.. toctree::\n\n requirements\n\n.. traceability-view:: " + " ".join(ids) + "\n", encoding="utf-8")
result = build(source, tmp_path / "html")
assert (result.returncode == 0) is success, result.stderr
if not success:
assert "index.rst:8" in result.stderr
assert "200 distinct starting IDs" in result.stderr
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_024: prior fatal exception is not replaced by bundle generation
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_prior_fatal_exception_is_not_replaced_by_bundle_generation(tmp_path):
from types import SimpleNamespace
from sphinx_traceability_view.directive.lifecycle import finish
page=tmp_path/'index.html';page.write_text('previous complete page',encoding='utf-8')
app=SimpleNamespace(_stv_graph={},outdir=str(tmp_path))
finish(app,RuntimeError('Earlier fatal failure'))
assert page.read_text(encoding='utf-8')=='previous complete page'
assert not (tmp_path/'_static').exists()
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_025: project paging
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_project_paging(browser,short_output,protocol):
import shutil
source=short_output/'source';shutil.copytree(FIXTURE,source)
(source/'requirements.rst').write_text('Requirements\n============\n\n'+'\n'.join(f'.. shr:: Stakeholder {i}\n :id: GOAL_{i:03}\n' for i in range(201)),encoding='utf-8')
(source/'index.rst').write_text('Project\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view::\n',encoding='utf-8')
output=short_output/'html';result=build(source,output);assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url)
_assert_project_pages(browser)
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_027: host owns broken trace failure
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_host_owns_broken_trace_failure(tmp_path):
source = tmp_path / "source"
shutil.copytree(FIXTURE, source)
with (source / "conf.py").open("a", encoding="utf-8") as stream:
stream.write('\nneeds_report_dead_links = True\n')
path = source / "requirements.rst"
path.write_text(path.read_text(encoding="utf-8").replace(":traces: LEAF_B", ":traces: MISSING_A"), encoding="utf-8")
output = tmp_path / "html"
result = build(source, output)
assert result.returncode != 0
assert "MISSING_A" in result.stderr
assert "STV_PENDING" not in (output / "index.html").read_text(encoding="utf-8")
data = manifest(output)
assert ["ROOT_A", "traces", "MISSING_A"] in data["edges"]
assert "MISSING_A" not in data["objects"]
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_028: seed selection release
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_seed_selection_release(browser,short_output,protocol):
source=short_output/'source';shutil.copytree(FIXTURE,source)
requirements='Requirements\n============\n\n'
for i in range(12):
requirements+=f'.. shr:: Goal {i}\n :id: GOAL_{i:03}\n'
if i==0:
requirements+=' :traces: '+', '.join(f'CHILD_{j}' for j in range(5))+'\n'
requirements+='\n'
requirements+='.. shr:: Secret hidden title\n :id: HIDDEN_A\n :hide:\n\n'
requirements+='\n'.join(f'.. swr:: Software child {j}\n :id: CHILD_{j}\n' for j in range(5))
(source/'requirements.rst').write_text(requirements,encoding='utf-8')
(source/'index.rst').write_text('Project\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view::\n\n.. traceability-view:: GOAL_000\n',encoding='utf-8')
output=short_output/'html';result=build(source,output);assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,20)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==2)
roots=browser.find_elements(By.CSS_SELECTOR,'.stv-view');root=roots[0]
field=root.find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==[f'GOAL_{i:03}' for i in range(5)]
field.send_keys('software child')
assert root.get_attribute('data-stv-revision')=='0'
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-seed-matches [role="option"]'))==5
field.send_keys(Keys.ESCAPE)
assert field.get_attribute('aria-expanded')=='false'
field.send_keys(Keys.CONTROL,'a');field.send_keys(Keys.BACKSPACE);field.send_keys('software child')
root.find_elements(By.CSS_SELECTOR,'.stv-seed-matches [role="option"]')[2].click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['CHILD_2']
assert browser.switch_to.active_element==field
for invalid in ['', 'HIDDEN_A', 'Secret hidden', 'goal_000']:
field.send_keys(Keys.CONTROL,'a');field.send_keys(Keys.BACKSPACE)
if invalid:field.send_keys(invalid)
if invalid!='goal_000':
assert not root.find_elements(By.CSS_SELECTOR,'.stv-seed-matches [role="option"]')
field.send_keys(Keys.ENTER)
assert field.get_attribute('aria-invalid')=='true'
assert root.get_attribute('data-stv-revision')=='1'
field.send_keys(Keys.CONTROL,'a');field.send_keys(Keys.BACKSPACE);field.send_keys(' GOAL_000 ');field.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='2')
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['GOAL_000']
control=root.find_element(By.CSS_SELECTOR,'[aria-label="Expand Traces (outgoing) for GOAL_000"]')
browser.execute_script('arguments[0].click()',control)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='3')
boxes=browser.execute_script("""return Object.fromEntries([...arguments[0].querySelectorAll('.stv-node')].map(n=>[n.querySelector('.stv-id').textContent,{x:parseFloat(n.style.left),w:n.offsetWidth}]))""",root)
children=[boxes[f'CHILD_{i}'] for i in range(5)]
midpoint=(min(n['x'] for n in children)+max(n['x']+n['w'] for n in children))/2
assert abs(boxes['GOAL_000']['x']+boxes['GOAL_000']['w']/2-midpoint)<1
root.find_element(By.XPATH,'.//button[text()="Project overview"]').click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='4')
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-node'))==6
assert roots[1].get_attribute('data-stv-revision')=='0'
other=roots[1].find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
other.send_keys('CHILD_4');other.send_keys(Keys.ENTER)
wait.until(lambda d:roots[1].get_attribute('data-stv-revision')=='1')
assert root.get_attribute('data-stv-revision')=='4'
assert [n.text for n in roots[1].find_elements(By.CSS_SELECTOR,'.stv-id')]==['CHILD_4']
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_029: host default directional labels are used
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_host_default_directional_labels_are_used(tmp_path):
source=tmp_path/'source';shutil.copytree(FIXTURE,source)
with (source/'conf.py').open('a',encoding='utf-8') as stream:
stream.write('\nneeds_links={"traces":{}}\n')
output=tmp_path/'html';result=build(source,output)
assert result.returncode==0,result.stderr
assert manifest(output)['model']['relations']['traces']=={'incoming':'traces incoming','outgoing':'traces'}
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_030: offline exploration
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_offline_exploration(browser, short_output, protocol):
tmp_path = short_output
output = tmp_path / "nested" / "documentation"
result = build(FIXTURE, output)
assert result.returncode == 0, result.stderr
server = None
try:
if protocol == "http":
server = ThreadingHTTPServer(("127.0.0.1", 0), partial(SimpleHTTPRequestHandler, directory=str(tmp_path)))
threading.Thread(target=server.serve_forever, daemon=True).start()
url = f"http://127.0.0.1:{server.server_port}/nested/documentation/index.html"
else:
url = (output / "index.html").as_uri()
browser.get(url)
wait = WebDriverWait(browser, 20)
wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, '.stv-view[data-stv-revision="0"]')) == 2)
roots = browser.find_elements(By.CSS_SELECTOR, ".stv-view")
assert len(roots[0].find_elements(By.CSS_SELECTOR, ".stv-node")) == 2
assert len(roots[1].find_elements(By.CSS_SELECTOR, ".stv-node")) == 2
root_button = roots[1].find_element(By.CSS_SELECTOR, '[data-focus="root"]')
browser.execute_script("arguments[0].focus()", root_button)
root_button.send_keys(Keys.ENTER)
wait.until(lambda d: roots[1].get_attribute("data-stv-revision") == "1")
assert len(roots[1].find_elements(By.CSS_SELECTOR, ".stv-node")) == 1
assert roots[0].get_attribute("data-stv-revision") == "0"
inspect = roots[0].find_element(By.CSS_SELECTOR, '[aria-label="Details for ROOT_A"]')
browser.execute_script("arguments[0].focus()", inspect)
inspect.send_keys(Keys.ENTER)
try:
wait.until(lambda d: "formatted body" in d.find_element(By.CSS_SELECTOR, ".stv-detail").text)
except Exception:
print(browser.find_element(By.CSS_SELECTOR, ".stv-detail").get_attribute("outerHTML"))
raise
assert browser.find_elements(By.CSS_SELECTOR, ".stv-detail table")
browser.switch_to.active_element.send_keys(Keys.ESCAPE)
assert browser.find_elements(By.CSS_SELECTOR,'.stv-persistent-detail')
browser.find_element(By.CSS_SELECTOR,'.stv-persistent-detail button').send_keys(Keys.ENTER)
wait.until(lambda d: not d.find_elements(By.CSS_SELECTOR, ".stv-detail"))
assert browser.switch_to.active_element == inspect
assert roots[0].find_element(By.CSS_SELECTOR, '.stv-node.stv-unaccepted .stv-id').text == 'LEAF_B'
assert roots[0].find_element(By.CSS_SELECTOR, '.stv-node:not(.stv-unaccepted) .stv-id').text == 'ROOT_A'
# Pointer hover presents the same final host fragment; pointer travel
# into the interactive panel keeps it open beyond the dismissal grace.
ActionChains(browser).move_to_element(inspect).perform()
wait.until(lambda d: d.find_elements(By.CSS_SELECTOR, '.stv-detail'))
panel=browser.find_element(By.CSS_SELECTOR,'.stv-detail')
ActionChains(browser).move_to_element(panel).perform()
wait.until(lambda d: 'formatted body' in panel.text)
assert browser.execute_script('return arguments[0].getBoundingClientRect().right <= innerWidth',panel)
assert browser.execute_script('return arguments[0].getBoundingClientRect().bottom <= innerHeight',panel)
panel.find_element(By.CSS_SELECTOR,'button').click()
# View-local transforms are shared by edge and node layers.
other_transform=roots[1].find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style')
for label in ['Zoom in','Pan right','Pan down']:
control=roots[0].find_element(By.CSS_SELECTOR,f'[aria-label="{label}"]')
browser.execute_script('arguments[0].focus()',control);control.send_keys(Keys.ENTER)
assert roots[1].find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style') == other_transform
roots[0].find_element(By.CSS_SELECTOR,'[aria-label="Reset viewport"]').click()
viewport=roots[0].find_element(By.CSS_SELECTOR,'.stv-viewport')
scene=roots[0].find_element(By.CSS_SELECTOR,'.stv-scene')
before_drag=scene.get_attribute('style')
x,y=browser.execute_script('const r=arguments[0].getBoundingClientRect();return [Math.min(r.right,innerWidth)-20,Math.min(r.bottom,innerHeight)-20]',viewport)
drag=ActionChains(browser)
drag.w3c_actions.pointer_action.move_to_location(x,y)
drag.click_and_hold().move_by_offset(-80,-60).release().perform()
wait.until(lambda d:scene.get_attribute('style')!=before_drag)
before_wheel=scene.get_attribute('style')
ActionChains(browser).scroll_from_origin(ScrollOrigin.from_element(viewport),0,-120).perform()
wait.until(lambda d:scene.get_attribute('style')!=before_wheel)
assert len(browser.window_handles)==1
assert roots[1].find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style') == other_transform
roots[0].find_element(By.CSS_SELECTOR,'[aria-label="Reset viewport"]').click()
group=roots[0].find_element(By.CSS_SELECTOR,'[aria-label="Expand Traces (outgoing) for ROOT_A"]')
browser.execute_script('arguments[0].focus()',group);group.send_keys(Keys.ENTER)
wait.until(lambda d: roots[0].get_attribute('data-stv-revision')=='1')
assert len(roots[0].find_elements(By.CSS_SELECTOR,'.stv-node'))==2
assert len(roots[0].find_elements(By.CSS_SELECTOR,'.stv-edges polyline'))==1
reverse=roots[0].find_element(By.CSS_SELECTOR,'[aria-label="Expand Traced by (incoming) for LEAF_B"]')
browser.execute_script('arguments[0].focus()',reverse);reverse.send_keys(Keys.ENTER)
wait.until(lambda d:roots[0].get_attribute('data-stv-revision')=='2')
assert len(roots[0].find_elements(By.CSS_SELECTOR,'.stv-edges polyline'))==1
original=roots[0].find_element(By.CSS_SELECTOR,'.stv-summary')
before=browser.current_window_handle
browser.execute_script('arguments[0].focus()',original);original.send_keys(Keys.ENTER)
wait.until(lambda d: len(d.window_handles)==2)
browser.switch_to.window(next(handle for handle in browser.window_handles if handle!=before))
wait.until(lambda d:d.current_url.endswith('requirements.html#ROOT_A'))
wait.until(lambda d:d.find_elements(By.ID,'ROOT_A'))
assert browser.current_url.endswith('requirements.html#ROOT_A')
browser.close();browser.switch_to.window(before)
# The card background belongs to the original-object anchor; separate
# group/detail controls remain above that stretched hit region.
card=roots[0].find_element(By.CSS_SELECTOR,'[data-key*="ROOT_A"]')
ActionChains(browser).move_to_element_with_offset(card,-card.size['width']/2+3,-card.size['height']/2+3).click().perform()
wait.until(lambda d:len(d.window_handles)==2)
browser.switch_to.window(next(handle for handle in browser.window_handles if handle!=before))
wait.until(lambda d:d.current_url.endswith('requirements.html#ROOT_A'))
wait.until(lambda d:d.find_elements(By.ID,'ROOT_A'))
assert browser.current_url.endswith('requirements.html#ROOT_A')
browser.close();browser.switch_to.window(before)
artifact = os.environ.get("STV_SCREENSHOTS")
if artifact:
Path(artifact).mkdir(parents=True, exist_ok=True)
browser.save_screenshot(str(Path(artifact) / f"{protocol}-example.png"))
finally:
if server:
server.shutdown()
server.server_close()
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_031: project paging
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_project_paging(browser,short_output,protocol):
import shutil
source=short_output/'source';shutil.copytree(FIXTURE,source)
(source/'requirements.rst').write_text('Requirements\n============\n\n'+'\n'.join(f'.. shr:: Stakeholder {i}\n :id: GOAL_{i:03}\n' for i in range(201)),encoding='utf-8')
(source/'index.rst').write_text('Project\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view::\n',encoding='utf-8')
output=short_output/'html';result=build(source,output);assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url)
_assert_project_pages(browser)
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_032: async layout failure and stale completion
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_async_layout_failure_and_stale_completion(browser,short_output,protocol):
project=Path(__file__).parents[1]
script="import{build}from'esbuild';await build({entryPoints:[process.argv[1]],bundle:true,format:'iife',outfile:process.argv[2]})"
result=subprocess.run(['node','--input-type=module','-e',script,str(project/'docs/test-scripts/fixtures/browser-harness.js'),str(short_output/'harness.js')],cwd=project/'frontend',capture_output=True,text=True)
assert result.returncode==0,result.stderr
(short_output/'index.html').write_text('<link rel="stylesheet" href="'+(project/'src/sphinx_traceability_view/static/stv.css').as_uri()+'"><div class="stv-view" id="test-view" data-stv-seeds=\'["A"]\'></div><script src="harness.js"></script>',encoding='utf-8')
# Keep the host alive across all injected completions.
with published_url(short_output,protocol) as url:
browser.get(url)
_assert_async_layout(browser)
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_033: alternate model geometry details and external
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_alternate_model_geometry_details_and_external(browser,short_output,protocol):
output=short_output/'html';result=build(FIXTURE.parent/'alternate_model',output);assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,30)
wait.until(lambda d:len(d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))==3)
roots=browser.find_elements(By.CSS_SELECTOR,'.stv-view');root=roots[0]
assert browser.execute_script('''const n=arguments[0].querySelector('.stv-node').getBoundingClientRect(),v=arguments[0].querySelector('.stv-viewport').getBoundingClientRect();return n.top>=v.top&&n.bottom<=v.bottom''',root)
def group(identity,direction):
return root.find_element(By.CSS_SELECTOR,f'[aria-label="Expand Traces {"to" if direction=="outgoing" else "from"} ({direction}) for {identity}"]')
control=group('SOFTWARE_A','outgoing');browser.execute_script('arguments[0].focus()',control);control.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
control=group('GOAL_A','outgoing');browser.execute_script('arguments[0].focus()',control);control.send_keys(Keys.ENTER)
wait.until(lambda d:root.get_attribute('data-stv-revision')=='2')
boxes=browser.execute_script('''return Object.fromEntries([...arguments[0].querySelectorAll('.stv-nodes>.stv-node')].map(n=>[n.querySelector('.stv-id').textContent,{x:parseFloat(n.style.left),y:parseFloat(n.style.top),width:n.offsetWidth,height:n.offsetHeight}]))''',root)
assert len(boxes)==6
assert 'HIDDEN_A' not in boxes
for a,b in zip(['GOAL_A','HARDWARE_A','SYSTEM_A','SOFTWARE_A'],['HARDWARE_A','SYSTEM_A','SOFTWARE_A','MODULE_A']):assert boxes[a]['y']+boxes[a]['height']<boxes[b]['y']
assert boxes['CHECK_A']['x']>=max(v['x']+v['width'] for k,v in boxes.items() if k!='CHECK_A')+96
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-edges polyline'))==7
assert browser.execute_script('''const title=arguments[0].querySelector('[data-key*="GOAL_A"] .stv-title');return title.scrollHeight>title.clientHeight&&title.clientHeight<=2*parseFloat(getComputedStyle(title).lineHeight)+1''',root)
long_id=roots[1].find_element(By.CSS_SELECTOR,'[data-key*=LONG_IDENTIFIER] .stv-id')
assert long_id.get_attribute('textContent')=='LONG_IDENTIFIER_WITH_MANY_SEGMENTS_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789'
assert browser.execute_script('return arguments[0].scrollWidth<=arguments[0].clientWidth',long_id)
assert len(roots[1].find_elements(By.CSS_SELECTOR,'.stv-unaccepted'))==2
# A part seed inspects the complete published containing Need.
part=roots[2].find_element(By.CSS_SELECTOR,'[aria-label="Details for GOAL_A.fragment"]')
browser.execute_script('arguments[0].focus()',part);part.send_keys(Keys.ENTER)
wait.until(lambda d:'Static widget text' in d.find_element(By.CSS_SELECTOR,'.stv-detail').text)
panel=browser.find_element(By.CSS_SELECTOR,'.stv-detail')
metadata=panel.find_element(By.CSS_SELECTOR,'.needs_collapse')
assert metadata.get_attribute('aria-expanded')=='false'
browser.execute_script('arguments[0].click()',metadata)
assert 'Fixture owner' in panel.text
assert 'Resource unavailable offline' in panel.text
assert not panel.find_elements(By.CSS_SELECTOR,'script,iframe,object,[onerror]')
assert not browser.execute_script('return window.STV_WIDGET_EXECUTED===true')
assert panel.find_element(By.CSS_SELECTOR,'img[data-stv-src]').get_attribute('complete')=='true'
assert browser.execute_script('return arguments[0].naturalWidth',panel.find_element(By.CSS_SELECTOR,'img[data-stv-src]'))>0
assert browser.execute_script('return arguments[0].naturalWidth',panel.find_element(By.CSS_SELECTOR,'.fixture-widget img'))>0
label=panel.find_element(By.CSS_SELECTOR,'label');field=panel.find_element(By.CSS_SELECTOR,'input')
assert label.get_attribute('for')==field.get_attribute('id')
assert field.get_attribute('id').startswith(panel.get_attribute('id'))
assert 'HIDDEN_A' not in panel.text
assert not browser.execute_script("return performance.getEntriesByType('resource').some(e=>e.name.includes('example.invalid'))")
assert browser.execute_script('return arguments[0].scrollHeight>arguments[0].clientHeight',panel)
browser.execute_script('arguments[0].focus()',panel);panel.send_keys(Keys.END)
wait.until(lambda d:browser.execute_script('return arguments[0].scrollTop>0',panel))
original=panel.find_element(By.CSS_SELECTOR,'a[href*="example.invalid/remote.png"]')
assert original.text=='Original resource'
panel.find_element(By.CSS_SELECTOR,'button').click()
external=roots[1].find_element(By.CSS_SELECTOR,'[aria-label="Details for EXTERNAL_A"]')
browser.execute_script('arguments[0].focus()',external);external.send_keys(Keys.ENTER)
wait.until(lambda d:'External owner' in d.find_element(By.CSS_SELECTOR,'.stv-detail').text)
assert 'full body unavailable offline' in browser.find_element(By.CSS_SELECTOR,'.stv-detail').text
browser.find_element(By.CSS_SELECTOR,'.stv-detail button').click()
isolated=roots[1].find_element(By.CSS_SELECTOR,'.stv-unaccepted')
browser.execute_script('arguments[0].focus()',isolated.find_element(By.CSS_SELECTOR,'[data-focus="details"]'))
wait.until(lambda d:isolated.find_element(By.CSS_SELECTOR,'.stv-id').text=='ISOLATED_A')
assert isolated.find_element(By.CSS_SELECTOR,'.stv-id').text=='ISOLATED_A'
assert 'No relationships' in isolated.text
assert not isolated.find_elements(By.CSS_SELECTOR,'.stv-groups button')
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_034: empty seed catalog
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_empty_seed_catalog(browser,short_output,protocol):
source=short_output/'source';shutil.copytree(FIXTURE,source)
(source/'requirements.rst').write_text('Requirements\n============\n',encoding='utf-8')
(source/'index.rst').write_text('Project\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view::\n',encoding='utf-8')
output=short_output/'html';result=build(source,output);assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,20)
wait.until(lambda d:d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))
root=browser.find_element(By.CSS_SELECTOR,'.stv-view')
assert 'No stakeholder requirements.' in root.text
assert not root.find_elements(By.CSS_SELECTOR,'[data-focus="root"],[data-focus="Next"]')
field=root.find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
field.send_keys('anything');field.send_keys(Keys.ARROW_UP);field.send_keys(Keys.ENTER)
assert field.get_attribute('aria-invalid')=='true'
assert field.get_attribute('aria-expanded')=='false'
assert root.get_attribute('data-stv-revision')=='0'
root.find_element(By.XPATH,'.//button[text()="Project overview"]').click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-node'))==1
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_035: large project at visible capacity
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_large_project_at_visible_capacity(browser,protocol):
output=Path(os.environ.get('STV_CAPACITY_OUTPUT','docs/_build/benchmark-v2/html')).resolve()
assert (output/'index.html').is_file(),'Run scripts/benchmark.py before capacity verification'
runs=[]
browser.set_script_timeout(35)
temporary=tempfile.TemporaryDirectory(prefix='stv-cap-') if protocol=='file' else None
if temporary:
assert Path(temporary.name).resolve().is_relative_to(Path(tempfile.gettempdir()).resolve())
# Full hash names need a short physical output root in Windows Firefox.
short=Path(temporary.name)/'html';shutil.copytree(output,short);output=short
try:
_measure_capacity(browser,output,protocol,runs)
finally:
if temporary:
assert Path(temporary.name).resolve().is_relative_to(Path(tempfile.gettempdir()).resolve())
temporary.cleanup()
result={'browser':browser.capabilities['browserVersion'],'geckodriver':browser.capabilities.get('moz:geckodriverVersion'),
'memory_scope':'Sum of working sets of the isolated Firefox process tree, including content/GPU helpers; shared pages may be counted more than once',
'runs':runs,'startup_median_ms':statistics.median(r['startup_ms'] for r in runs),
'startup_max_ms':max(r['startup_ms'] for r in runs),
'expansion_median_ms':statistics.median(v for r in runs for v in r['expansion_ms']),
'expansion_max_ms':max(v for r in runs for v in r['expansion_ms'])}
target=Path(os.environ.get('STV_CAPACITY_METRICS','docs/_build/capacity-browser.json'));target.parent.mkdir(parents=True,exist_ok=True)
aggregate=json.loads(target.read_text(encoding='utf-8')) if target.exists() else {}
aggregate[protocol]=result
target.write_text(json.dumps(aggregate,indent=2),encoding='utf-8')
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_036: seed layout replacement failure and staleness
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_seed_layout_replacement_failure_and_staleness(browser,short_output,protocol):
from pathlib import Path
project=Path(__file__).parents[1]
script="import{build}from'esbuild';await build({entryPoints:[process.argv[1]],bundle:true,format:'iife',outfile:process.argv[2]})"
result=subprocess.run(['node','--input-type=module','-e',script,str(project/'docs/test-scripts/fixtures/browser-harness.js'),str(short_output/'harness.js')],cwd=project/'frontend',capture_output=True,text=True)
assert result.returncode==0,result.stderr
(short_output/'index.html').write_text('<link rel="stylesheet" href="'+(project/'src/sphinx_traceability_view/static/stv.css').as_uri()+'"><div class="stv-view" id="test-view" data-stv-seeds=\'["A"]\'></div><script src="harness.js"></script>',encoding='utf-8')
with published_url(short_output,protocol) as url:
browser.get(url);_assert_async_layout(browser)
wait=WebDriverWait(browser,20);root=browser.find_element(By.CSS_SELECTOR,'.stv-view')
field=root.find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
previous=root.find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style')
field.send_keys('B');field.send_keys(Keys.ENTER)
wait.until(lambda d:d.execute_script('return calls.length')==7)
browser.execute_script('calls[6].reject(Error("Seed layout failure"))')
wait.until(lambda d:'Seed layout failure' in root.find_element(By.CSS_SELECTOR,'.stv-status').text)
assert root.get_attribute('data-stv-revision')=='2'
assert root.find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style')==previous
assert browser.switch_to.active_element==field
for identity,count in [('A',8),('B',9)]:
field.send_keys(Keys.CONTROL,'a');field.send_keys(Keys.BACKSPACE);field.send_keys(identity);field.send_keys(Keys.ENTER)
wait.until(lambda d:d.execute_script('return calls.length')==count)
browser.execute_script('stvHarness.compute(...calls[8].args).then(calls[8].resolve)')
wait.until(lambda d:root.get_attribute('data-stv-revision')=='3')
browser.execute_script('stvHarness.compute(...calls[7].args).then(calls[7].resolve)')
wait.until(lambda d:d.execute_script('return document.querySelectorAll(".stv-measure").length')==0)
assert root.get_attribute('data-stv-revision')=='3'
assert [n.text for n in root.find_elements(By.CSS_SELECTOR,'.stv-id')]==['B']
assert browser.switch_to.active_element==field
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_037: offline exploration
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_offline_exploration(browser, short_output, protocol):
tmp_path = short_output
output = tmp_path / "nested" / "documentation"
result = build(FIXTURE, output)
assert result.returncode == 0, result.stderr
server = None
try:
if protocol == "http":
server = ThreadingHTTPServer(("127.0.0.1", 0), partial(SimpleHTTPRequestHandler, directory=str(tmp_path)))
threading.Thread(target=server.serve_forever, daemon=True).start()
url = f"http://127.0.0.1:{server.server_port}/nested/documentation/index.html"
else:
url = (output / "index.html").as_uri()
browser.get(url)
wait = WebDriverWait(browser, 20)
wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, '.stv-view[data-stv-revision="0"]')) == 2)
roots = browser.find_elements(By.CSS_SELECTOR, ".stv-view")
assert len(roots[0].find_elements(By.CSS_SELECTOR, ".stv-node")) == 2
assert len(roots[1].find_elements(By.CSS_SELECTOR, ".stv-node")) == 2
root_button = roots[1].find_element(By.CSS_SELECTOR, '[data-focus="root"]')
browser.execute_script("arguments[0].focus()", root_button)
root_button.send_keys(Keys.ENTER)
wait.until(lambda d: roots[1].get_attribute("data-stv-revision") == "1")
assert len(roots[1].find_elements(By.CSS_SELECTOR, ".stv-node")) == 1
assert roots[0].get_attribute("data-stv-revision") == "0"
inspect = roots[0].find_element(By.CSS_SELECTOR, '[aria-label="Details for ROOT_A"]')
browser.execute_script("arguments[0].focus()", inspect)
inspect.send_keys(Keys.ENTER)
try:
wait.until(lambda d: "formatted body" in d.find_element(By.CSS_SELECTOR, ".stv-detail").text)
except Exception:
print(browser.find_element(By.CSS_SELECTOR, ".stv-detail").get_attribute("outerHTML"))
raise
assert browser.find_elements(By.CSS_SELECTOR, ".stv-detail table")
browser.switch_to.active_element.send_keys(Keys.ESCAPE)
assert browser.find_elements(By.CSS_SELECTOR,'.stv-persistent-detail')
browser.find_element(By.CSS_SELECTOR,'.stv-persistent-detail button').send_keys(Keys.ENTER)
wait.until(lambda d: not d.find_elements(By.CSS_SELECTOR, ".stv-detail"))
assert browser.switch_to.active_element == inspect
assert roots[0].find_element(By.CSS_SELECTOR, '.stv-node.stv-unaccepted .stv-id').text == 'LEAF_B'
assert roots[0].find_element(By.CSS_SELECTOR, '.stv-node:not(.stv-unaccepted) .stv-id').text == 'ROOT_A'
# Pointer hover presents the same final host fragment; pointer travel
# into the interactive panel keeps it open beyond the dismissal grace.
ActionChains(browser).move_to_element(inspect).perform()
wait.until(lambda d: d.find_elements(By.CSS_SELECTOR, '.stv-detail'))
panel=browser.find_element(By.CSS_SELECTOR,'.stv-detail')
ActionChains(browser).move_to_element(panel).perform()
wait.until(lambda d: 'formatted body' in panel.text)
assert browser.execute_script('return arguments[0].getBoundingClientRect().right <= innerWidth',panel)
assert browser.execute_script('return arguments[0].getBoundingClientRect().bottom <= innerHeight',panel)
panel.find_element(By.CSS_SELECTOR,'button').click()
# View-local transforms are shared by edge and node layers.
other_transform=roots[1].find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style')
for label in ['Zoom in','Pan right','Pan down']:
control=roots[0].find_element(By.CSS_SELECTOR,f'[aria-label="{label}"]')
browser.execute_script('arguments[0].focus()',control);control.send_keys(Keys.ENTER)
assert roots[1].find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style') == other_transform
roots[0].find_element(By.CSS_SELECTOR,'[aria-label="Reset viewport"]').click()
viewport=roots[0].find_element(By.CSS_SELECTOR,'.stv-viewport')
scene=roots[0].find_element(By.CSS_SELECTOR,'.stv-scene')
before_drag=scene.get_attribute('style')
x,y=browser.execute_script('const r=arguments[0].getBoundingClientRect();return [Math.min(r.right,innerWidth)-20,Math.min(r.bottom,innerHeight)-20]',viewport)
drag=ActionChains(browser)
drag.w3c_actions.pointer_action.move_to_location(x,y)
drag.click_and_hold().move_by_offset(-80,-60).release().perform()
wait.until(lambda d:scene.get_attribute('style')!=before_drag)
before_wheel=scene.get_attribute('style')
ActionChains(browser).scroll_from_origin(ScrollOrigin.from_element(viewport),0,-120).perform()
wait.until(lambda d:scene.get_attribute('style')!=before_wheel)
assert len(browser.window_handles)==1
assert roots[1].find_element(By.CSS_SELECTOR,'.stv-scene').get_attribute('style') == other_transform
roots[0].find_element(By.CSS_SELECTOR,'[aria-label="Reset viewport"]').click()
group=roots[0].find_element(By.CSS_SELECTOR,'[aria-label="Expand Traces (outgoing) for ROOT_A"]')
browser.execute_script('arguments[0].focus()',group);group.send_keys(Keys.ENTER)
wait.until(lambda d: roots[0].get_attribute('data-stv-revision')=='1')
assert len(roots[0].find_elements(By.CSS_SELECTOR,'.stv-node'))==2
assert len(roots[0].find_elements(By.CSS_SELECTOR,'.stv-edges polyline'))==1
reverse=roots[0].find_element(By.CSS_SELECTOR,'[aria-label="Expand Traced by (incoming) for LEAF_B"]')
browser.execute_script('arguments[0].focus()',reverse);reverse.send_keys(Keys.ENTER)
wait.until(lambda d:roots[0].get_attribute('data-stv-revision')=='2')
assert len(roots[0].find_elements(By.CSS_SELECTOR,'.stv-edges polyline'))==1
original=roots[0].find_element(By.CSS_SELECTOR,'.stv-summary')
before=browser.current_window_handle
browser.execute_script('arguments[0].focus()',original);original.send_keys(Keys.ENTER)
wait.until(lambda d: len(d.window_handles)==2)
browser.switch_to.window(next(handle for handle in browser.window_handles if handle!=before))
wait.until(lambda d:d.current_url.endswith('requirements.html#ROOT_A'))
wait.until(lambda d:d.find_elements(By.ID,'ROOT_A'))
assert browser.current_url.endswith('requirements.html#ROOT_A')
browser.close();browser.switch_to.window(before)
# The card background belongs to the original-object anchor; separate
# group/detail controls remain above that stretched hit region.
card=roots[0].find_element(By.CSS_SELECTOR,'[data-key*="ROOT_A"]')
ActionChains(browser).move_to_element_with_offset(card,-card.size['width']/2+3,-card.size['height']/2+3).click().perform()
wait.until(lambda d:len(d.window_handles)==2)
browser.switch_to.window(next(handle for handle in browser.window_handles if handle!=before))
wait.until(lambda d:d.current_url.endswith('requirements.html#ROOT_A'))
wait.until(lambda d:d.find_elements(By.ID,'ROOT_A'))
assert browser.current_url.endswith('requirements.html#ROOT_A')
browser.close();browser.switch_to.window(before)
artifact = os.environ.get("STV_SCREENSHOTS")
if artifact:
Path(artifact).mkdir(parents=True, exist_ok=True)
browser.save_screenshot(str(Path(artifact) / f"{protocol}-example.png"))
finally:
if server:
server.shutdown()
server.server_close()
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_038: clean wheel host
Level: System packaging integration. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. PRSA-003 refinement: Patch offline bootstrap tooling before installation. Prior design acceptance is preserved in historical reviews; the refined procedure is owner-accepted under Round 14: Owner acceptance for v1.0.0. def test_clean_wheel_host():
wheels=[Path(os.environ['STV_WHEEL'])] if os.environ.get('STV_WHEEL') else sorted((ROOT/'dist').glob('*.whl'))
assert wheels,'Build the wheel before packaging verification'
wheel=wheels[-1]
wheelhouse=ROOT/'docs/_build/wheelhouse'
assert wheelhouse.is_dir(),'Provision the runtime wheelhouse before offline packaging verification'
with zipfile.ZipFile(wheel) as archive:
names=archive.namelist()
assert not any("/tests/" in name for name in names), "Unit tests must not ship in the runtime wheel"
for asset in ('stv.js','stv.css','LICENSE.txt','THIRD_PARTY_NOTICES.txt','vendor/elk.bundled.js','vendor/ELK-SOURCE.txt','vendor/ELK-INVENTORY.json'):
assert f'sphinx_traceability_view/static/{asset}' in names
packaged_inventory=json.loads(archive.read('sphinx_traceability_view/static/vendor/ELK-INVENTORY.json'))
assert hashlib.sha256(archive.read('sphinx_traceability_view/static/vendor/elk.bundled.js')).hexdigest()==packaged_inventory['elkjs']['sha256']
metadata=archive.read(next(name for name in names if name.endswith('.dist-info/METADATA'))).decode()
assert not any('Requires-Dist: '+name in metadata for name in ('pytest','selenium','esbuild'))
assert 'License-Expression: MIT AND ISC AND BSD-3-Clause AND Apache-2.0 AND EPL-1.0 AND EPL-2.0' in metadata
assert 'Copyright (c) 2026 Henning Heikes' in archive.read('sphinx_traceability_view/static/LICENSE.txt').decode()
license_members=[name for name in names if '.dist-info/licenses/' in name]
assert any(name.endswith('/LICENSE') for name in license_members)
assert any(name.endswith('/THIRD_PARTY_NOTICES.txt') for name in license_members)
assert any(name.endswith('/ELK-SOURCE.txt') for name in license_members)
source_archive=wheel.parent/(wheel.name.split('-py3-')[0]+'.tar.gz')
with tarfile.open(source_archive,'r:gz') as archive:
names=archive.getnames()
assert any(name.endswith('/LICENSE') for name in names)
assert any(name.endswith('/static/THIRD_PARTY_NOTICES.txt') for name in names)
assert any(name.endswith('/static/vendor/ELK-SOURCE.txt') for name in names)
with tempfile.TemporaryDirectory(prefix='stv-wheel-') as directory:
root=Path(directory);envpath=root/'env'
assert root.resolve().is_relative_to(Path(tempfile.gettempdir()).resolve())
subprocess.run([sys.executable,'-m','venv',str(envpath)],check=True,capture_output=True)
python=envpath/'Scripts/python.exe'
bootstrap=subprocess.run([str(python),'-m','pip','install','--no-index','--find-links',str(wheelhouse),'-r',str(ROOT/'requirements-tooling.txt')],capture_output=True,text=True)
assert bootstrap.returncode==0,bootstrap.stdout+bootstrap.stderr
result=subprocess.run([str(python),'-m','pip','install','--no-index','--find-links',str(wheelhouse),str(wheel)],capture_output=True,text=True)
assert result.returncode==0,result.stdout+result.stderr
source=root/'source';shutil.copytree(ROOT/'docs/test-scripts/fixtures/minimal',source)
env=dict(os.environ);env.pop('PYTHONPATH',None)
env['PATH']=str(envpath/'Scripts')+os.pathsep+str(Path(os.environ['SystemRoot'])/'System32')
result=subprocess.run([str(python),'-m','sphinx','-b','html','-W',str(source),str(root/'html')],env=env,cwd=root,capture_output=True,text=True)
assert result.returncode==0,result.stdout+result.stderr
assert 'STV_PENDING' not in (root/'html/index.html').read_text(encoding='utf-8')
static=root/'html/_static/stv'
assert (static/'LICENSE.txt').read_bytes()==(ROOT/'LICENSE').read_bytes()
assert 'https://github.com/kieler/elkjs/tree/0.10.0' in (static/'vendor/ELK-SOURCE.txt').read_text(encoding='utf-8')
notices=(static/'THIRD_PARTY_NOTICES.txt').read_text(encoding='utf-8')
assert 'Eclipse Public License' in notices
assert 'Apache License' in notices
assert 'EMF-GWT common/ecore' in notices
inventory=json.loads((static/'vendor/ELK-INVENTORY.json').read_text())
assert inventory==packaged_inventory
assert {x['license'] for x in inventory['compiled_dependencies']} >= {'Apache-2.0','EPL-1.0','EPL-2.0','MIT'}
assert 'https://github.com/eclipse-elk/elk/tree/v0.10.0' in notices
probe=subprocess.run([str(python),'-c','import sphinx_traceability_view as s;from importlib.metadata import version;assert s.__version__==version("sphinx-traceability-view");print(s.__file__);print(s.__version__)'],env=env,cwd=root,capture_output=True,text=True,check=True)
assert str(envpath).lower() in probe.stdout.lower()
retained=os.environ.get('STV_CONSUMER_RECORD')
if retained:
destination=Path(retained);destination.mkdir(parents=True,exist_ok=True)
(destination/'build.log').write_text(result.stdout+result.stderr,encoding='utf-8')
frozen=subprocess.run([str(python),'-m','pip','freeze'],capture_output=True,text=True,check=True)
(destination/'environment.txt').write_text(frozen.stdout,encoding='utf-8')
(destination/'identity.txt').write_text(probe.stdout+'\nPATH='+env['PATH']+'\n',encoding='utf-8')
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_039: empty seed catalog
Level: System/browser acceptance. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_empty_seed_catalog(browser,short_output,protocol):
source=short_output/'source';shutil.copytree(FIXTURE,source)
(source/'requirements.rst').write_text('Requirements\n============\n',encoding='utf-8')
(source/'index.rst').write_text('Project\n=======\n\n.. toctree::\n\n requirements\n\n.. traceability-view::\n',encoding='utf-8')
output=short_output/'html';result=build(source,output);assert result.returncode==0,result.stderr
with published_url(output,protocol) as url:
browser.get(url);wait=WebDriverWait(browser,20)
wait.until(lambda d:d.find_elements(By.CSS_SELECTOR,'.stv-view[data-stv-revision="0"]'))
root=browser.find_element(By.CSS_SELECTOR,'.stv-view')
assert 'No stakeholder requirements.' in root.text
assert not root.find_elements(By.CSS_SELECTOR,'[data-focus="root"],[data-focus="Next"]')
field=root.find_element(By.CSS_SELECTOR,'.stv-seed-picker input')
field.send_keys('anything');field.send_keys(Keys.ARROW_UP);field.send_keys(Keys.ENTER)
assert field.get_attribute('aria-invalid')=='true'
assert field.get_attribute('aria-expanded')=='false'
assert root.get_attribute('data-stv-revision')=='0'
root.find_element(By.XPATH,'.//button[text()="Project overview"]').click()
wait.until(lambda d:root.get_attribute('data-stv-revision')=='1')
assert len(root.find_elements(By.CSS_SELECTOR,'.stv-node'))==1
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_040: unsupported builder fails clearly
Level: System integration through the public Sphinx build interface. Preconditions and inputs: Use the pinned implementation environment, fixture setup and parameter variant identified by the executable selector. Browser cases require provisioned Firefox/geckodriver and both file/HTTP variants where parameterized. Capacity cases require a freshly generated 30,000-Need/90,000-edge fixture; packaging requires the candidate wheel and offline wheelhouse. The procedure below specifies test-specific inputs. Executable selector: Invocation: Procedure and expected outcomes: Execute the following existing test procedure with its fixture helpers. Every assertion is a required observable outcome; expected error cases must assert the specified diagnostic/state. Links identify criterion portions exercised by this case, not a claim that this individual case alone verifies every linked requirement in full. Review the full case set and the release criterion assessment together. def test_unsupported_builder_fails_clearly(tmp_path):
result=build(FIXTURE,tmp_path,'-b','text')
assert result.returncode!=0
assert 'supports only the html builder' in result.stderr
Evidence to retain: Per-case execution result and timestamp, candidate and test identities, environment, invocation and linked JUnit/log evidence. |
STV_TC_SY_041: Select separate occurrences
Level/environment: Planned browser acceptance, Windows/Firefox, file and nested static HTTP output, network blocked. Preconditions/inputs: Future occurrence-mode implementation; two parents sharing one child with two disjoint nonempty relationship groups. Procedure: Open a two-parent graph sharing one child. Select separate-occurrence mode, then shared mode. Expected outcomes: Separate mode shows two child occurrences; shared mode shows one. The selector and visible mode agree. Repeat with a cycle and a missing target to verify termination and visible placeholder identity. Start at 199 visible occurrences and request two more: the transition must be rejected atomically with the node-limit explanation. A transition to exactly 200 succeeds. Attempt a mode switch producing 201: retain mode, graph and exploration state and explain the limit. Execution/evidence: No executable implementation exists in v0.2.0. Retain per-case results, candidate identity and file/HTTP screenshots when the future release implements this scope; no execution record is invented. |
STV_TC_SY_042: Restore occurrence exploration
Level/environment: Planned browser acceptance, Windows/Firefox, file and nested static HTTP output, network blocked. Preconditions/inputs: Future occurrence-mode implementation; two parents sharing one child with two disjoint nonempty relationship groups. Procedure: In separate mode expand different groups in each occurrence. Switch to shared mode and back. Expected outcomes: Each occurrence recovers its own expanded and collapsed groups without losing prior exploration. Repeat with a cycle and a missing target to verify termination and visible placeholder identity. Start at 199 visible occurrences and request two more: the transition must be rejected atomically with the node-limit explanation. A transition to exactly 200 succeeds. Attempt a mode switch producing 201: retain mode, graph and exploration state and explain the limit. Execution/evidence: No executable implementation exists in v0.2.0. Retain per-case results, candidate identity and file/HTTP screenshots when the future release implements this scope; no execution record is invented. |
STV_TC_SY_043: Isolate occurrence expansion
Level/environment: Planned browser acceptance, Windows/Firefox, file and nested static HTTP output, network blocked. Preconditions/inputs: Future occurrence-mode implementation; two parents sharing one child with two disjoint nonempty relationship groups. Procedure: In separate mode show two occurrences of one child through different parents. Expand a group on only the first. Expected outcomes: Only the first occurrence changes; the other retains its original graph and expansion state. Repeat with a cycle and a missing target to verify termination and visible placeholder identity. Start at 199 visible occurrences and request two more: the transition must be rejected atomically with the node-limit explanation. A transition to exactly 200 succeeds. Attempt a mode switch producing 201: retain mode, graph and exploration state and explain the limit. Execution/evidence: No executable implementation exists in v0.2.0. Retain per-case results, candidate identity and file/HTTP screenshots when the future release implements this scope; no execution record is invented. |
STV_TC_SY_045: Inspect details from a nested Windows checkout
Level/environment: Windows browser system acceptance; stable Firefox with recorded version, direct file URLs and nested static HTTP hosting. Executable selector: Preconditions/inputs: The fixture computes a nested output root where old two-hash asset paths exceed 260 characters and compact paths fit. Retain the actual measured lengths and the failed pre-fix attempt. Root and nested view pages display ROOT_A’s formatted body, table and source links. Procedure: Build the host using the actual implementation. Open both pages over file and HTTP, activate Details with keyboard, compare rich content and links, then reload to clear fragment memory. Temporarily rename the detail asset, activate Details by pointer and require the unavailable error. Restore the asset, dismiss/reopen and require successful recovery. Expected outcomes: All four page/transport combinations display the correct body and retry successfully. The old path exceeds 260 characters, compact assets exist and fit below that threshold. The companion compact asset software case verifies full digest identity and deduplication. Evidence: Per-case JUnit/log, before/after path observations and rendered screenshots in the maintenance execution record. No arbitrary-depth path, other browser or other-machine support is inferred. |
STV_TC_SY_046: Reject unaudited distribution inputs
Level: System build/distribution interface. Executable selector: Preconditions: Pinned Node/esbuild installation and maintained legal inventory; use an isolated copy for dependency/legal inputs. Procedure and expected outcomes: Invoke the real frontend build against a changed ELK version, changed binary, modified legal text and missing EPL notice. Each must fail with its specific audit/input diagnostic before distributing browser output. Preserve installed dependencies unchanged. Evidence: Per-case JUnit and command log. The clean installed-wheel case separately verifies the successful shipping path and notice inventory. |
STV_TC_SY_049: Retained historical archives carry a complete licensing supplement
Level: Repository/distribution integration. Preconditions: Retained project Git objects and supplement for the historical case; patched offline runtime wheelhouse, development artifact and existing Python 3.10 executable for runtime compatibility. Executable selector: Invocation: Run the indicated script with the implementation Python;
the runtime script additionally takes Procedure and expected outcomes: Check every available historical Git archive and retained canonical archive. Require the audited upstream ELK fingerprint and byte-identical complete supplement materials; retain immutable artifact hashes. Evidence: Actual result in |
STV_TC_SY_050: Patched offline distribution remains usable on Python 3.10
Level: Repository/distribution integration. Preconditions: Retained project Git objects and supplement for the historical case; patched offline runtime wheelhouse, development artifact and existing Python 3.10 executable for runtime compatibility. Executable selector: Invocation: Run the indicated script with the implementation Python;
the runtime script additionally takes Procedure and expected outcomes: Create a Python 3.10 environment, install patched bootstrap tools and the development wheel from the offline wheelhouse, run pip check, build the minimal Sphinx host, and verify installed module identity and copied inventory. Evidence: Actual result in |
STV_TC_SY_051: Actual source archive respects the distribution boundary
Level: Packaging integration. Preconditions and inputs: Existing implementation environment and frontend/legal assets. The test copies build inputs to an isolated source tree and seeds local-only evidence/cache/tool files, including execution logs subject to root and nested ignore negations. Executable selector: Invocation: Procedure and expected outcomes: Build the actual source archive and its wheel using the normal build frontend. Inspect tar members and content; require no seeded evidence or local-only sentinel bytes, and require source, legal and build inputs. Compare rebuilt wheel browser assets and notices byte-for-byte with their source archive members. The separate existing installed-wheel case verifies an isolated offline consumer. Evidence: Retain JUnit/log results, source identity, artifact hashes and member inventory in PRSA-004 source archive boundary verification. The initial old-configuration failure is historical; the latest outcome is independent of owner acceptance and release readiness. |
STV_TC_SY_052: Clean rebuild retires obsolete publication files
Level: Maintainer/Sphinx publication integration. Preconditions and inputs: Existing implementation environment and local extension source. Use isolated temporary Sphinx source/build directories; no installed public output or external service is modified by the case. Executable selector: Invocation: Procedure and expected outcomes: Build the real Sphinx fixture with two Needs, a retired page and a download. Remove one Need/page/download reference and seed an obsolete output file. Rebuild; require one current graph, no old graph/page/download, no public doctree cache, and a complete byte-verified manifest. Previous output must remain outside html. Evidence: Retain actual JUnit/log outcomes, source hashes and relative file inventory in PRSA-005 clean documentation publication verification. Design acceptance is recorded in Round 14: Owner acceptance for v1.0.0; complete release qualification remains separate from this individual case. |
STV_TC_SY_053: Failed documentation build preserves published bytes
Level: Maintainer/Sphinx publication integration. Preconditions and inputs: Existing implementation environment and local extension source. Use isolated temporary Sphinx source/build directories; no installed public output or external service is modified by the case. Executable selector: Invocation: Procedure and expected outcomes: Build successfully and snapshot every publication file. Introduce an unknown Sphinx directive. Require a nonzero build result and an identical published file/hash snapshot. Evidence: Retain actual JUnit/log outcomes, source hashes and relative file inventory in PRSA-005 clean documentation publication verification. Design acceptance is recorded in Round 14: Owner acceptance for v1.0.0; complete release qualification remains separate from this individual case. |
STV_TC_SY_054: Failed directory promotion restores previous publication
Level: Maintainer/Sphinx publication integration. Preconditions and inputs: Existing implementation environment and local extension source. Use isolated temporary Sphinx source/build directories; no installed public output or external service is modified by the case. Executable selector: Invocation: Procedure and expected outcomes: Build successfully and snapshot all published files. Inject an OSError only for the fresh-stage-to-html rename after the prior output is moved. Require the failure to propagate and every previous publication file/hash to be restored. Evidence: Retain actual JUnit/log outcomes, source hashes and relative file inventory in PRSA-005 clean documentation publication verification. Design acceptance is recorded in Round 14: Owner acceptance for v1.0.0; complete release qualification remains separate from this individual case. |
STV_TC_SY_055: CI policy rejects privilege and provenance drift
Level: CI/distribution integration. Preconditions and inputs: Maintained implementation environment for workflow/checksum tests, real GitHub workflow files, Git and GNU sha256sum. The runtime case requires the development wheel installed on Python 3.10. No hosted workflow or attestation service is used in these local checks. Executable selector: Invocation: Run pytest for Procedure and expected outcomes: Load both real workflow files, require the reviewed least-privilege policy, and mutate copies to request write tokens, unsafe triggers, mutable action tags, privileged repository code, missing attestation jobs, altered subjects or an unrestricted attestation condition. Every modified variant must be rejected. Evidence: Retain actual reports, commands and source/artifact identities in PRSA-006 CI and provenance preparation. Case design review is pending; local verification does not prove a hosted run or attestation occurred. |
STV_TC_SY_056: Candidate checksums detect changed artifact bytes
Level: CI/distribution integration. Preconditions and inputs: Maintained implementation environment for workflow/checksum tests, real GitHub workflow files, Git and GNU sha256sum. The runtime case requires the development wheel installed on Python 3.10. No hosted workflow or attestation service is used in these local checks. Executable selector: Invocation: Run pytest for Procedure and expected outcomes: Generate checksums and source metadata for a wheel/source pair. Verify both with GNU sha256sum, alter wheel bytes and require verification failure. An extra wheel must make checksum generation fail rather than omit an ambiguous subject. Evidence: Retain actual reports, commands and source/artifact identities in PRSA-006 CI and provenance preparation. Case design review is pending; local verification does not prove a hosted run or attestation occurred. |
STV_TC_SY_057: CI runtime command imports the installed Python 3.10 package
Level: CI/distribution integration. Preconditions and inputs: Maintained implementation environment for workflow/checksum tests, real GitHub workflow files, Git and GNU sha256sum. The runtime case requires the development wheel installed on Python 3.10. No hosted workflow or attestation service is used in these local checks. Executable selector: Invocation: Run pytest for Procedure and expected outcomes: Install the newly built development wheel into a Python 3.10 host, execute the CI runtime subcommand, require an installed-package import outside the checkout, matching installed version, a successful pip check and warnings-as-errors Sphinx fixture build. Evidence: Retain actual reports, commands and source/artifact identities in PRSA-006 CI and provenance preparation. Case design review is pending; local verification does not prove a hosted run or attestation occurred. |
STV_TC_SY_059: Negative route rendering and viewport reset
Level/environment: System browser integration: Windows, Firefox/geckodriver, both file and loopback HTTP, external network blocked. Preconditions: Rebuilt local browser asset and isolated fixtures. Current development identity is retained separately from historical PoC evidence. Selector/invocation: Inputs/procedure: Expand downward P to five children (negative x), and upward A to P then R with a wrapping relation label (negative y). Read final SVG route points, viewBox and screen CTM. Record retained node positions. Activate Reset viewport and inspect fitted node boxes; repeat on file and HTTP. Expected: At least one negative point in each configured direction fixture, every route point inside the padded viewBox, and first/last route endpoints on node boundaries within 2 screen px. Surviving nodes do not move on toggles. Reset fits all node boxes inside the viewport without changing world positions; markers and paths remain visible. Pan/zoom and focus have existing adjacent cases. |
STV_TC_SY_060: Natural seeded order survives expansion
Level/environment: System browser integration: Windows, Firefox/geckodriver, both file and loopback HTTP, external network blocked. Preconditions: Rebuilt local browser asset and isolated fixtures. Current development identity is retained separately from historical PoC evidence. Selector/invocation: Inputs/procedure: Build shuffled REQ_20, REQ_10, REQ_3, REQ_2, REQ_1 seeds, read actual DOM/world x ordering and expand REQ_2 to CHILD_A. Expected: Initial left-to-right IDs are REQ_1, REQ_2, REQ_3, REQ_10, REQ_20; all existing world/screen coordinates remain within 0.5 px after expansion. Project-mode ordering is independently covered by STV_TC_SW_045. |