Repository: def-studio/telegraph (php)

Issue:
Leaking states when sending multiple messages
This code:
```php
/** @var TelegraphChat $telegraphChat */
$telegraphChat = $user->telegraphChat;
$telegraphChat
    ->markdown('*new message*')
    ->send();
$telegraphChat
    ->location(
        latitude: 12,
        longitude: 34,
    )
    ->send();
$telegraphChat
    ->markdown('*action*')
    ->keyboard(
        Keyboard::make()
            ->buttons([
                Button::make('Do it')->action('solved')->param('id', '12'),
            ])
    )
    ->send();
```
Results in following final post data:
```php
array:6 [
  "text" => """
    *action*
    """
  "chat_id" => "xxx"
  "parse_mode" => "markdown"
  "latitude" => 12.0
  "longitude" => 34.0
  "reply_markup" => array:1 [
    "inline_keyboard" => array:1 [
      0 => array:1 [
        0 => array:2 [
          "text" => "Do it"
          "callback_data" => "action:solved;id:12"
        ]
      ]
    ]
  ]
]
```

Pull request description:
fix leaking state
fix #64 

Patch:
diff --git a/src/Concerns/ComposesMessages.php b/src/Concerns/ComposesMessages.php
index 476d886d..b85a8c02 100644
--- a/src/Concerns/ComposesMessages.php
+++ b/src/Concerns/ComposesMessages.php
@@ -10,9 +10,11 @@ trait ComposesMessages
 {
     public function message(string $message): Telegraph
     {
+        $telegraph = clone $this;
+
         return match (config('telegraph.default_parse_mode')) {
-            self::PARSE_MARKDOWN => $this->markdown($message),
-            default => $this->html($message)
+            self::PARSE_MARKDOWN => $telegraph->markdown($message),
+            default => $telegraph->html($message)
         };
     }
 
@@ -26,70 +28,86 @@ trait ComposesMessages
 
     public function html(string $message = null): Telegraph
     {
+        $telegraph = clone $this;
+
         if ($message !== null) {
-            $this->setMessageText($message);
+            $telegraph->setMessageText($message);
         }
 
-        $this->data['parse_mode'] = 'html';
+        $telegraph->data['parse_mode'] = 'html';
 
-        return $this;
+        return $telegraph;
     }
 
     public function markdown(string $message = null): Telegraph
     {
+        $telegraph = clone $this;
+
         if ($message !== null) {
-            $this->setMessageText($message);
+            $telegraph->setMessageText($message);
         }
 
-        $this->data['parse_mode'] = 'markdown';
+        $telegraph->data['parse_mode'] = 'markdown';
 
-        return $this;
+        return $telegraph;
     }
 
     public function reply(int $messageId): Telegraph
     {
-        $this->data['reply_to_message_id'] = $messageId;
+        $telegraph = clone $this;
 
-        return $this;
+        $telegraph->data['reply_to_message_id'] = $messageId;
+
+        return $telegraph;
     }
 
     public function protected(): Telegraph
     {
-        $this->data['protect_content'] = true;
+        $telegraph = clone $this;
+
+        $telegraph->data['protect_content'] = true;
 
-        return $this;
+        return $telegraph;
     }
 
     public function silent(): Telegraph
     {
-        $this->data['disable_notification'] = true;
+        $telegraph = clone $this;
 
-        return $this;
+        $telegraph->data['disable_notification'] = true;
+
+        return $telegraph;
     }
 
     public function withoutPreview(): Telegraph
     {
-        $this->data['disable_web_page_preview'] = true;
+        $telegraph = clone $this;
+
+        $telegraph->data['disable_web_page_preview'] = true;
 
-        return $this;
+        return $telegraph;
     }
 
     public function deleteMessage(int $messageId): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_DELETE_MESSAGE;
-        $this->data = [
-            'chat_id' => $this->getChat()->chat_id,
+        $telegraph = clone $this;
+
+        $telegraph->endpoint = self::ENDPOINT_DELETE_MESSAGE;
+        $telegraph->data = [
+            'chat_id' => $telegraph->getChat()->chat_id,
             'message_id' => $messageId,
         ];
 
-        return $this;
+        return $telegraph;
     }
 
     public function edit(int $messageId): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_EDIT_MESSAGE;
-        $this->data['message_id'] = $messageId;
+        $telegraph = clone $this;
+
+        $telegraph->endpoint = self::ENDPOINT_EDIT_MESSAGE;
+        $telegraph->data['message_id'] = $messageId;
 
-        return $this;
+        return $telegraph;
     }
 }
