Skip to content
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
6 changes: 6 additions & 0 deletions src/gitmojis/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .model import Gitmoji, Guide

__all__ = [
"Gitmoji",
"Guide",
]
53 changes: 53 additions & 0 deletions src/gitmojis/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from collections import UserList
from dataclasses import dataclass
from typing import Iterable, Literal


@dataclass(frozen=True, kw_only=True)
class Gitmoji:
"""Represents a single Gitmoji and its data.

The data model is adapted from the schema of the original Gitmoji project.

Attributes:
emoji: The emoji symbol representing the Gitmoji.
entity: The HTML entity corresponding to the Gitmoji.
code: The Markdown code of the Gitmoji's emoji.
description: A brief description of changes introduced by commits and pull
requests marked by the Gitmoji.
name: The user-defined name or identifier of the Gitmoji.
semver: The Semantic Versioning level affected by the commits or pull requests
marked by the emoji associated with the Gitmoji, if specified. May be `None`
or one of the following: `"major"`, `"minor"`, `"patch"`.
"""

emoji: str
entity: str
code: str
description: str
name: str
semver: Literal["major", "minor", "patch"] | None


class Guide(UserList[Gitmoji]):
"""Represents a list of (a "guide" through) `Gitmoji` objects.

This class is used to create a collection of `Gitmoji` objects, providing a simple
framework for accessing various Gitmojis and their data.
"""

def __init__(self, *, gitmojis: Iterable[Gitmoji] | None = None) -> None:
"""Construct a new `Guide` object.

Args:
gitmojis: An optional iterable of `Gitmoji` objects used to create
the guide. If `None`, the guide is initialized as empty. Note
that the data must be passed as a keyword argument, in contrast
to the implementation provided by the base class.
"""
super().__init__(gitmojis)

@property
def gitmojis(self) -> list[Gitmoji]:
"""Return the guide's data with a semantically meaningful attribute name."""
return self.data
36 changes: 36 additions & 0 deletions tests/test_gitmoji.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from dataclasses import FrozenInstanceError, fields

import pytest

from gitmojis.model import Gitmoji


@pytest.fixture()
def gitmoji_json():
return {
"emoji": "🐛",
"entity": "🐛",
"code": ":bug:",
"description": "Fix a bug.",
"name": "bug",
"semver": "patch",
}


@pytest.fixture()
def gitmoji(gitmoji_json):
return Gitmoji(**gitmoji_json)


def test_gitmoji_init_populates_fields_from_kwargs(gitmoji_json, gitmoji):
for key, value in gitmoji_json.items():
assert getattr(gitmoji, key) == value


@pytest.mark.parametrize(
"field_name",
[field.name for field in fields(Gitmoji)],
)
def test_gitmoji_is_immutable(gitmoji, field_name):
with pytest.raises(FrozenInstanceError):
setattr(gitmoji, field_name, getattr(gitmoji, field_name))
62 changes: 62 additions & 0 deletions tests/test_guide.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import pytest

from gitmojis.model import Gitmoji, Guide


@pytest.fixture()
def gitmojis_json():
return [
{
"emoji": "💥",
"entity": "💥",
"code": ":boom:",
"description": "Introduce breaking changes.",
"name": "boom",
"semver": "major",
},
{
"emoji": "✨",
"entity": "✨",
"code": ":sparkles:",
"description": "Introduce new features.",
"name": "sparkles",
"semver": "minor",
},
{
"emoji": "🐛",
"entity": "🐛",
"code": ":bug:",
"description": "Fix a bug.",
"name": "bug",
"semver": "patch",
},
{
"emoji": "📝",
"entity": "📝",
"code": ":memo:",
"description": "Add or update documentation.",
"name": "memo",
"semver": None,
},
]


@pytest.fixture()
def guide(gitmojis_json):
return Guide(gitmojis=[Gitmoji(**gitmoji_json) for gitmoji_json in gitmojis_json])


def test_guide_init_raises_if_called_with_positional_argument():
with pytest.raises(TypeError):
Guide([])


def test_guide_init_succeeds_if_called_with_keyword_argument():
try:
Guide(gitmojis=[])
except TypeError:
pytest.fail()


def test_guide_gitmojis_returns_data(guide):
assert guide.gitmojis == guide.data