Repository: aiogram/aiogram
Pull request #662 (2021-08-09 11:24:30)

add aliases for edit/delete reply markup to Message
# Description

add aliases for edit/delete reply markup to Message

Fixes # (issue)

## Type of change

Please delete options that are not relevant.

- [ ] Documentation (typos, code examples or any documentation update)
- [x] New feature (non-breaking change which adds functionality)
- [ ] This change requires a documentation update

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

- [x] Add tests
- [x] Manual testing in bot

**Test Configuration**:
* Operating System: Win10
* Python version: 3.9.2

# Checklist:

- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] New and existing unit tests pass locally with my changes


codecov[bot]: # [Codecov](https://codecov.io/gh/aiogram/aiogram/pull/662?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) Report
> Merging [#662](https://codecov.io/gh/aiogram/aiogram/pull/662?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) (514e819) into [dev-3.x](https://codecov.io/gh/aiogram/aiogram/commit/0ca9f98baf5820c2f6cd3d770d7e6cc61bdf8306?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) (0ca9f98) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/aiogram/aiogram/pull/662/graphs/tree.svg?width=650&height=150&src=pr&token=cFqEr3p9MX&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram)](https://codecov.io/gh/aiogram/aiogram/pull/662?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram)

```diff
@@            Coverage Diff            @@
##           dev-3.x      #662   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          269       269           
  Lines         5632      5637    +5     
=========================================
+ Hits          5632      5637    +5     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `100.00% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/aiogram/aiogram/pull/662?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) | Coverage Δ | |
|---|---|---|
| [aiogram/types/message.py](https://codecov.io/gh/aiogram/aiogram/pull/662/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram#diff-YWlvZ3JhbS90eXBlcy9tZXNzYWdlLnB5) | `100.00% <100.00%> (ø)` | |


Patch:
diff --git a/CHANGES/660.bugfix b/CHANGES/660.bugfix
new file mode 100644
index 00000000..3da5568d
--- /dev/null
+++ b/CHANGES/660.bugfix
@@ -0,0 +1,1 @@
+Prepare parse mode for InputMessageContent in AnswerInlineQuery method
diff --git a/CHANGES/662.feature b/CHANGES/662.feature
new file mode 100644
index 00000000..3a92b141
--- /dev/null
+++ b/CHANGES/662.feature
@@ -0,0 +1,1 @@
+add aliases for edit/delete reply markup to Message
diff --git a/CHANGES/663.misc b/CHANGES/663.misc
new file mode 100644
index 00000000..38bdfb5d
--- /dev/null
+++ b/CHANGES/663.misc
@@ -0,0 +1,1 @@
+add allow_sending_without_reply param to Message reply aliases
diff --git a/aiogram/dispatcher/fsm/state.py b/aiogram/dispatcher/fsm/state.py
index d4ea1974..ced9779a 100644
--- a/aiogram/dispatcher/fsm/state.py
+++ b/aiogram/dispatcher/fsm/state.py
@@ -111,8 +111,8 @@ class StatesGroupMeta(type):
             return item in cls.__all_states_names__
         if isinstance(item, State):
             return item in cls.__all_states__
-        # if isinstance(item, StatesGroup):
-        #     return item in cls.__all_childs__
+        if isinstance(item, StatesGroupMeta):
+            return item in cls.__all_childs__
         return False
 
     def __str__(self) -> str:
@@ -126,8 +126,11 @@ class StatesGroup(metaclass=StatesGroupMeta):
             return cls
         return cls.__parent__.get_root()
 
-    # def __call__(cls, event: TelegramObject, raw_state: Optional[str] = None) -> bool:
-    #     return raw_state in cls.__all_states_names__
+    def __call__(cls, event: TelegramObject, raw_state: Optional[str] = None) -> bool:
+        return raw_state in type(cls).__all_states_names__
+
+    def __str__(self) -> str:
+        return f"StatesGroup {type(self).__full_group_name__}"
 
 
 default_state = State()
diff --git a/aiogram/methods/answer_inline_query.py b/aiogram/methods/answer_inline_query.py
index 95c9b66d..78511403 100644
--- a/aiogram/methods/answer_inline_query.py
+++ b/aiogram/methods/answer_inline_query.py
@@ -37,5 +37,12 @@ class AnswerInlineQuery(TelegramMethod[bool]):
 
     def build_request(self, bot: Bot) -> Request:
         data: Dict[str, Any] = self.dict()
-        prepare_parse_mode(bot, data["results"])
+
+        input_message_contents = []
+        for result in data["results"]:
+            input_message_content = result.get("input_message_content", None)
+            if input_message_content is not None:
+                input_message_contents.append(input_message_content)
+
+        prepare_parse_mode(bot, data["results"] + input_message_contents)
         return Request(method="answerInlineQuery", data=data)
diff --git a/aiogram/types/message.py b/aiogram/types/message.py
index 8d72d0e5..1f2fd996 100644
--- a/aiogram/types/message.py
+++ b/aiogram/types/message.py
@@ -14,6 +14,7 @@ if TYPE_CHECKING:  # pragma: no cover
         CopyMessage,
         DeleteMessage,
         EditMessageCaption,
+        EditMessageReplyMarkup,
         EditMessageText,
         SendAnimation,
         SendAudio,
@@ -268,6 +269,7 @@ class Message(TelegramObject):
         caption: Optional[str] = None,
         parse_mode: Optional[str] = UNSET,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -283,6 +285,7 @@ class Message(TelegramObject):
         :param caption:
         :param parse_mode:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -299,6 +302,7 @@ class Message(TelegramObject):
             parse_mode=parse_mode,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -356,6 +360,7 @@ class Message(TelegramObject):
         title: Optional[str] = None,
         thumb: Optional[Union[InputFile, str]] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -371,6 +376,7 @@ class Message(TelegramObject):
         :param title:
         :param thumb:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -387,6 +393,7 @@ class Message(TelegramObject):
             thumb=thumb,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -441,6 +448,7 @@ class Message(TelegramObject):
         last_name: Optional[str] = None,
         vcard: Optional[str] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -453,6 +461,7 @@ class Message(TelegramObject):
         :param last_name:
         :param vcard:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -466,6 +475,7 @@ class Message(TelegramObject):
             vcard=vcard,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -511,6 +521,7 @@ class Message(TelegramObject):
         caption: Optional[str] = None,
         parse_mode: Optional[str] = UNSET,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -523,6 +534,7 @@ class Message(TelegramObject):
         :param caption:
         :param parse_mode:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -536,6 +548,7 @@ class Message(TelegramObject):
             parse_mode=parse_mode,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -578,6 +591,7 @@ class Message(TelegramObject):
         self,
         game_short_name: str,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[InlineKeyboardMarkup] = None,
     ) -> SendGame:
         """
@@ -585,6 +599,7 @@ class Message(TelegramObject):
 
         :param game_short_name:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -595,6 +610,7 @@ class Message(TelegramObject):
             game_short_name=game_short_name,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -644,6 +660,7 @@ class Message(TelegramObject):
         send_email_to_provider: Optional[bool] = None,
         is_flexible: Optional[bool] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[InlineKeyboardMarkup] = None,
     ) -> SendInvoice:
         """