diff --git a/src/Concerns/HasBotsAndChats.php b/src/Concerns/HasBotsAndChats.php
index 6e351aef..4b5400fe 100644
--- a/src/Concerns/HasBotsAndChats.php
+++ b/src/Concerns/HasBotsAndChats.php
@@ -24,79 +24,95 @@ trait HasBotsAndChats
 
     public function bot(TelegraphBot $bot): Telegraph
     {
-        $this->bot = $bot;
+        $telegraph = clone $this;
 
-        if (empty($this->chat)) {
-            $this->chat = rescue(fn () => $this->bot->chats->sole(), report: false); //@phpstan-ignore-line
+        $telegraph->bot = $bot;
+
+        if (empty($telegraph->chat)) {
+            $telegraph->chat = rescue(fn () => $telegraph->bot->chats->sole(), report: false); //@phpstan-ignore-line
         }
 
-        return $this;
+        return $telegraph;
     }
 
     public function chat(TelegraphChat $chat): Telegraph
     {
-        $this->chat = $chat;
+        $telegraph = clone $this;
+
+        $telegraph->chat = $chat;
 
-        if (empty($this->bot)) {
-            $this->bot = $this->chat->bot;
+        if (empty($telegraph->bot)) {
+            $telegraph->bot = $telegraph->chat->bot;
         }
 
-        return $this;
+        return $telegraph;
     }
 
     protected function getBotIfAvailable(): TelegraphBot|null
     {
-        if (empty($this->bot)) {
+        $telegraph = clone $this;
+
+        if (empty($telegraph->bot)) {
             /** @var TelegraphBot $bot */
             $bot = rescue(fn () => TelegraphBot::query()->with('chats')->sole(), null, false);
 
-            $this->bot = $bot;
+            $telegraph->bot = $bot;
         }
 
-        return $this->bot;
+        return $telegraph->bot;
     }
 
     protected function getBot(): TelegraphBot
     {
-        return $this->getBotIfAvailable() ?? throw TelegraphException::missingBot();
+        $telegraph = clone $this;
+
+        return $telegraph->getBotIfAvailable() ?? throw TelegraphException::missingBot();
     }
 
     protected function getChatIfAvailable(): TelegraphChat|null
     {
-        if (empty($this->chat)) {
+        $telegraph = clone $this;
+
+        if (empty($telegraph->chat)) {
             /** @var TelegraphChat $chat */
-            $chat = rescue(fn () => $this->getBotIfAvailable()?->chats()->sole(), null, false);
+            $chat = rescue(fn () => $telegraph->getBotIfAvailable()?->chats()->sole(), null, false);
 
-            $this->chat = $chat;
+            $telegraph->chat = $chat;
         }
 
-        if (empty($this->chat)) {
+        if (empty($telegraph->chat)) {
             /** @var TelegraphChat $chat */
             $chat = rescue(fn () => TelegraphChat::query()->sole(), null, false);
 
-            $this->chat = $chat;
+            $telegraph->chat = $chat;
         }
 
-        return $this->chat;
+        return $telegraph->chat;
     }
 
     protected function getChat(): TelegraphChat
     {
-        return $this->getChatIfAvailable() ?? throw TelegraphException::missingChat();
+        $telegraph = clone $this;
+
+        return $telegraph->getChatIfAvailable() ?? throw TelegraphException::missingChat();
     }
 
     public function botInfo(): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_GET_BOT_INFO;
+        $telegraph = clone $this;
 
-        return $this;
+        $telegraph->endpoint = self::ENDPOINT_GET_BOT_INFO;
+
+        return $telegraph;
     }
 
     public function botUpdates(): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_GET_BOT_UPDATES;
+        $telegraph = clone $this;
+
+        $telegraph->endpoint = self::ENDPOINT_GET_BOT_UPDATES;
 
-        return $this;
+        return $telegraph;
     }
 
     /**
@@ -104,13 +120,15 @@ trait HasBotsAndChats
      */
     public function registerBotCommands(array $commands): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_REGISTER_BOT_COMMANDS;
+        $telegraph = clone $this;
+
+        $telegraph->endpoint = self::ENDPOINT_REGISTER_BOT_COMMANDS;
 
         if (count($commands) > 100) {
             throw BotCommandException::tooManyCommands();
         }
 
-        $this->data['commands'] = collect($commands)->map(function (string $description, string $command) {
+        $telegraph->data['commands'] = collect($commands)->map(function (string $description, string $command) {
             if (strlen($command) > 32) {
                 throw BotCommandException::longCommand($command);
             }
@@ -125,24 +143,28 @@ trait HasBotsAndChats
             ];
         })->values()->toArray();
 
-        return $this;
+        return $telegraph;
     }
 
     public function unregisterBotCommands(): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_UNREGISTER_BOT_COMMANDS;
+        $telegraph = clone $this;
 
-        return $this;
+        $telegraph->endpoint = self::ENDPOINT_UNREGISTER_BOT_COMMANDS;
+
+        return $telegraph;
     }
 
     public function chatAction(string $action): Telegraph
     {
+        $telegraph = clone $this;
+
         in_array($action, ChatActions::available_actions()) || throw TelegraphException::invalidChatAction($action);
 
-        $this->endpoint = self::ENDPOINT_SEND_CHAT_ACTION;
-        $this->data['chat_id'] = $this->getChat()->chat_id;
-        $this->data['action'] = $action;
+        $telegraph->endpoint = self::ENDPOINT_SEND_CHAT_ACTION;
+        $telegraph->data['chat_id'] = $telegraph->getChat()->chat_id;
+        $telegraph->data['action'] = $action;
 
-        return $this;
+        return $telegraph;
     }
 }
diff --git a/src/Concerns/InteractsWithWebhooks.php b/src/Concerns/InteractsWithWebhooks.php
index 2b3f1cec..a9a8f472 100644
--- a/src/Concerns/InteractsWithWebhooks.php
+++ b/src/Concerns/InteractsWithWebhooks.php
@@ -13,29 +13,35 @@ trait InteractsWithWebhooks
 {
     public function registerWebhook(): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_SET_WEBHOOK;
-        $this->data = [
-            'url' => route('telegraph.webhook', $this->getBot()),
+        $telegraph = clone $this;
+
+        $telegraph->endpoint = self::ENDPOINT_SET_WEBHOOK;
+        $telegraph->data = [
+            'url' => route('telegraph.webhook', $telegraph->getBot()),
         ];
 
-        return $this;
+        return $telegraph;
     }
 
     public function getWebhookDebugInfo(): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_GET_WEBHOOK_DEBUG_INFO;
+        $telegraph = clone $this;
+
+        $telegraph->endpoint = self::ENDPOINT_GET_WEBHOOK_DEBUG_INFO;
 
-        return $this;
+        return $telegraph;
     }
 
     public function replyWebhook(int $callbackQueryId, string $message): Telegraph
     {
-        $this->endpoint = self::ENDPOINT_ANSWER_WEBHOOK;
-        $this->data = [
+        $telegraph = clone $this;
+
+        $telegraph->endpoint = self::ENDPOINT_ANSWER_WEBHOOK;
+        $telegraph->data = [
             'callback_query_id' => $callbackQueryId,
             'text' => $message,
         ];
 
-        return $this;
+        return $telegraph;
     }
 }
diff --git a/src/Concerns/ManagesKeyboards.php b/src/Concerns/ManagesKeyboards.php
index db37fd77..47dd52e3 100644
--- a/src/Concerns/ManagesKeyboards.php
+++ b/src/Concerns/ManagesKeyboards.php
@@ -21,6 +21,8 @@ trait ManagesKeyboards
      */
     public function keyboard(callable|array|Keyboard $keyboard): Telegraph
     {
+        $telegraph = clone $this;
+
         if (is_callable($keyboard)) {
             $keyboard = $keyboard(Keyboard::make());
         }
@@ -29,11 +31,11 @@ trait ManagesKeyboards
             $keyboard = Keyboard::fromArray($keyboard);
         }
 
-        $this->data['reply_markup'] = [
+        $telegraph->data['reply_markup'] = [
             'inline_keyboard' => $keyboard->toArray(),
         ];
 
-        return $this;
+        return $telegraph;
     }
 
     /**
@@ -41,6 +43,8 @@ trait ManagesKeyboards
      */
     public function replaceKeyboard(int $messageId, Keyboard|callable $newKeyboard): Telegraph
     {
+        $telegraph = clone $this;
+
         if (is_callable($newKeyboard)) {
             $newKeyboard = $newKeyboard(Keyboard::make());
         }
@@ -51,18 +55,20 @@ trait ManagesKeyboards
             $replyMarkup = ['inline_keyboard' => $newKeyboard->toArray()];
         }
 
-        $this->endpoint = self::ENDPOINT_REPLACE_KEYBOARD;
-        $this->data = [
-            'chat_id' => $this->getChat()->chat_id,
+        $telegraph->endpoint = self::ENDPOINT_REPLACE_KEYBOARD;
+        $telegraph->data = [
+            'chat_id' => $telegraph->getChat()->chat_id,
             'message_id' => $messageId,
             'reply_markup' => $replyMarkup,
         ];
 
-        return $this;
+        return $telegraph;
     }
 
     public function deleteKeyboard(int $messageId): Telegraph
     {
-        return $this->replaceKeyboard($messageId, Keyboard::make());
+        $telegraph = clone $this;
+
+        return $telegraph->replaceKeyboard($messageId, Keyboard::make());
     }
 }
