Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Theme Structure #1051

Merged
merged 12 commits into from
Jul 3, 2021
Merged
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
5 changes: 1 addition & 4 deletions tests/cli/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import pytest

from zulipterminal.cli.run import (
THEMES,
_write_zuliprc,
exit_with_error,
get_login_id,
Expand Down Expand Up @@ -140,7 +139,6 @@ def test_valid_zuliprc_but_no_connection(
def test_warning_regarding_incomplete_theme(
capsys,
mocker,
monkeypatch,
minimal_zuliprc,
bad_theme,
server_connection_error="sce",
Expand All @@ -149,13 +147,12 @@ def test_warning_regarding_incomplete_theme(
"zulipterminal.core.Controller.__init__",
side_effect=ServerConnectionFailure(server_connection_error),
)

monkeypatch.setitem(THEMES, bad_theme, [])
mocker.patch("zulipterminal.cli.run.all_themes", return_value=("a", "b", "c", "d"))
mocker.patch(
"zulipterminal.cli.run.complete_and_incomplete_themes",
return_value=(["a", "b"], ["c", "d"]),
)
mocker.patch("zulipterminal.cli.run.generate_theme")

with pytest.raises(SystemExit) as e:
main(["-c", minimal_zuliprc, "-t", bad_theme])
Expand Down
15 changes: 15 additions & 0 deletions tests/config/test_color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from enum import Enum

from zulipterminal.config.color import color_properties


def test_color_properties():
class Color(Enum):
WHITE = "wh #256 #24"

Color = color_properties(Color, "BOLD", "ITALICS")

assert Color.WHITE in Color
assert Color.WHITE.value == "wh #256 #24"
assert Color.WHITE__BOLD_ITALICS in Color
assert Color.WHITE__BOLD_ITALICS.value == "wh #256 #24 , bold , italics"
115 changes: 93 additions & 22 deletions tests/config/test_themes.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import re
from enum import Enum

Rohitth007 marked this conversation as resolved.
Show resolved Hide resolved
import pytest

from zulipterminal.config.themes import (
REQUIRED_STYLES,
THEMES,
all_themes,
complete_and_incomplete_themes,
required_styles,
theme_with_monochrome_added,
parse_themefile,
)


Expand All @@ -14,8 +17,26 @@
"gruvbox_dark",
"zt_light",
"zt_blue",
"gruvbox_dark24",
}
aliases_16_color = [
"default",
"black",
"dark red",
"dark green",
"brown",
"dark blue",
"dark magenta",
"dark cyan",
"dark gray",
"light red",
"light green",
"yellow",
"light blue",
"light magenta",
"light cyan",
"light gray",
"white",
]


def test_all_themes():
Expand All @@ -34,10 +55,37 @@ def test_all_themes():
)
def test_builtin_theme_completeness(theme_name):
theme = THEMES[theme_name]
styles_in_theme = {style[0] for style in theme}
theme_styles = theme.STYLES
theme_colors = theme.Color
Rohitth007 marked this conversation as resolved.
Show resolved Hide resolved

# Check if STYLE and REQUIRED_STYLES use the same styles.
assert len(theme_styles) == len(REQUIRED_STYLES)
assert all(required_style in theme_styles for required_style in REQUIRED_STYLES)
# Check if colors are defined with all 3 color codes.
for color in theme_colors:
if "__" in color.name:
continue

assert len(styles_in_theme) == len(required_styles)
assert all(required_style in styles_in_theme for required_style in required_styles)
codes = color.value.split()
assert len(codes) == 3
# Check if 16-color alias is correct
assert codes[0].replace("_", " ") in aliases_16_color
# Check if 24-bit and 256 color is any of
# #000000-#ffffff or #000-#fff or h0-h255 or g0-g100 0r g#00-g#ff
pattern = re.compile(
"#[\\da-f]{6}|#[\\da-f]{3}|(?:h|g)([\\d]{1,3})|g#[\\da-f]{2}|default$"
)
for code in [codes[1], codes[2]]:
code = pattern.match(code)
assert code
if code.group(1) and code.group(0).startswith("h"):
assert int(code.group(1)) < 256
elif code.group(1) and code.group(0).startswith("g"):
assert int(code.group(1)) <= 100
# Check if color used in STYLE exists in Color.
for style_name, style_conf in theme_styles.items():
fg, bg = style_conf
assert fg in theme_colors and bg in theme_colors


def test_complete_and_incomplete_themes():
Expand All @@ -50,26 +98,49 @@ def test_complete_and_incomplete_themes():


@pytest.mark.parametrize(
"theme, expected_new_theme, req_styles",
"color_depth, expected_urwid_theme",
[
([("a", "another")], [], {}),
([("a", "another")], [("a", "another")], {"a": ""}),
([("a", "fg", "bg")], [("a", "fg", "bg", "x")], {"a": "x"}),
([("a", "fg", "bg", "bold")], [("a", "fg", "bg", "x")], {"a": "x"}),
(1, [("s1", "", "", ""), ("s2", "", "", "bold")]),
(
16,
[
("s1", "white , bold", "dark magenta"),
("s2", "white , bold , italics", "dark magenta"),
],
),
(
[("a", "fg", "bg", "bold", "h1", "h2")],
[("a", "fg", "bg", "x", "h1", "h2")],
{"a": "x"},
256,
[
("s1", "", "", "", "#fff , bold", "h90"),
("s2", "", "", "", "#fff , bold , italics", "h90"),
],
),
(
2 ** 24,
[
("s1", "", "", "", "#ffffff , bold", "#870087"),
("s2", "", "", "", "#ffffff , bold , italics", "#870087"),
],
),
],
ids=[
"incomplete_theme",
"one_to_one",
"16_color_add_mono",
"16_color_mono_overwrite",
"256_color",
"mono-chrome",
"16-color",
"256-color",
"24-bit-color",
],
)
def test_theme_with_monochrome_added(mocker, theme, expected_new_theme, req_styles):
mocker.patch.dict("zulipterminal.config.themes.required_styles", req_styles)
assert theme_with_monochrome_added(theme) == expected_new_theme
def test_parse_themefile(mocker, color_depth, expected_urwid_theme):
class Color(Enum):
WHITE__BOLD = "white #fff #ffffff , bold"
WHITE__BOLD_ITALICS = "white #fff #ffffff , bold , italics"
DARK_MAGENTA = "dark_magenta h90 #870087"

STYLES = {
"s1": (Color.WHITE__BOLD, Color.DARK_MAGENTA),
"s2": (Color.WHITE__BOLD_ITALICS, Color.DARK_MAGENTA),
}

req_styles = {"s1": "", "s2": "bold"}
mocker.patch.dict("zulipterminal.config.themes.REQUIRED_STYLES", req_styles)
assert parse_themefile(STYLES, color_depth) == expected_urwid_theme
8 changes: 2 additions & 6 deletions zulipterminal/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@
from urwid import display_common, set_encoding

from zulipterminal.config.themes import (
THEMES,
aliased_themes,
all_themes,
complete_and_incomplete_themes,
theme_with_monochrome_added,
generate_theme,
)
from zulipterminal.core import Controller
from zulipterminal.model import ServerConnectionFailure
Expand Down Expand Up @@ -486,10 +485,7 @@ def main(options: Optional[List[str]] = None) -> None:
break
boolean_settings[setting] = zterm[setting][0] == valid_values[0]

if color_depth == 1:
theme_data = theme_with_monochrome_added(THEMES[theme_to_use[0]])
else:
theme_data = THEMES[theme_to_use[0]]
theme_data = generate_theme(theme_to_use[0], color_depth)
Rohitth007 marked this conversation as resolved.
Show resolved Hide resolved

Controller(
zuliprc_path,
Expand Down
63 changes: 63 additions & 0 deletions zulipterminal/config/color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
COLOR
-----
Contains color definitions or functions common across all themes.
For further details on themefiles look at the theme contribution guide.
"""
from enum import Enum
from typing import Any


# fmt: off
# NOTE: The 24bit color codes use 256 color which can be
# enhanced to be truly 24bit.
# NOTE: The 256code format can be moved to h0-255 to
# make use of the complete range instead of only 216 colors.
class DefaultColor(Enum):
# color = 16code 256code 24code
DEFAULT = 'default default default'
BLACK = 'black g19 g19'
DARK_RED = 'dark_red #a00 #a00'
DARK_GREEN = 'dark_green #080 #080'
BROWN = 'brown #880 #880'
DARK_BLUE = 'dark_blue #24a #24a'
DARK_MAGENTA = 'dark_magenta h90 h90' # #870087
DARK_CYAN = 'dark_cyan #088 #088'
DARK_GRAY = 'dark_gray #666 #666'
LIGHT_RED = 'light_red #f00 #f00'
LIGHT_GREEN = 'light_green #0f0 #0f0'
YELLOW = 'yellow #ff0 #ff0'
LIGHT_BLUE = 'light_blue #28d #28d'
LIGHT_MAGENTA = 'light_magenta #c8f #c8f'
LIGHT_CYAN = 'light_cyan h152 h152' # #afd7d7
LIGHT_GRAY = 'light_gray #ccc #ccc'
WHITE = 'white #fff #fff'
# fmt: on


def color_properties(colors: Any, *prop: str) -> Any:
"""
Adds properties(Bold, Italics, etc...) to Enum Colors in theme files.
Useage: color_properties(Color, 'BOLD', 'ITALICS', 'STRIKETHROUGH')

NOTE: color_properties(Color, BOLD, ITALICS) would result in only
Color.WHITE and Color.WHITE__BOLD_ITALICS
but not Color.WHITE__BOLD or Color.WHITE__ITALICS.
One would also have to do color_properties(Color, BOLD)
and color_properties(Color, ITALICS) for the others to work
>>> This function can be later extended to achieve all combinations
with one call to the function.
"""
prop_n = "_".join([p.upper() for p in prop])
prop_v = " , ".join([p.lower() for p in prop])
updated_colors: Any = Enum( # type: ignore # Ref: python/mypy#529, #535 and #5317
"Color",
{
**{c.name: c.value for c in colors},
**{c.name + f"__{prop_n}": c.value + f" , {prop_v}" for c in colors},
},
)
return updated_colors


DefaultBoldColor = color_properties(DefaultColor, "BOLD")
Loading