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

import pytest
from _pytest.mark.structures import ParameterSet

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 and overriding terminal
colours"""

env_tests: ClassVar[list[ParameterSet]] = [
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}"
)
49 changes: 49 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,54 @@ def get_help_website():
return "https://docs.cloudsmith.com/developer-tools/cli"


class ColorMode(Enum):
AUTO = auto()
ALWAYS = auto()
NEVER = auto()


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


def color_enabled(env: dict[str, str], colorMode: ColorMode, ttyMode: TTYMode) -> bool:
"""Checks based on environment and"""
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], ttyMode: TTYMode) -> bool:
if env.get("CI") == "true":
return False

match ttyMode:
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