from __future__ import annotations import json import subprocess from pathlib import Path FRONTEND = Path(__file__).resolve().parents[2] / "apps/web/vue3/index.html" def _frontend_text() -> str: return FRONTEND.read_text(encoding="utf-8") def test_gwas_dialog_uses_species_prefix_copy_and_batch_binding() -> None: frontend = _frontend_text() assert "物种简称(用于结果前缀)" in frontend assert 'placeholder="At / Os / Zm"' in frontend assert 'v-model="batchExecuteParams.prefix"' in frontend assert 'v-if="!isBatchExecuting && batchIncludesGwasStages()"' in frontend def test_gwas_template_helpers_are_exposed_from_vue_setup() -> None: frontend = _frontend_text() setup_return_start = frontend.rindex(" return {") setup_return_end = frontend.index(" };\n }\n });", setup_return_start) setup_return = frontend[setup_return_start:setup_return_end] assert " isGwasStage," in setup_return assert " batchIncludesGwasStages," in setup_return def test_gwas_prefix_helpers_require_and_lock_one_batch_value(tmp_path: Path) -> None: frontend = _frontend_text() start = frontend.index("const gwasStageIds =") end = frontend.index("const uniqueSpeciesList =", start) helper_source = frontend[start:end] node_script = tmp_path / "gwas-prefix-contract.cjs" node_script.write_text( "const batchExecuteParams={value:{prefix:'Os',params:{prefix:'Os'},plan:{params:{prefix:'Os'}}}};" "const batchExecuteStages={value:[" "{id:'gwas_fmt',params:{prefix:'Os'}}," "{id:'gwas_trait',params:{}}," "{id:'rnaseq_qc',params:{prefix:'RNA'}}]};" "const confirmParams={prefix:'Os'};" "const getStageId=(stage)=>typeof stage==='object'?stage.id:stage;" "const prefixlessStages=new Set(); const isGeneFamilyScopeStage=()=>false;" "const geneFamilyAutoPrefix=()=>''; const params={prefix:''};" "const normalizedPrefix=(value)=>String(value||'').trim()||'At';" + helper_source + "const blankMissing=gwasPrefixMissingFor({stage:'gwas_fmt',prefix:' '});" + "const locked=lockGwasBatchPrefix(' cohort ');" + "const lockedBatch=JSON.parse(JSON.stringify(batchExecuteParams.value));" + "const lockedStages=JSON.parse(JSON.stringify(batchExecuteStages.value));" + "const lockedConfirm=confirmParams.prefix;" + "batchExecuteParams.value={}; confirmParams.prefix='';" + "const blankExecutionPrefix=executionPrefix('gwas_fmt',{});" + "process.stdout.write(JSON.stringify({blankMissing,locked,blankExecutionPrefix,batch:lockedBatch,stages:lockedStages,confirm:lockedConfirm}));", encoding="utf-8", ) completed = subprocess.run(["node", str(node_script)], text=True, capture_output=True, check=False) assert completed.returncode == 0, completed.stderr payload = json.loads(completed.stdout) assert payload["blankMissing"] is True assert payload["locked"] == "cohort" assert payload["blankExecutionPrefix"] == "" assert payload["batch"]["prefix"] == "cohort" assert payload["batch"]["params"]["prefix"] == "cohort" assert payload["batch"]["plan"]["params"]["prefix"] == "cohort" assert payload["stages"][0]["params"]["prefix"] == "cohort" assert payload["stages"][1]["params"]["prefix"] == "cohort" assert payload["stages"][2]["params"]["prefix"] == "RNA" assert payload["confirm"] == "cohort" def test_gwas_submission_validates_before_closing_dialog_and_disables_auto_start() -> None: frontend = _frontend_text() batch_start = frontend.index("const startBatchExecute = async () =>") batch_end = frontend.index("const executeStagesWithDependencies =", batch_start) batch_source = frontend[batch_start:batch_end] direct_start = frontend.index("const doExecute = async () =>") direct_end = frontend.index("const queueAutofillFromPayload = async", direct_start) direct_source = frontend[direct_start:direct_end] batch_timer_start = frontend.index("const startBatchExecuteTimer = () =>") batch_timer_end = frontend.index("const clearBatchExecuteTimer =", batch_timer_start) batch_timer_source = frontend[batch_timer_start:batch_timer_end] assert batch_source.index("gwasPrefixMissingFor") < batch_source.index("batchExecuteDialogVisible.value = false") assert batch_source.index("lockGwasBatchPrefix") < batch_source.index("batchExecuteDialogVisible.value = false") assert direct_source.index("gwasPrefixMissingFor") < direct_source.index("confirmDialogVisible.value = false") assert "batchIncludesGwasStages()" in batch_timer_source assert "batchExecuteCountdown.value = 0" in batch_timer_source