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
123 changes: 123 additions & 0 deletions cloudsmith_cli/core/tests/test_terminal_color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
from enum import Flag, auto
from typing import ClassVar

import pytest

from cloudsmith_cli.core.utils import (
ColorMode,
TTYMode,
color_enabled,
is_interactive,
)


class Desired(Flag):
NONE = 0
COLOR = auto()
INTERACTIVE = auto()


class TestTerminalUISuppression:
"""Testing precedence in environment variables for controlling interactivity and colour"""

env_tests: ClassVar[list] = [
pytest.param(
{},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.COLOR | Desired.INTERACTIVE,
id="default behaviour",
),
pytest.param(
{},
ColorMode.NEVER,
TTYMode.DISABLED,
Desired.NONE,
id="force disable tty and color mode",
),
pytest.param(
{"NO_COLOR": ""},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.COLOR | Desired.INTERACTIVE,
id="empty no color var",
),
pytest.param(
{"CLOUDSMITH_FORCE_COLOR": "true"},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.COLOR | Desired.INTERACTIVE,
id="force ANSI output",
),
pytest.param(
{"NO_COLOR": "true", "CLOUDSMITH_FORCE_COLOR": "true"},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.INTERACTIVE,
id="force ANSI output, respects NO_COLOR",
),
pytest.param(
{"TERM": "dumb"},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.INTERACTIVE,
id="no color enabled for TERM=dumb",
),
pytest.param(
{"CI": "true"},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.COLOR,
id="in a CI server, do not use interactive features",
),
pytest.param(
{"CI": "true", "NO_COLOR": "true"},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.NONE,
id="suppress interactivity and colour",
),
pytest.param(
{
"NO_COLOR": "true",
"TERM": "dumb",
"CLOUDSMITH_FORCE_COLOR": "true",
},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.INTERACTIVE,
id="test NO_COLOR always respected",
),
pytest.param(
{
"TERM": "dumb",
"CLOUDSMITH_FORCE_COLOR": "true",
},
ColorMode.AUTO,
TTYMode.ENABLED,
Desired.COLOR | Desired.INTERACTIVE,
id="ensure force tty respected over term dumb",
),
]

@pytest.mark.parametrize("env,colorMode,ttyMode,desired", env_tests)
def test_no_color_environment_variables(
self,
env: dict[str, str],
colorMode: ColorMode,
ttyMode: TTYMode,
desired: Desired,
):
want_color = Desired.COLOR in desired
assert color_enabled(env, colorMode, ttyMode) == want_color, (
f"colour suppression check failed for environment: {env} wanted {desired}"
)

@pytest.mark.parametrize("env,colorMode,ttyMode,desired", env_tests)
def test_no_interactive_environment_variables(
self, env, colorMode, ttyMode, desired
):
want_interactive = Desired.INTERACTIVE in desired
assert is_interactive(env, ttyMode) == want_interactive, (
f"interactive suppression check failed for environment {env} wanted {desired}"
)
50 changes: 50 additions & 0 deletions cloudsmith_cli/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import hashlib
import os
from enum import Enum, auto

import click

Expand All @@ -11,6 +12,55 @@ def get_help_website():
return "https://docs.cloudsmith.com/developer-tools/cli"


class ColorMode(Enum):
AUTO = auto()
ALWAYS = auto()
NEVER = auto()
Comment thread
coillteoir marked this conversation as resolved.


class TTYMode(Enum):
ENABLED = auto()
DISABLED = auto()


def color_enabled(env: dict[str, str], colorMode: ColorMode, ttyMode: TTYMode) -> bool:
"""Suppresses ANSI colour based on environment and CLI input"""
match colorMode:
case ColorMode.ALWAYS:
return True
case ColorMode.NEVER:
return False
case ColorMode.AUTO:
pass

if env.get("NO_COLOR"):
return False
if env.get("CLOUDSMITH_FORCE_COLOR") == "true":
return True
if env.get("TERM") == "dumb":
return False

match ttyMode:
case TTYMode.ENABLED:
return True
case TTYMode.DISABLED:
return False
return True


def is_interactive(env: dict[str, str], tty_mode: TTYMode) -> bool:
"""Suppresses interactive prompts for user input based on conditions in the shell environment"""
if env.get("CI"):
return False

match tty_mode:
case TTYMode.ENABLED:
return True
case TTYMode.DISABLED:
return False
return True


def get_github_website():
"""Get the URL for the GitHub project."""
return "https://github.com/cloudsmith-io/cloudsmith-cli"
Expand Down
Loading