diff --git a/src/Concerns/SendsFiles.php b/src/Concerns/SendsFiles.php
index f012c71f..dd5c721f 100644
--- a/src/Concerns/SendsFiles.php
+++ b/src/Concerns/SendsFiles.php
@@ -19,45 +19,51 @@ trait SendsFiles
 {
     public function document(string $path, string $filename = null): Telegraph
     {
+        $telegraph = clone $this;
+
         if (!File::exists($path)) {
             throw FileException::fileNotFound("Document", $path);
         }
 
-        if (($size = $this->fileSizeInMb($path)) > Telegraph::MAX_DOCUMENT_SIZE_IN_MB) {
+        if (($size = $telegraph->fileSizeInMb($path)) > Telegraph::MAX_DOCUMENT_SIZE_IN_MB) {
             throw FileException::documentSizeExceeded($size);
         }
 
-        $this->endpoint = self::ENDPOINT_SEND_DOCUMENT;
+        $telegraph->endpoint = self::ENDPOINT_SEND_DOCUMENT;
 
-        $this->data['chat_id'] = $this->getChat()->chat_id;
+        $telegraph->data['chat_id'] = $telegraph->getChat()->chat_id;
 
-        $this->files->put('document', new Attachment($path, $filename));
+        $telegraph->files->put('document', new Attachment($path, $filename));
 
-        return $this;
+        return $telegraph;
     }
 
     public function withoutContentTypeDetection(): Telegraph
     {
-        $this->data['disable_content_type_detection'] = 1;
+        $telegraph = clone $this;
+
+        $telegraph->data['disable_content_type_detection'] = 1;
 
-        return $this;
+        return $telegraph;
     }
 
     public function thumbnail(string $path): Telegraph
     {
+        $telegraph = clone $this;
+
         if (!File::exists($path)) {
             throw FileException::fileNotFound("Thumbnail", $path);
         }
 
-        if (($size = $this->fileSizeInKb($path)) > Telegraph::MAX_THUMBNAIL_SIZE_IN_KB) {
+        if (($size = $telegraph->fileSizeInKb($path)) > Telegraph::MAX_THUMBNAIL_SIZE_IN_KB) {
             throw FileException::thumbnailSizeExceeded($size);
         }
 
-        if (($height = $this->imageHeight($path)) > Telegraph::MAX_THUMBNAIL_HEIGHT) {
+        if (($height = $telegraph->imageHeight($path)) > Telegraph::MAX_THUMBNAIL_HEIGHT) {
             throw FileException::thumbnailHeightExceeded($height);
         }
 
-        if (($width = $this->imageWidth($path)) > Telegraph::MAX_THUMBNAIL_WIDTH) {
+        if (($width = $telegraph->imageWidth($path)) > Telegraph::MAX_THUMBNAIL_WIDTH) {
             throw FileException::thumbnailWidthExceeded($width);
         }
 
@@ -65,9 +71,9 @@ trait SendsFiles
             throw FileException::invalidThumbnailExtension($ext);
         }
 
-        $this->files->put('thumb', new Attachment($path));
+        $telegraph->files->put('thumb', new Attachment($path));
 
-        return $this;
+        return $telegraph;
     }
 
     private function imageHeight(string $path): int
diff --git a/src/Facades/Telegraph.php b/src/Facades/Telegraph.php
index 00fce782..f3a28903 100644
--- a/src/Facades/Telegraph.php
+++ b/src/Facades/Telegraph.php
@@ -45,6 +45,7 @@ class Telegraph extends Facade
      */
     public static function fake(array $replies = []): TelegraphFake
     {
+        TelegraphFake::reset();
         static::swap($fake = new TelegraphFake($replies));
 
         return $fake;


Test patch:
diff --git a/src/Support/Testing/Fakes/TelegraphFake.php b/src/Support/Testing/Fakes/TelegraphFake.php
index d1e630c0..e1be2f1c 100644
--- a/src/Support/Testing/Fakes/TelegraphFake.php
+++ b/src/Support/Testing/Fakes/TelegraphFake.php
@@ -24,7 +24,7 @@ use Psr\Http\Message\StreamInterface;
 class TelegraphFake extends Telegraph
 {
     /** @var array<int, mixed[]> */
-    private array $sentMessages = [];
+    private static array $sentMessages = [];
 
     /**
      * @param array<string, array<mixed>> $replies
@@ -34,9 +34,14 @@ class TelegraphFake extends Telegraph
         parent::__construct();
     }
 
+    public static function reset(): void
+    {
+        self::$sentMessages = [];
+    }
+
     protected function dispatchRequestToTelegram(string $queue = null): PendingDispatch
     {
-        $this->sentMessages[] = $this->messageToArray();
+        self::$sentMessages[] = $this->messageToArray();
 
         if (!Queue::getFacadeRoot() instanceof QueueFake) {
             Queue::fake();
@@ -65,7 +70,7 @@ class TelegraphFake extends Telegraph
 
     protected function sendRequestToTelegram(): Response
     {
-        $this->sentMessages[] = $this->messageToArray();
+        self::$sentMessages[] = $this->messageToArray();
 
         $messageClass = new class () implements MessageInterface {
             /**
@@ -238,7 +243,7 @@ class TelegraphFake extends Telegraph
      */
     public function assertSentData(string $endpoint, array $data = [], bool $exact = true): void
     {
-        $foundMessages = collect($this->sentMessages);
+        $foundMessages = collect(self::$sentMessages);
 
         $foundMessages = $foundMessages
             ->filter(fn (array $message): bool => $message['endpoint'] == $endpoint)
@@ -266,9 +271,9 @@ class TelegraphFake extends Telegraph
 
 
         if ($data == null) {
-            $errorMessage = sprintf("Failed to assert that a request was sent to [%s] endpoint (sent %d requests so far)", $endpoint, count($this->sentMessages));
+            $errorMessage = sprintf("Failed to assert that a request was sent to [%s] endpoint (sent %d requests so far)", $endpoint, count(self::$sentMessages));
         } else {
-            $errorMessage = sprintf("Failed to assert that a request was sent to [%s] endpoint with the given data (sent %d requests so far)", $endpoint, count($this->sentMessages));
+            $errorMessage = sprintf("Failed to assert that a request was sent to [%s] endpoint with the given data (sent %d requests so far)", $endpoint, count(self::$sentMessages));
         }
 
         Assert::assertNotEmpty($foundMessages->toArray(), $errorMessage);
@@ -279,7 +284,7 @@ class TelegraphFake extends Telegraph
      */
     public function assertSentFiles(string $endpoint, array $expectedFiles = []): void
     {
-        $foundMessages = collect($this->sentMessages);
+        $foundMessages = collect(self::$sentMessages);
 
         $foundMessages = $foundMessages
             ->filter(fn (array $message): bool => $message['endpoint'] == $endpoint)
@@ -306,9 +311,9 @@ class TelegraphFake extends Telegraph
 
 
         if ($foundMessages == null) {
-            $errorMessage = sprintf("Failed to assert that a request was sent to [%s] endpoint (sent %d requests so far)", $endpoint, count($this->sentMessages));
+            $errorMessage = sprintf("Failed to assert that a request was sent to [%s] endpoint (sent %d requests so far)", $endpoint, count(self::$sentMessages));
         } else {
-            $errorMessage = sprintf("Failed to assert that a request was sent to [%s] endpoint with the given files (sent %d requests so far)", $endpoint, count($this->sentMessages));
+            $errorMessage = sprintf("Failed to assert that a request was sent to [%s] endpoint with the given files (sent %d requests so far)", $endpoint, count(self::$sentMessages));
         }
 
         Assert::assertNotEmpty($foundMessages->toArray(), $errorMessage);
@@ -323,7 +328,7 @@ class TelegraphFake extends Telegraph
 
     public function assertNothingSent(): void
     {
-        Assert::assertEmpty($this->sentMessages, sprintf("Failed to assert that no request were sent (sent %d requests so far)", count($this->sentMessages)));
+        Assert::assertEmpty(self::$sentMessages, sprintf("Failed to assert that no request were sent (sent %d requests so far)", count(self::$sentMessages)));
     }
 
     public function assertRegisteredWebhook(): void
@@ -345,6 +350,6 @@ class TelegraphFake extends Telegraph
 
     public function dumpSentData(): void
     {
-        dump($this->sentMessages);
+        dump(self::$sentMessages);
     }
 }
diff --git a/tests/Pest.php b/tests/Pest.php
index 4ae3fc6a..ae0024d4 100644
--- a/tests/Pest.php
+++ b/tests/Pest.php
@@ -12,9 +12,10 @@ use DefStudio\Telegraph\Tests\TestCase;
 use Illuminate\Database\Eloquent\Collection;
 use Illuminate\Http\Request;
 
-uses(TestCase::class)->in(__DIR__ . "/Feature");
-uses(TestCase::class)->in(__DIR__ . "/Unit");
-uses(TestCase::class)->group('sandbox')->in(__DIR__ . "/Sandbox");
+uses(TestCase::class)->in("Feature");
+uses(TestCase::class)->in("Unit");
+uses(TestCase::class)->in("Regression");
+uses(TestCase::class)->group('sandbox')->in("Sandbox");
 
 
 /**
@@ -140,7 +141,7 @@ expect()->extend('toMatchTelegramSnapshot', function () {
     /** @var Telegraph $telegraph */
     $telegraph = app(Telegraph::class)->chat(make_chat());
 
-    $configurationClosure($telegraph);
+    $telegraph = $configurationClosure($telegraph);
 
     expect($telegraph->toArray())->toMatchSnapshot();
 });
diff --git a/tests/Regression/Issue64LeakingState.php b/tests/Regression/Issue64LeakingState.php
new file mode 100644
index 00000000..62fbeb29
--- /dev/null
+++ b/tests/Regression/Issue64LeakingState.php
@@ -0,0 +1,28 @@
+<?php
+
+use DefStudio\Telegraph\Facades\Telegraph;
+use DefStudio\Telegraph\Keyboard\Keyboard;
+
+test("telegraph state doesn't leak between calls", function () {
+    Telegraph::fake();
+
+    $chat = make_chat();
+
+    $chat->markdown('*action*')
+        ->keyboard(
+            Keyboard::make()
+            ->button('do it')->action('do')
+        )->send();
+
+    $array = $chat->message('test')->toArray();
+
+    expect($array)->toBe([
+        'url' => 'https://api.telegram.org/bot3f3814e1-5836-3d77-904e-60f64b15df36/sendMessage',
+        'payload' => [
+            'text' => 'test',
+            'chat_id' => '-123456789',
+            'parse_mode' => 'html',
+        ],
+        'files' => [],
+    ]);
+});
diff --git a/tests/Unit/Concerns/ComposesMessagesTest.php b/tests/Unit/Concerns/ComposesMessagesTest.php
index 43c3f17a..bed4b22d 100644
--- a/tests/Unit/Concerns/ComposesMessagesTest.php
+++ b/tests/Unit/Concerns/ComposesMessagesTest.php
@@ -4,43 +4,43 @@ use DefStudio\Telegraph\Telegraph;
 
 it('can send an html message', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->html('foobar');
+        return $telegraph->html('foobar');
     })->toMatchTelegramSnapshot();
 });
 
 it('can send a markdown message', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->markdown('foobar');
+        return $telegraph->markdown('foobar');
     })->toMatchTelegramSnapshot();
 });
 
 it('can send protected content', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->markdown('test')->protected();
+        return $telegraph->markdown('test')->protected();
     })->toMatchTelegramSnapshot();
 });
 
 it('can send silent messages', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->markdown('test')->silent();
+        return $telegraph->markdown('test')->silent();
     })->toMatchTelegramSnapshot();
 });
 
 it('can disable url preview', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->markdown('test')->withoutPreview();
+        return $telegraph->markdown('test')->withoutPreview();
     })->toMatchTelegramSnapshot();
 });
 
 it('can reply to a message', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->markdown('test')->reply(123456);
+        return $telegraph->markdown('test')->reply(123456);
     })->toMatchTelegramSnapshot();
 });
 
 it('can delete a message', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->deleteMessage(123456);
+        return $telegraph->deleteMessage(123456);
     })->toMatchTelegramSnapshot();
 });
 
