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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions tests/test_check_prerequisites_paths_only.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
CHECK_PREREQS_PS = PROJECT_ROOT / "scripts" / "powershell" / "check-prerequisites.ps1"

HAS_PWSH = shutil.which("pwsh") is not None
_POWERSHELL = shutil.which("powershell.exe") or shutil.which("powershell")
_WINDOWS_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None


def _install_bash_scripts(repo: Path) -> None:
Expand Down Expand Up @@ -160,14 +160,14 @@ def test_normal_mode_still_validates_branch(prereq_repo: Path) -> None:
# ── PowerShell tests ──────────────────────────────────────────────────────


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_ps_paths_only_succeeds_on_non_spec_branch(prereq_repo: Path) -> None:
"""-PathsOnly must return paths when feature.json pins the feature dir."""
feat = prereq_repo / "specs" / "001-my-feature"
feat.mkdir(parents=True, exist_ok=True)
_write_feature_json(prereq_repo)
script = prereq_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL
result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json", "-PathsOnly"],
cwd=prereq_repo,
Expand All @@ -183,7 +183,7 @@ def test_ps_paths_only_succeeds_on_non_spec_branch(prereq_repo: Path) -> None:
assert "FEATURE_DIR" in data


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_ps_paths_only_succeeds_on_spec_branch(prereq_repo: Path) -> None:
"""-PathsOnly must also work when feature.json and SPECIFY_FEATURE agree."""
subprocess.run(
Expand All @@ -195,7 +195,7 @@ def test_ps_paths_only_succeeds_on_spec_branch(prereq_repo: Path) -> None:
feat.mkdir(parents=True, exist_ok=True)
_write_feature_json(prereq_repo)
script = prereq_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL
env = _clean_env()
env["SPECIFY_FEATURE"] = "001-my-feature"
result = subprocess.run(
Expand All @@ -211,11 +211,11 @@ def test_ps_paths_only_succeeds_on_spec_branch(prereq_repo: Path) -> None:
assert "FEATURE_DIR" in data


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_ps_normal_mode_still_validates_branch(prereq_repo: Path) -> None:
"""Without -PathsOnly, feature directory validation must still fail on main."""
script = prereq_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL
result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
cwd=prereq_repo,
Expand Down
10 changes: 5 additions & 5 deletions tests/test_setup_plan_feature_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
PLAN_TEMPLATE = PROJECT_ROOT / "templates" / "plan-template.md"

HAS_PWSH = shutil.which("pwsh") is not None
_POWERSHELL = shutil.which("powershell.exe") or shutil.which("powershell")
_WINDOWS_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None


def _install_bash_scripts(repo: Path) -> None:
Expand Down Expand Up @@ -153,7 +153,7 @@ def test_setup_plan_numbered_branch_works_with_feature_json(
assert (feat / "plan.md").is_file()


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_setup_plan_ps_passes_custom_branch_when_feature_json_valid(plan_repo: Path) -> None:
subprocess.run(
["git", "checkout", "-q", "-b", "feature/my-feature-branch"],
Expand All @@ -165,7 +165,7 @@ def test_setup_plan_ps_passes_custom_branch_when_feature_json_valid(plan_repo: P
(feat / "spec.md").write_text("# spec\n", encoding="utf-8")
_write_feature_json(plan_repo, "specs/001-tiny-notes-app")
script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL
result = subprocess.run(
[exe, "-NoProfile", "-File", str(script)],
cwd=plan_repo,
Expand All @@ -178,12 +178,12 @@ def test_setup_plan_ps_passes_custom_branch_when_feature_json_valid(plan_repo: P
assert (feat / "plan.md").is_file()


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_setup_plan_ps_errors_without_feature_context(
plan_repo: Path,
) -> None:
script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL
result = subprocess.run(
[exe, "-NoProfile", "-File", str(script)],
cwd=plan_repo,
Expand Down
10 changes: 5 additions & 5 deletions tests/test_setup_plan_no_overwrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
PLAN_TEMPLATE = PROJECT_ROOT / "templates" / "plan-template.md"

HAS_PWSH = shutil.which("pwsh") is not None
_POWERSHELL = shutil.which("powershell.exe") or shutil.which("powershell")
_WINDOWS_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None


def _install_bash_scripts(repo: Path) -> None:
Expand Down Expand Up @@ -178,11 +178,11 @@ def test_setup_plan_json_parseable_on_first_run(plan_repo: Path) -> None:
# ── PowerShell tests ──────────────────────────────────────────────────────


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_ps_setup_plan_creates_plan_when_missing(plan_repo: Path) -> None:
"""First run must create plan.md from the template."""
script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL
result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
cwd=plan_repo,
Expand All @@ -199,7 +199,7 @@ def test_ps_setup_plan_creates_plan_when_missing(plan_repo: Path) -> None:
assert len(content) > 0


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_ps_setup_plan_preserves_existing_plan(plan_repo: Path) -> None:
"""Rerun must not overwrite an existing plan.md."""
feat = plan_repo / "specs" / "001-my-feature"
Expand All @@ -208,7 +208,7 @@ def test_ps_setup_plan_preserves_existing_plan(plan_repo: Path) -> None:
(feat / "plan.md").write_text(existing_content, encoding="utf-8")

script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL
result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
cwd=plan_repo,
Expand Down
36 changes: 18 additions & 18 deletions tests/test_setup_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
TASKS_TEMPLATE = PROJECT_ROOT / "templates" / "tasks-template.md"

HAS_PWSH = shutil.which("pwsh") is not None
_POWERSHELL = shutil.which("powershell.exe") or shutil.which("powershell")
_WINDOWS_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None


# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -118,7 +118,7 @@ def _run_bash_format_command(repo: Path, command_name: str) -> subprocess.Comple

def _run_powershell_format_command(repo: Path, command_name: str) -> subprocess.CompletedProcess:
script = repo / ".specify" / "scripts" / "powershell" / "common.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL
return subprocess.run(
[
exe,
Expand Down Expand Up @@ -606,7 +606,7 @@ def test_setup_tasks_bash_errors_without_feature_context(
# POWERSHELL TESTS
# ===========================================================================

@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_setup_tasks_ps_core_template_resolved(tasks_repo: Path) -> None:
"""
When the core tasks-template.md is present and all prerequisites are met,
Expand All @@ -615,7 +615,7 @@ def test_setup_tasks_ps_core_template_resolved(tasks_repo: Path) -> None:
"""
_minimal_feature(tasks_repo)
script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL

result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
Expand All @@ -635,7 +635,7 @@ def test_setup_tasks_ps_core_template_resolved(tasks_repo: Path) -> None:
assert tasks_tmpl.name == "tasks-template.md"


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_setup_tasks_ps_override_wins(tasks_repo: Path) -> None:
"""
When an override exists at .specify/templates/overrides/tasks-template.md,
Expand All @@ -649,7 +649,7 @@ def test_setup_tasks_ps_override_wins(tasks_repo: Path) -> None:
override_file.write_text("# override tasks template\n", encoding="utf-8")

script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL

result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
Expand All @@ -671,7 +671,7 @@ def test_setup_tasks_ps_override_wins(tasks_repo: Path) -> None:
)


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_setup_tasks_ps_missing_template_errors(tasks_repo: Path) -> None:
"""
When tasks-template.md is absent from all locations, setup-tasks.ps1 must
Expand All @@ -683,7 +683,7 @@ def test_setup_tasks_ps_missing_template_errors(tasks_repo: Path) -> None:
core.unlink()

script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL

result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
Expand All @@ -698,7 +698,7 @@ def test_setup_tasks_ps_missing_template_errors(tasks_repo: Path) -> None:
assert "tasks-template" in result.stderr.lower() or "tasks-template" in result.stdout.lower()


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_powershell_command_hint_normalizes_mixed_separators(
tasks_repo: Path,
) -> None:
Expand All @@ -717,7 +717,7 @@ def test_powershell_command_hint_normalizes_mixed_separators(
assert result.stdout.strip() == "/speckit-git-commit"


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_powershell_command_hint_preserves_hyphens_inside_segments(
tasks_repo: Path,
) -> None:
Expand All @@ -729,7 +729,7 @@ def test_powershell_command_hint_preserves_hyphens_inside_segments(
assert result.stdout.strip() == "/speckit.jira.sync-status"


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_setup_tasks_ps_uses_invoke_separator_in_plan_hint(tasks_repo: Path) -> None:
_write_integration_state(tasks_repo, "claude", "-")
feat = tasks_repo / "specs" / "001-my-feature"
Expand All @@ -738,7 +738,7 @@ def test_setup_tasks_ps_uses_invoke_separator_in_plan_hint(tasks_repo: Path) ->
_write_feature_json(tasks_repo)

script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL

result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
Expand All @@ -755,15 +755,15 @@ def test_setup_tasks_ps_uses_invoke_separator_in_plan_hint(tasks_repo: Path) ->
assert "/speckit.plan" not in output


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_check_prerequisites_ps_uses_invoke_separator_in_tasks_hint(
tasks_repo: Path,
) -> None:
_write_integration_state(tasks_repo, "claude", "-")
_minimal_feature(tasks_repo)

script = tasks_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL

result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-RequireTasks"],
Expand All @@ -780,7 +780,7 @@ def test_check_prerequisites_ps_uses_invoke_separator_in_tasks_hint(
assert "/speckit.tasks" not in output


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_setup_tasks_ps_passes_custom_branch_when_feature_json_valid(
tasks_repo: Path,
) -> None:
Expand All @@ -801,7 +801,7 @@ def test_setup_tasks_ps_passes_custom_branch_when_feature_json_valid(
_write_feature_json(tasks_repo)

script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL

result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
Expand All @@ -815,7 +815,7 @@ def test_setup_tasks_ps_passes_custom_branch_when_feature_json_valid(
assert result.returncode == 0, result.stderr + result.stdout


@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available")
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
def test_setup_tasks_ps_errors_without_feature_context(
tasks_repo: Path,
) -> None:
Expand All @@ -826,7 +826,7 @@ def test_setup_tasks_ps_errors_without_feature_context(
(main_feat / "plan.md").write_text("# plan\n", encoding="utf-8")

script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1"
exe = "pwsh" if HAS_PWSH else _POWERSHELL
exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL

result = subprocess.run(
[exe, "-NoProfile", "-File", str(script), "-Json"],
Expand Down