@@ -669,6 +686,7 @@ class Message(TelegramObject):
         :param send_email_to_provider:
         :param is_flexible:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -697,6 +715,7 @@ class Message(TelegramObject):
             is_flexible=is_flexible,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -784,6 +803,7 @@ class Message(TelegramObject):
         longitude: float,
         live_period: Optional[int] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -795,6 +815,7 @@ class Message(TelegramObject):
         :param longitude:
         :param live_period:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -807,6 +828,7 @@ class Message(TelegramObject):
             live_period=live_period,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -846,12 +868,14 @@ class Message(TelegramObject):
         self,
         media: List[Union[InputMediaPhoto, InputMediaVideo]],
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
     ) -> SendMediaGroup:
         """
         Reply with media group
 
         :param media:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :return:
         """
         from ..methods import SendMediaGroup
@@ -861,6 +885,7 @@ class Message(TelegramObject):
             media=media,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
         )
 
     def answer_media_group(
@@ -890,6 +915,7 @@ class Message(TelegramObject):
         parse_mode: Optional[str] = UNSET,
         disable_web_page_preview: Optional[bool] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -901,6 +927,7 @@ class Message(TelegramObject):
         :param parse_mode:
         :param disable_web_page_preview:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -913,6 +940,7 @@ class Message(TelegramObject):
             disable_web_page_preview=disable_web_page_preview,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -954,6 +982,7 @@ class Message(TelegramObject):
         caption: Optional[str] = None,
         parse_mode: Optional[str] = UNSET,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -965,6 +994,7 @@ class Message(TelegramObject):
         :param caption:
         :param parse_mode:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -977,6 +1007,7 @@ class Message(TelegramObject):
             parse_mode=parse_mode,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -1026,6 +1057,7 @@ class Message(TelegramObject):
         close_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None,
         is_closed: Optional[bool] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -1045,6 +1077,7 @@ class Message(TelegramObject):
         :param close_date:
         :param is_closed:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -1065,6 +1098,7 @@ class Message(TelegramObject):
             is_closed=is_closed,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -1128,6 +1162,7 @@ class Message(TelegramObject):
         self,
         emoji: Optional[str] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -1137,6 +1172,7 @@ class Message(TelegramObject):
 
         :param emoji:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -1147,6 +1183,7 @@ class Message(TelegramObject):
             emoji=emoji,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -1180,6 +1217,7 @@ class Message(TelegramObject):
         self,
         sticker: Union[InputFile, str],
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -1189,6 +1227,7 @@ class Message(TelegramObject):
 
         :param sticker:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -1199,6 +1238,7 @@ class Message(TelegramObject):
             sticker=sticker,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -1237,6 +1277,7 @@ class Message(TelegramObject):
         foursquare_id: Optional[str] = None,
         foursquare_type: Optional[str] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -1251,6 +1292,7 @@ class Message(TelegramObject):
         :param foursquare_id:
         :param foursquare_type:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -1266,6 +1308,7 @@ class Message(TelegramObject):
             foursquare_type=foursquare_type,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -1321,6 +1364,7 @@ class Message(TelegramObject):
         parse_mode: Optional[str] = UNSET,
         supports_streaming: Optional[bool] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -1337,6 +1381,7 @@ class Message(TelegramObject):
         :param parse_mode:
         :param supports_streaming:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -1354,6 +1399,7 @@ class Message(TelegramObject):
             supports_streaming=supports_streaming,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -1411,6 +1457,7 @@ class Message(TelegramObject):
         length: Optional[int] = None,
         thumb: Optional[Union[InputFile, str]] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -1423,6 +1470,7 @@ class Message(TelegramObject):
         :param length:
         :param thumb:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -1436,6 +1484,7 @@ class Message(TelegramObject):
             thumb=thumb,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -1481,6 +1530,7 @@ class Message(TelegramObject):
         parse_mode: Optional[str] = UNSET,
         duration: Optional[int] = None,
         disable_notification: Optional[bool] = None,
+        allow_sending_without_reply: Optional[bool] = None,
         reply_markup: Optional[
             Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
         ] = None,
@@ -1493,6 +1543,7 @@ class Message(TelegramObject):
         :param parse_mode:
         :param duration:
         :param disable_notification:
+        :param allow_sending_without_reply:
         :param reply_markup:
         :return:
         """
@@ -1506,6 +1557,7 @@ class Message(TelegramObject):
             duration=duration,
             disable_notification=disable_notification,
             reply_to_message_id=self.message_id,
+            allow_sending_without_reply=allow_sending_without_reply,
             reply_markup=reply_markup,
         )
 
@@ -1737,6 +1789,21 @@ class Message(TelegramObject):
             reply_markup=reply_markup,
         )
 
+    def edit_reply_markup(
+        self,
+        reply_markup: Optional[InlineKeyboardMarkup] = None,
+    ) -> EditMessageReplyMarkup:
+        from ..methods import EditMessageReplyMarkup
+
+        return EditMessageReplyMarkup(
+            chat_id=self.chat.id,
+            message_id=self.message_id,
+            reply_markup=reply_markup,
+        )
+
+    def delete_reply_markup(self) -> EditMessageReplyMarkup:
+        return self.edit_reply_markup(reply_markup=None)
+
     def edit_caption(
         self,
         caption: str,


Test patch:
diff --git a/tests/test_api/test_methods/test_answer_inline_query.py b/tests/test_api/test_methods/test_answer_inline_query.py
index 6b15cb8c..98227b57 100644
--- a/tests/test_api/test_methods/test_answer_inline_query.py
+++ b/tests/test_api/test_methods/test_answer_inline_query.py
@@ -2,7 +2,7 @@ import pytest
 
 from aiogram import Bot
 from aiogram.methods import AnswerInlineQuery, Request
-from aiogram.types import InlineQueryResult, InlineQueryResultPhoto
+from aiogram.types import InlineQueryResult, InlineQueryResultPhoto, InputTextMessageContent
 from tests.mocked_bot import MockedBot
 
 pytestmark = pytest.mark.asyncio
@@ -40,3 +40,22 @@ class TestAnswerInlineQuery:
         new_bot = Bot(token="42:TEST", parse_mode="HTML")
         request = query.build_request(new_bot)
         assert request.data["results"][0]["parse_mode"] == "HTML"
+
+    def test_parse_mode_input_message_content(self, bot: MockedBot):
+        query = AnswerInlineQuery(
+            inline_query_id="query id",
+            results=[
+                InlineQueryResultPhoto(
+                    id="result id",
+                    photo_url="photo",
+                    thumb_url="thumb",
+                    input_message_content=InputTextMessageContent(message_text="test"),
+                )
+            ],
+        )
+        request = query.build_request(bot)
+        assert request.data["results"][0]["input_message_content"]["parse_mode"] is None
+
+        new_bot = Bot(token="42:TEST", parse_mode="HTML")
+        request = query.build_request(new_bot)
+        assert request.data["results"][0]["input_message_content"]["parse_mode"] == "HTML"
diff --git a/tests/test_api/test_types/test_message.py b/tests/test_api/test_types/test_message.py
index bea20f08..cb450731 100644
--- a/tests/test_api/test_types/test_message.py
+++ b/tests/test_api/test_types/test_message.py
@@ -7,6 +7,7 @@ from aiogram.methods import (
     CopyMessage,
     DeleteMessage,
     EditMessageCaption,
+    EditMessageReplyMarkup,
     EditMessageText,
     SendAnimation,
     SendAudio,
@@ -36,6 +37,8 @@ from aiogram.types import (
     Document,
     EncryptedCredentials,
     Game,
+    InlineKeyboardButton,
+    InlineKeyboardMarkup,
     Invoice,
     Location,
     MessageAutoDeleteTimerChanged,
@@ -561,6 +564,64 @@ class TestMessage:
         assert isinstance(method, EditMessageText)
         assert method.chat_id == message.chat.id
 
+    def test_edit_reply_markup(self):
+        reply_markup = InlineKeyboardMarkup(
+            inline_keyboard=[
+                [
+                    InlineKeyboardButton(
+                        text="test",
+                        callback_data="test",
+                    ),
+                ],
+            ]
+        )
+        reply_markup_new = InlineKeyboardMarkup(
+            inline_keyboard=[
+                [
+                    InlineKeyboardButton(
+                        text="test2",
+                        callback_data="test2",
+                    ),
+                ],
+            ]
+        )
+
+        message = Message(
+            message_id=42,
+            chat=Chat(id=42, type="private"),
+            date=datetime.datetime.now(),
+            reply_markup=reply_markup,
+        )
+        method = message.edit_reply_markup(
+            reply_markup=reply_markup_new,
+        )
+        assert isinstance(method, EditMessageReplyMarkup)
+        assert method.reply_markup == reply_markup_new
+        assert method.chat_id == message.chat.id
+
+    def test_delete_reply_markup(self):
+        reply_markup = InlineKeyboardMarkup(
+            inline_keyboard=[
+                [
+                    InlineKeyboardButton(
+                        text="test",
+                        callback_data="test",
+                    ),
+                ],
+            ]
+        )
+
+        message = Message(
+            message_id=42,
+            chat=Chat(id=42, type="private"),
+            date=datetime.datetime.now(),
+            reply_markup=reply_markup,
+        )
+        method = message.delete_reply_markup()
+        assert isinstance(method, EditMessageReplyMarkup)
+        assert method.reply_markup is None
+        assert method.chat_id == message.chat.id
+
     def test_edit_caption(self):
         message = Message(
             message_id=42, chat=Chat(id=42, type="private"), date=datetime.datetime.now()
diff --git a/tests/test_dispatcher/test_fsm/test_state.py b/tests/test_dispatcher/test_fsm/test_state.py
index 07037e86..04c8e448 100644
--- a/tests/test_dispatcher/test_fsm/test_state.py
+++ b/tests/test_dispatcher/test_fsm/test_state.py
@@ -139,8 +139,7 @@ class TestStatesGroup:
         assert MyGroup.state1 not in MyGroup.MyNestedGroup
         assert MyGroup.state1 in MyGroup
 
-        # Not working as well
-        # assert MyGroup.MyNestedGroup in MyGroup
+        assert MyGroup.MyNestedGroup in MyGroup
 
         assert "MyGroup.MyNestedGroup:state1" in MyGroup
         assert "MyGroup.MyNestedGroup:state1" in MyGroup.MyNestedGroup
@@ -150,3 +149,36 @@ class TestStatesGroup:
         assert 42 not in MyGroup
 
         assert MyGroup.MyNestedGroup.get_root() is MyGroup
+
+    def test_empty_filter(self):
+        class MyGroup(StatesGroup):
+            pass
+
+        assert str(MyGroup()) == "StatesGroup MyGroup"
+
+    def test_with_state_filter(self):
+        class MyGroup(StatesGroup):
+            state1 = State()
+            state2 = State()
+
+        assert MyGroup()(None, "MyGroup:state1")
+        assert MyGroup()(None, "MyGroup:state2")
+        assert not MyGroup()(None, "MyGroup:state3")
+
+        assert str(MyGroup()) == "StatesGroup MyGroup"
+
+    def test_nested_group_filter(self):
+        class MyGroup(StatesGroup):
+            state1 = State()
+
+            class MyNestedGroup(StatesGroup):
+                state1 = State()
+
+        assert MyGroup()(None, "MyGroup:state1")
+        assert MyGroup()(None, "MyGroup.MyNestedGroup:state1")
+        assert not MyGroup()(None, "MyGroup:state2")
+        assert MyGroup.MyNestedGroup()(None, "MyGroup.MyNestedGroup:state1")
+        assert not MyGroup.MyNestedGroup()(None, "MyGroup:state1")
+
+        assert str(MyGroup()) == "StatesGroup MyGroup"
+        assert str(MyGroup.MyNestedGroup()) == "StatesGroup MyGroup.MyNestedGroup"