@@ -48,9 +48,9 @@ it('can edit a message', function (callable $setupClosure) {
     expect($setupClosure)->toMatchTelegramSnapshot();
 })->with([
     'edit before text' => fn () => function (Telegraph $telegraph) {
-        $telegraph->edit(123456)->markdown('new text');
+        return $telegraph->edit(123456)->markdown('new text');
     },
     'edit after text' => fn () => function (Telegraph $telegraph) {
-        $telegraph->markdown('new text')->edit(123456);
+        return $telegraph->markdown('new text')->edit(123456);
     },
 ]);
diff --git a/tests/Unit/Concerns/HasBotsAndChatsTest.php b/tests/Unit/Concerns/HasBotsAndChatsTest.php
index 645df8fd..1b258690 100644
--- a/tests/Unit/Concerns/HasBotsAndChatsTest.php
+++ b/tests/Unit/Concerns/HasBotsAndChatsTest.php
@@ -19,7 +19,7 @@ it('can customize the destination bot', function () {
 
 it('can customize the destination chat', function () {
     expect(function (\DefStudio\Telegraph\Telegraph $telegraph) {
-        $telegraph->chat(make_chat())
+        return $telegraph->chat(make_chat())
             ->html('foobar');
     })->toMatchTelegramSnapshot();
 });
@@ -34,7 +34,7 @@ it('can retrieve bot info', function () {
 
 it('can register commands', function () {
     expect(function (\DefStudio\Telegraph\Telegraph $telegraph) {
-        $telegraph->bot(make_bot())->registerBotCommands([
+        return $telegraph->bot(make_bot())->registerBotCommands([
             'foo' => 'first command',
             'bar' => 'second command',
         ]);
@@ -43,12 +43,12 @@ it('can register commands', function () {
 
 it('can unregister commands', function () {
     expect(function (\DefStudio\Telegraph\Telegraph $telegraph) {
-        $telegraph->bot(make_bot())->unregisterBotCommands();
+        return $telegraph->bot(make_bot())->unregisterBotCommands();
     })->toMatchTelegramSnapshot();
 });
 
 it('can send a chat action', function () {
     expect(function (\DefStudio\Telegraph\Telegraph $telegraph) {
-        $telegraph->chat(make_chat())->chatAction(ChatActions::TYPING)->send();
+        return $telegraph->chat(make_chat())->chatAction(ChatActions::TYPING);
     })->toMatchTelegramSnapshot();
 });
diff --git a/tests/Unit/Concerns/InteractsWithWebhooksTest.php b/tests/Unit/Concerns/InteractsWithWebhooksTest.php
index 418e4238..859d17ab 100644
--- a/tests/Unit/Concerns/InteractsWithWebhooksTest.php
+++ b/tests/Unit/Concerns/InteractsWithWebhooksTest.php
@@ -4,21 +4,21 @@ use DefStudio\Telegraph\Telegraph;
 
 it('can register a webhook', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->bot(make_bot())
+        return $telegraph->bot(make_bot())
             ->registerWebhook();
     })->toMatchTelegramSnapshot();
 });
 
 it('can get webhook debug info', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph ->bot(make_bot())
+        return $telegraph ->bot(make_bot())
             ->getWebhookDebugInfo();
     })->toMatchTelegramSnapshot();
 });
 
 it('can reply to a webhook call', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph ->bot(make_bot())
+        return $telegraph ->bot(make_bot())
             ->replyWebhook(2123456, 'foo');
     })->toMatchTelegramSnapshot();
 });
diff --git a/tests/Unit/Concerns/ManagesKeyboardsTest.php b/tests/Unit/Concerns/ManagesKeyboardsTest.php
index 5dc3ebdf..e71d08eb 100644
--- a/tests/Unit/Concerns/ManagesKeyboardsTest.php
+++ b/tests/Unit/Concerns/ManagesKeyboardsTest.php
@@ -6,7 +6,7 @@ use DefStudio\Telegraph\Telegraph;
 
 it('can add a keyboard to a message', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->chat(make_chat())
+        return $telegraph->chat(make_chat())
             ->html('foobar')
             ->keyboard(Keyboard::make()->button('foo')->url('bar'));
     })->toMatchTelegramSnapshot();
@@ -14,7 +14,7 @@ it('can add a keyboard to a message', function () {
 
 it('can add a keyboard as an array', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph ->chat(make_chat())
+        return $telegraph ->chat(make_chat())
             ->html('foobar')
             ->keyboard([
                 [
@@ -26,7 +26,7 @@ it('can add a keyboard as an array', function () {
 
 it('can add a keyboard as a closure', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->chat(make_chat())
+        return $telegraph->chat(make_chat())
             ->html('foobar')
             ->keyboard(fn ($keyboard) => $keyboard->button('foo')->url('bar'));
     })->toMatchTelegramSnapshot();
@@ -34,7 +34,7 @@ it('can add a keyboard as a closure', function () {
 
 it('can replace the keyboard of a message', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph ->chat(make_chat())
+        return $telegraph->chat(make_chat())
             ->replaceKeyboard('123456', Keyboard::make()->buttons([
                 Button::make('foo')->url('bar'),
             ]));
diff --git a/tests/Unit/Concerns/SendsFilesTest.php b/tests/Unit/Concerns/SendsFilesTest.php
index d4ebd938..89f5bb50 100644
--- a/tests/Unit/Concerns/SendsFilesTest.php
+++ b/tests/Unit/Concerns/SendsFilesTest.php
@@ -11,7 +11,7 @@ use Illuminate\Support\Facades\Storage;
 
 it('can send a document', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->document(Storage::path('test.txt'));
+        return $telegraph->document(Storage::path('test.txt'));
     })->toMatchTelegramSnapshot();
 });
 
@@ -21,28 +21,28 @@ it('requires a chat to send a document', function () {
 
 it('can attach a document while writing a message', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->markdown('look at **this** file!')
+        return $telegraph->markdown('look at **this** file!')
             ->document(Storage::path('test.txt'));
     })->toMatchTelegramSnapshot();
 });
 
 it('can attach a document with markdown caption', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->document(Storage::path('test.txt'))
+        return $telegraph->document(Storage::path('test.txt'))
             ->markdown('look at **this** file!');
     })->toMatchTelegramSnapshot();
 });
 
 it('can attach a document with html caption', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->document(Storage::path('test.txt'))
+        return $telegraph->document(Storage::path('test.txt'))
             ->markdown('look at <b>this</b> file!');
     })->toMatchTelegramSnapshot();
 });
 
 it('can disable content type detection', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph
+        return $telegraph
             ->document(Storage::path('test.txt'))
             ->withoutContentTypeDetection();
     })->toMatchTelegramSnapshot();
@@ -50,28 +50,28 @@ it('can disable content type detection', function () {
 
 it('can disable notification', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->document(Storage::path('test.txt'))
+        return $telegraph->document(Storage::path('test.txt'))
             ->silent();
     })->toMatchTelegramSnapshot();
 });
 
 it('can protect content from sharing', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->document(Storage::path('test.txt'))
+        return $telegraph->document(Storage::path('test.txt'))
             ->protected();
     })->toMatchTelegramSnapshot();
 });
 
 it('can send a document replying to a message', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->document(Storage::path('test.txt'))
+        return $telegraph->document(Storage::path('test.txt'))
             ->reply(1234);
     })->toMatchTelegramSnapshot();
 });
 
 it('can attach a keyboard to a document', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph->document(Storage::path('test.txt'))
+        return $telegraph->document(Storage::path('test.txt'))
             ->keyboard(fn (Keyboard $keyboard) => $keyboard->button('def:studio')->url('https://defstudio.it'));
     })->toMatchTelegramSnapshot();
 });
@@ -105,7 +105,7 @@ test('documents are validated', function (string $path, bool $valid, string $exc
 
 it('can attach a thumbnail', function () {
     expect(function (Telegraph $telegraph) {
-        $telegraph
+        return $telegraph
             ->document(Storage::path('test.txt'))
             ->thumbnail(Storage::path('thumbnail.jpg'));
     })->toMatchTelegramSnapshot();