├── .github ├── coverage-badge.svg ├── images │ ├── blocks-with-header.png │ ├── section-with-button.png │ └── simple-text.png └── workflows │ ├── coverage.yml │ ├── develop-branch.yml │ ├── main-branch.yml │ ├── pull-request.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── bin ├── run-tests └── run-tests.bat ├── composer.json ├── composer.lock ├── config └── slack.php ├── examples ├── README.md ├── blocks-with-header.md ├── section-with-button.md └── simple-text.md ├── phpcs.xml ├── phpunit.xml ├── src ├── Channels │ └── SlackNotificationChannel.php ├── Contracts │ ├── Channels │ │ └── SlackNotificationChannelContract.php │ ├── Notifications │ │ └── SlackApiNotificationContract.php │ ├── PaginatorContract.php │ ├── Services │ │ ├── SlackCommandHandlerFactoryServiceContract.php │ │ └── SlackCommandHandlerServiceContract.php │ ├── SlackApiServiceContract.php │ ├── SlackCommandHandlerContract.php │ └── Support │ │ └── LayoutBuilder │ │ └── BuilderContract.php ├── Providers │ └── SlackBotServiceProvider.php ├── Services │ ├── SlackApiService.php │ ├── SlackCommandHandlerFactoryService.php │ └── SlackCommandHandlerService.php └── Support │ ├── LayoutBlocks │ ├── Block.php │ ├── Blocks │ │ ├── ActionsBlock.php │ │ ├── ContextBlock.php │ │ ├── DividerBlock.php │ │ ├── FileBlock.php │ │ ├── HeaderBlock.php │ │ ├── ImageBlock.php │ │ ├── InputBlock.php │ │ └── SectionBlock.php │ ├── Composition │ │ ├── ConfirmationDialogObject.php │ │ ├── DispatchActionConfigurationObject.php │ │ ├── FilterObject.php │ │ ├── MarkdownObject.php │ │ ├── OptionGroupObject.php │ │ ├── OptionObject.php │ │ └── TextObject.php │ ├── CompositionObject.php │ ├── Element.php │ ├── Elements │ │ ├── ButtonElement.php │ │ ├── CheckboxesElement.php │ │ ├── DatePickerElement.php │ │ ├── ImageElement.php │ │ ├── MultiSelect │ │ │ ├── MultiSelect.php │ │ │ ├── MultiSelectConversationElement.php │ │ │ ├── MultiSelectExternalElement.php │ │ │ ├── MultiSelectPublicChannelElement.php │ │ │ ├── MultiSelectStaticElement.php │ │ │ └── MultiSelectUserElement.php │ │ ├── OverflowMenuElement.php │ │ ├── PlainTextInputElement.php │ │ ├── RadioButtonGroupElement.php │ │ ├── SelectMenu │ │ │ ├── SelectMenu.php │ │ │ ├── SelectMenuConversationElement.php │ │ │ ├── SelectMenuExternalElement.php │ │ │ ├── SelectMenuPublicChannelElement.php │ │ │ ├── SelectMenuStaticElement.php │ │ │ └── SelectMenuUserElement.php │ │ └── TimePickerElement.php │ └── Traits │ │ ├── Composition │ │ └── WithFocusOnLoad.php │ │ ├── Elements │ │ ├── HasInitialOption.php │ │ ├── WithConfirmationDialog.php │ │ └── WithFilter.php │ │ ├── HasActionId.php │ │ ├── HasElements.php │ │ ├── MergesArrays.php │ │ └── MultiSelectElementCompatibility.php │ ├── LayoutBuilder │ └── Builder.php │ ├── Paginator.php │ ├── SlackCommandRequest.php │ └── SlackOptionsBuilder.php └── tests ├── TestCase.php ├── Traits ├── BasicMultiSelectTests.php └── BasicSelectMenuTests.php └── Unit ├── Channels └── SlackNotificationChannelTest.php ├── Services ├── SlackApiServiceTest.php ├── SlackCommandHandlerFactoryServiceTest.php └── SlackCommandHandlerServiceTest.php └── Support ├── LayoutBlocks ├── Blocks │ ├── ActionsBlockTest.php │ ├── ContextBlockTest.php │ ├── DividerBlockTest.php │ ├── FileBlockTest.php │ ├── HeaderBlockTest.php │ ├── ImageBlockTest.php │ ├── InputBlockTest.php │ └── SectionBlockTest.php ├── Composition │ ├── ConfirmationDialogObjectTest.php │ ├── DispatchActionConfigurationObjectTest.php │ ├── FilterObjectTest.php │ ├── MarkdownObjectTest.php │ ├── OptionGroupObjectTest.php │ ├── OptionObjectTest.php │ └── TextObjectTest.php └── Elements │ ├── ButtonElementTest.php │ ├── CheckboxesElementTest.php │ ├── DatePickerElementTest.php │ ├── ImageElementTest.php │ ├── MultiSelect │ ├── MultiSelectConversationElementTest.php │ ├── MultiSelectExternalElementTest.php │ ├── MultiSelectPublicChannelElementTest.php │ ├── MultiSelectStaticElementTest.php │ └── MultiSelectUserElementTest.php │ ├── OverflowMenuElementTest.php │ ├── PlainTextInputElementTest.php │ ├── RadioButtonGroupElementTest.php │ ├── SelectMenu │ ├── SelectMenuConversationElementTest.php │ ├── SelectMenuExternalElementTest.php │ ├── SelectMenuPublicChannelElementTest.php │ ├── SelectMenuStaticElementTest.php │ └── SelectMenuUserElementTest.php │ └── TimePickerElementTest.php ├── LayoutBuilder └── BuilderTest.php ├── PaginatorTest.php └── SlackOptionsBuilderTest.php /.github/coverage-badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | coverage 10 | coverage 11 | 12 | 13 | 96 % 14 | 96 % 15 | 16 | -------------------------------------------------------------------------------- /.github/images/blocks-with-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwilging/laravel-slack-bot/046f46e4cd896de75ec62b1b789c6de1962e3e9a/.github/images/blocks-with-header.png -------------------------------------------------------------------------------- /.github/images/section-with-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwilging/laravel-slack-bot/046f46e4cd896de75ec62b1b789c6de1962e3e9a/.github/images/section-with-button.png -------------------------------------------------------------------------------- /.github/images/simple-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwilging/laravel-slack-bot/046f46e4cd896de75ec62b1b789c6de1962e3e9a/.github/images/simple-text.png -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | on: 3 | workflow_call: 4 | 5 | jobs: 6 | run-coverage: 7 | name: Run Tests with Coverage 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v2 12 | with: 13 | fetch-depth: 0 14 | - name: Composer 15 | uses: php-actions/composer@v5 16 | with: 17 | php_extensions: xdebug 18 | - name: Run Tests 19 | env: 20 | XDEBUG_MODE: coverage 21 | run: vendor/bin/phpunit 22 | - name: Commit Coverage 23 | uses: timkrase/phpunit-coverage-badge@v1.2.0 24 | with: 25 | report: clover.xml 26 | report_type: clover 27 | coverage_badge_path: ./.github/coverage-badge.svg 28 | repo_token: ${{ secrets.GH_ACCESS_TOKEN }} 29 | push_badge: true 30 | -------------------------------------------------------------------------------- /.github/workflows/develop-branch.yml: -------------------------------------------------------------------------------- 1 | name: Develop Branch 2 | on: 3 | push: 4 | branches: 5 | - develop 6 | 7 | jobs: 8 | run-tests: 9 | name: Tests 10 | uses: ./.github/workflows/test.yml 11 | run-coverage: 12 | name: Coverage 13 | uses: ./.github/workflows/coverage.yml 14 | -------------------------------------------------------------------------------- /.github/workflows/main-branch.yml: -------------------------------------------------------------------------------- 1 | name: Main Branch 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | run-tests: 9 | name: Tests 10 | uses: ./.github/workflows/test.yml 11 | run-coverage: 12 | name: Coverage 13 | uses: ./.github/workflows/coverage.yml 14 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | on: 3 | pull_request: 4 | 5 | jobs: 6 | test: 7 | name: Run Tests 8 | uses: ./.github/workflows/test.yml 9 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | workflow_call: 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | php: ["7.4", "8.0", "8.1"] 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: Composer 17 | uses: php-actions/composer@v5 18 | with: 19 | php_version: ${{ matrix.php }} 20 | - name: Run Tests 21 | run: vendor/bin/phpunit --no-coverage 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | .env 3 | .phpunit.result.cache 4 | tests/html-coverage 5 | clover.xml 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 nwilging 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/run-tests: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | vendor/bin/phpunit 4 | -------------------------------------------------------------------------------- /bin/run-tests.bat: -------------------------------------------------------------------------------- 1 | vendor\bin\phpunit.bat -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nwilging/laravel-slack-bot", 3 | "description": "A robust Slack messaging integration for Laravel", 4 | "type": "library", 5 | "license": "MIT", 6 | "autoload": { 7 | "psr-4": { 8 | "Nwilging\\LaravelSlackBot\\": "src/", 9 | "Nwilging\\LaravelSlackBotTests\\": "tests/" 10 | } 11 | }, 12 | "authors": [ 13 | { 14 | "name": "Nicole Wilging", 15 | "email": "nicole@wilging.com" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=7.4", 20 | "laravel/framework": ">=8", 21 | "guzzlehttp/guzzle": "^7.4" 22 | }, 23 | "extra": { 24 | "laravel": { 25 | "providers": [ 26 | "Nwilging\\LaravelSlackBot\\Providers\\SlackBotServiceProvider" 27 | ] 28 | } 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "^9.5", 32 | "mockery/mockery": "^1.5", 33 | "squizlabs/php_codesniffer": "^3.6", 34 | "dealerinspire/laravel-coding-standard": "^2.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /config/slack.php: -------------------------------------------------------------------------------- 1 | env('SLACK_API_BOT_TOKEN'), 6 | 'api_url' => env('SLACK_API_URL', 'https://slack.com/api'), 7 | 'driver_name' => env('SLACK_API_DRIVER_NAME', 'slack'), 8 | 'signing_secret' => env('SLACK_API_SIGNING_SECRET'), 9 | ]; 10 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Examples of different types of messages and how to configure them in your 4 | Laravel notification. 5 | 6 | --- 7 | 8 | ### Simple Text 9 | 10 | ![Simple Text](../.github/images/simple-text.png) 11 | 12 | [Example](./simple-text.md) 13 | 14 | ### Blocks Message with Header 15 | 16 | ![Blocks with Header](../.github/images/blocks-with-header.png) 17 | 18 | [Example](./blocks-with-header.md) 19 | 20 | ### Section with Button Accessory 21 | 22 | ![Section with Button](../.github/images/section-with-button.png) 23 | 24 | [Example](./section-with-button.md) 25 | -------------------------------------------------------------------------------- /examples/blocks-with-header.md: -------------------------------------------------------------------------------- 1 | # Blocks Message with Header 2 | 3 | ![Message Example](../.github/images/blocks-with-header.png) 4 | 5 | Code: 6 | ```phpt 7 | username('My Bot') 37 | ->iconEmoji(':smile:'); 38 | 39 | $layoutBuilder = new Builder(); 40 | 41 | $sectionBlock = new SectionBlock(); 42 | $sectionBlock->withFields([ 43 | $layoutBuilder->withPlainText('Some text in a section'), 44 | $layoutBuilder->withPlainText('More text!'), 45 | ]); 46 | 47 | $layoutBuilder 48 | ->header('Test Message') 49 | ->divider() 50 | ->addBlock($sectionBlock); 51 | 52 | return [ 53 | 'contentType' => 'blocks', 54 | 'blocks' => $layoutBuilder->getBlocks(), 55 | 'channelId' => 'general', 56 | 'options' => $options->toArray(), 57 | ]; 58 | } 59 | } 60 | ``` -------------------------------------------------------------------------------- /examples/section-with-button.md: -------------------------------------------------------------------------------- 1 | # Section with Button Accessory 2 | 3 | ![Message Example](../.github/images/section-with-button.png) 4 | 5 | Code: 6 | ```phpt 7 | username('My Bot') 38 | ->iconEmoji(':smile:'); 39 | 40 | $layoutBuilder = new Builder(); 41 | 42 | $sectionBlock = new SectionBlock(); 43 | $sectionBlock->withFields([ 44 | $layoutBuilder->withPlainText('Some text in a section'), 45 | ]); 46 | 47 | $accessoryButton = new ButtonElement($layoutBuilder->withPlainText('A Button'), 'actionId'); 48 | $accessoryButton->primary(); 49 | 50 | $sectionBlock->withAccessory($accessoryButton); 51 | 52 | $layoutBuilder 53 | ->header('Test Message') 54 | ->divider() 55 | ->addBlock($sectionBlock); 56 | 57 | return [ 58 | 'contentType' => 'blocks', 59 | 'blocks' => $layoutBuilder->getBlocks(), 60 | 'channelId' => 'general', 61 | 'options' => $options->toArray(), 62 | ]; 63 | } 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /examples/simple-text.md: -------------------------------------------------------------------------------- 1 | # Simple Text Message 2 | 3 | ![Message Example](../.github/images/simple-text.png) 4 | 5 | Code: 6 | ```phpt 7 | username('My Bot') 35 | ->iconEmoji(':smile:'); 36 | 37 | return [ 38 | 'contentType' => 'text', 39 | 'message' => 'This is a test message!', 40 | 'channelId' => 'general', 41 | 'options' => $options->toArray(), 42 | ]; 43 | } 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The coding standard for PHP_CodeSniffer itself. 4 | 5 | src 6 | tests 7 | 8 | */src/Standards/*/Tests/*\.(inc|css|js)$ 9 | */tests/Core/*/*\.(inc|css|js)$ 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests/Unit 10 | 11 | 12 | 13 | 14 | ./src 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Channels/SlackNotificationChannel.php: -------------------------------------------------------------------------------- 1 | slackApiService = $slackApiService; 19 | } 20 | 21 | /** 22 | * @param Notifiable $notifiable 23 | * @param SlackApiNotificationContract $notification 24 | * @return void 25 | */ 26 | public function send($notifiable, SlackApiNotificationContract $notification): void 27 | { 28 | $slackNotificationArray = $notification->toSlackArray($notifiable); 29 | switch ($slackNotificationArray['contentType']) { 30 | case 'text': 31 | $this->handleTextMessage($slackNotificationArray); 32 | break; 33 | case 'blocks': 34 | $this->handleBlocksMessage($slackNotificationArray); 35 | break; 36 | default: 37 | throw new \InvalidArgumentException(sprintf('%s is not a valid content type. Use `blocks` or `text`', $slackNotificationArray['contentType'])); 38 | } 39 | } 40 | 41 | protected function handleBlocksMessage(array $slackNotificationArray): void 42 | { 43 | $blocks = $slackNotificationArray['blocks']; 44 | $options = $slackNotificationArray['options'] ?? []; 45 | 46 | $this->slackApiService->sendBlocksMessage($slackNotificationArray['channelId'], $blocks, $options); 47 | } 48 | 49 | protected function handleTextMessage(array $slackNotificationArray): void 50 | { 51 | $message = $slackNotificationArray['message']; 52 | $options = $slackNotificationArray['options'] ?? []; 53 | 54 | $this->slackApiService->sendTextMessage($slackNotificationArray['channelId'], $message, $options); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Contracts/Channels/SlackNotificationChannelContract.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__ . '/../../config/slack.php', 'slack'); 28 | } 29 | 30 | public function register() 31 | { 32 | Notification::resolved(function (ChannelManager $channelManager): void { 33 | $driverName = $this->app->make(Config::class)->get('slack.driver_name'); 34 | $channelManager->extend($driverName, function (): SlackNotificationChannelContract { 35 | return $this->app->make(SlackNotificationChannelContract::class); 36 | }); 37 | }); 38 | 39 | $this->app->bind(ClientInterface::class, Client::class); 40 | $this->app->bind(SlackApiServiceContract::class, SlackApiService::class); 41 | 42 | $this->app->when(SlackApiService::class)->needs('$botToken')->give(function (): string { 43 | return $this->app->make(Config::class)->get('slack.bot_token'); 44 | }); 45 | 46 | $this->app->when(SlackApiService::class)->needs('$apiUrl')->give(function (): string { 47 | return $this->app->make(Config::class)->get('slack.api_url'); 48 | }); 49 | 50 | $this->app->bind(BuilderContract::class, Builder::class); 51 | 52 | $this->app->bind(SlackNotificationChannelContract::class, SlackNotificationChannel::class); 53 | 54 | $this->app->singleton(SlackCommandHandlerFactoryServiceContract::class, SlackCommandHandlerFactoryService::class); 55 | 56 | $this->app->bind(SlackCommandHandlerServiceContract::class, SlackCommandHandlerService::class); 57 | $this->app->when(SlackCommandHandlerService::class)->needs('$signingSecret')->give(function (): string { 58 | return $this->app->make(Config::class)->get('slack.signing_secret'); 59 | }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Services/SlackCommandHandlerFactoryService.php: -------------------------------------------------------------------------------- 1 | laravel = $laravel; 19 | } 20 | 21 | public function register(string $slackCommandHandlerClass, string $slackSlashCommand): void 22 | { 23 | $this->commandHandlers[$slackSlashCommand] = $slackCommandHandlerClass; 24 | } 25 | 26 | public function getHandler(string $slackSlashCommand): SlackCommandHandlerContract 27 | { 28 | if (!array_key_exists($slackSlashCommand, $this->commandHandlers)) { 29 | throw new \InvalidArgumentException(sprintf('No handler found for command %s', $slackSlashCommand)); 30 | } 31 | 32 | return $this->laravel->make($this->commandHandlers[$slackSlashCommand]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Services/SlackCommandHandlerService.php: -------------------------------------------------------------------------------- 1 | handlerFactory = $handlerFactory; 22 | $this->signingSecret = $signingSecret; 23 | } 24 | 25 | public function handle(Request $request): Response 26 | { 27 | $data = ($request->isJson()) ? $request->json()->all() : $request->toArray(); 28 | $this->validateSignature($request, $data); 29 | 30 | $command = $this->generateCommandRequest($data); 31 | 32 | $handler = $this->handlerFactory->getHandler($command->command); 33 | return $handler->handle($command); 34 | } 35 | 36 | protected function generateCommandRequest(array $data): SlackCommandRequest 37 | { 38 | $request = new SlackCommandRequest(); 39 | 40 | $request->token = $data['token']; 41 | $request->teamId = $data['team_id']; 42 | $request->teamDomain = $data['team_domain']; 43 | $request->channelId = $data['channel_id']; 44 | $request->channelName = $data['channel_name']; 45 | $request->userId = $data['user_id']; 46 | $request->username = $data['user_name']; 47 | $request->command = ltrim($data['command'], '/'); 48 | $request->commandArgs = explode(' ', $data['text'] ?? ''); 49 | $request->apiAppId = $data['api_app_id']; 50 | $request->isEnterpriseInstall = ($data['is_enterprise_install'] === 'true'); 51 | $request->responseUrl = $data['response_url']; 52 | $request->triggerId = $data['trigger_id']; 53 | 54 | return $request; 55 | } 56 | 57 | protected function validateSignature(Request $request, array $data): void 58 | { 59 | $timestamp = $request->header('X-Slack-Request-Timestamp'); 60 | $slackSignature = $request->header('X-Slack-Signature'); 61 | if (!$timestamp || !$slackSignature) { 62 | throw new UnauthorizedException(); 63 | } 64 | 65 | $formattedRequestBody = implode('&', array_map(function (string $key) use ($data): string { 66 | return sprintf('%s=%s', urlencode($key), urlencode($data[$key] ?? '')); 67 | }, array_keys($data))); 68 | 69 | $signatureBase = sprintf('v0:%s:%s', $timestamp, $formattedRequestBody); 70 | $signature = sprintf('v0=%s', hash_hmac('sha256', $signatureBase, $this->signingSecret)); 71 | 72 | if (!hash_equals($signature, $slackSignature)) { 73 | throw new UnauthorizedException(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Block.php: -------------------------------------------------------------------------------- 1 | blockId = $blockId; 29 | } 30 | 31 | /** 32 | * The Slack-API compatible type of this block 33 | * 34 | * @return string 35 | */ 36 | public abstract function getType(): string; 37 | 38 | /** 39 | * The Slack-API compatible array for this block 40 | * 41 | * @return array 42 | */ 43 | public function toArray(): array 44 | { 45 | return array_filter([ 46 | 'type' => $this->getType(), 47 | 'block_id' => $this->blockId, 48 | ]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Blocks/ActionsBlock.php: -------------------------------------------------------------------------------- 1 | elements = $elements; 22 | } 23 | 24 | public function getType(): string 25 | { 26 | return static::TYPE_ACTIONS; 27 | } 28 | 29 | public function toArray(): array 30 | { 31 | return $this->toMergedArray([ 32 | 'elements' => $this->elementsArray(), 33 | 'block_id' => $this->blockId, 34 | ]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Blocks/ContextBlock.php: -------------------------------------------------------------------------------- 1 | textObjects = $textObjects; 35 | $this->elements = $elements; 36 | } 37 | 38 | public function getType(): string 39 | { 40 | return static::TYPE_CONTEXT; 41 | } 42 | 43 | public function toArray(): array 44 | { 45 | return $this->toMergedArray([ 46 | 'elements' => array_merge($this->elementsArray(), array_map(function (TextObject $textObject): array { 47 | return $textObject->toArray(); 48 | }, $this->textObjects)), 49 | 'block_id' => $this->blockId, 50 | ]); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Blocks/DividerBlock.php: -------------------------------------------------------------------------------- 1 | externalId = $externalId; 23 | } 24 | 25 | public function getType(): string 26 | { 27 | return static::TYPE_FILE; 28 | } 29 | 30 | public function toArray(): array 31 | { 32 | return $this->toMergedArray([ 33 | 'external_id' => $this->externalId, 34 | /** 35 | * At the moment, source will always be remote for a remote file. 36 | * @see https://api.slack.com/reference/block-kit/blocks#file_fields 37 | */ 38 | 'source' => 'remote', 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Blocks/HeaderBlock.php: -------------------------------------------------------------------------------- 1 | textObject = $textObject; 24 | } 25 | 26 | public function getType(): string 27 | { 28 | return static::TYPE_HEADER; 29 | } 30 | 31 | public function toArray(): array 32 | { 33 | return $this->toMergedArray([ 34 | 'text' => $this->textObject->toArray(), 35 | ]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Blocks/ImageBlock.php: -------------------------------------------------------------------------------- 1 | imageUrl = $imageUrl; 29 | $this->altText = $altText; 30 | } 31 | 32 | /** 33 | * An optional title for the image. Maximum length for the text in this field is 2000 characters. 34 | * 35 | * @see https://api.slack.com/reference/block-kit/blocks#image_fields 36 | * 37 | * @param TextObject $textObject 38 | * @return $this 39 | */ 40 | public function withTitle(TextObject $textObject): self 41 | { 42 | $this->title = $textObject; 43 | return $this; 44 | } 45 | 46 | public function getType(): string 47 | { 48 | return static::TYPE_IMAGE; 49 | } 50 | 51 | public function toArray(): array 52 | { 53 | return $this->toMergedArray([ 54 | 'image_url' => $this->imageUrl, 55 | 'alt_text' => $this->altText, 56 | 'title' => ($this->title) ? $this->title->toArray() : null, 57 | ]); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Blocks/InputBlock.php: -------------------------------------------------------------------------------- 1 | label = $label; 34 | $this->element = $element; 35 | } 36 | 37 | public function dispatchAction(bool $dispatchAction = true): self 38 | { 39 | $this->dispatchAction = $dispatchAction; 40 | return $this; 41 | } 42 | 43 | public function withHint(TextObject $text): self 44 | { 45 | $this->hint = $text; 46 | return $this; 47 | } 48 | 49 | public function optional(bool $optional = true): self 50 | { 51 | $this->optional = $optional; 52 | return $this; 53 | } 54 | 55 | public function getType(): string 56 | { 57 | return static::TYPE_INPUT; 58 | } 59 | 60 | public function toArray(): array 61 | { 62 | return $this->toMergedArray([ 63 | 'label' => $this->label->toArray(), 64 | 'element' => $this->element->toArray(), 65 | 'hint' => ($this->hint) ? $this->hint->toArray() : null, 66 | 'dispatch_action' => $this->dispatchAction, 67 | 'optional' => $this->optional, 68 | ]); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Blocks/SectionBlock.php: -------------------------------------------------------------------------------- 1 | text = $text; 45 | return $this; 46 | } 47 | 48 | /** 49 | * Required if no text is provided. An array of text objects. 50 | * Any text objects included with fields will be rendered in a compact format that allows for 2 columns of 51 | * side-by-side text. Maximum number of items is 10. Maximum length for the text in each item is 2000 characters. 52 | * 53 | * @see https://api.slack.com/reference/block-kit/blocks#section_fields 54 | * 55 | * @param TextObject[] $fields 56 | * @return $this 57 | */ 58 | public function withFields(array $fields): self 59 | { 60 | $this->fields = $fields; 61 | return $this; 62 | } 63 | 64 | /** 65 | * A Block Element to add as an accessory to this section 66 | * 67 | * @see https://api.slack.com/reference/block-kit/blocks#section_fields 68 | * 69 | * @param Element $accessory 70 | * @return $this 71 | */ 72 | public function withAccessory(Element $accessory): self 73 | { 74 | $this->accessory = $accessory; 75 | return $this; 76 | } 77 | 78 | public function getType(): string 79 | { 80 | return static::TYPE_SECTION; 81 | } 82 | 83 | public function toArray(): array 84 | { 85 | $data = [ 86 | 'accessory' => ($this->accessory) ? $this->accessory->toArray() : null, 87 | ]; 88 | 89 | if (!$this->fields) { 90 | $data['text'] = $this->text->toArray(); 91 | } else { 92 | $data['fields'] = array_map(function (TextObject $textObject): array { 93 | return $textObject->toArray(); 94 | }, $this->fields); 95 | } 96 | 97 | return $this->toMergedArray($data); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Composition/ConfirmationDialogObject.php: -------------------------------------------------------------------------------- 1 | title = $title; 30 | $this->text = $text; 31 | $this->confirmText = $confirmText; 32 | $this->denyText = $denyText; 33 | } 34 | 35 | /** 36 | * Defines the color scheme applied to the confirm button. 37 | * A value of primary will display the button with a green background on desktop, or blue text on mobile. 38 | * 39 | * @see https://api.slack.com/reference/block-kit/composition-objects#confirm__fields 40 | * 41 | * @return $this 42 | */ 43 | public function confirmButtonPrimary(): self 44 | { 45 | $this->confirmButtonStyle = 'primary'; 46 | return $this; 47 | } 48 | 49 | /** 50 | * Defines the color scheme applied to the confirm button. 51 | * A value of danger will display the button with a red background on desktop, or red text on mobile. 52 | * 53 | * @see https://api.slack.com/reference/block-kit/composition-objects#confirm__fields 54 | * 55 | * @return $this 56 | */ 57 | public function confirmButtonDanger(): self 58 | { 59 | $this->confirmButtonStyle = 'danger'; 60 | return $this; 61 | } 62 | 63 | public function toArray(): array 64 | { 65 | return $this->toMergedArray([ 66 | 'title' => $this->title->toArray(), 67 | 'text' => $this->text->toArray(), 68 | 'confirm' => $this->confirmText->toArray(), 69 | 'deny' => $this->denyText->toArray(), 70 | 'style' => $this->confirmButtonStyle, 71 | ]); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Composition/DispatchActionConfigurationObject.php: -------------------------------------------------------------------------------- 1 | triggerActionsOn[] = 'on_enter_pressed'; 33 | return $this; 34 | } 35 | 36 | /** 37 | * Payload is dispatched when a character is entered (or removed) in the input. 38 | * 39 | * @see https://api.slack.com/reference/block-kit/composition-objects#dispatch_action_config__fields 40 | * 41 | * @return $this 42 | */ 43 | public function onCharacterEntered(): self 44 | { 45 | $this->triggerActionsOn[] = 'on_character_entered'; 46 | return $this; 47 | } 48 | 49 | public function toArray(): array 50 | { 51 | return $this->toMergedArray([ 52 | 'trigger_actions_on' => !empty($this->triggerActionsOn) ? $this->triggerActionsOn : null, 53 | ]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Composition/FilterObject.php: -------------------------------------------------------------------------------- 1 | include = $include; 39 | return $this; 40 | } 41 | 42 | /** 43 | * Indicates whether to exclude external shared channels from conversation lists. 44 | * 45 | * @see https://api.slack.com/reference/block-kit/composition-objects#filter_conversations__fields 46 | * 47 | * @param bool $exclude 48 | * @return $this 49 | */ 50 | public function excludeExternalSharedChannels(bool $exclude = true): self 51 | { 52 | $this->excludeExternalSharedChannels = $exclude; 53 | return $this; 54 | } 55 | 56 | /** 57 | * Indicates whether to exclude bot users from conversation lists. 58 | * 59 | * @see https://api.slack.com/reference/block-kit/composition-objects#filter_conversations__fields 60 | * 61 | * @param bool $exclude 62 | * @return $this 63 | */ 64 | public function excludeBotUsers(bool $exclude = true): self 65 | { 66 | $this->excludeBotUsers = $exclude; 67 | return $this; 68 | } 69 | 70 | public function toArray(): array 71 | { 72 | return $this->toMergedArray([ 73 | 'include' => $this->include, 74 | 'exclude_external_shared_channels' => $this->excludeExternalSharedChannels, 75 | 'exclude_bot_users' => $this->excludeBotUsers, 76 | ]); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Composition/MarkdownObject.php: -------------------------------------------------------------------------------- 1 | label = $label; 31 | $this->options = $options; 32 | } 33 | 34 | public function toArray(): array 35 | { 36 | return $this->toMergedArray([ 37 | 'label' => $this->label->toArray(), 38 | 'options' => array_map(function (OptionObject $option): array { 39 | return $option->toArray(); 40 | }, $this->options), 41 | ]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Composition/OptionObject.php: -------------------------------------------------------------------------------- 1 | text = $text; 28 | $this->value = $value; 29 | } 30 | 31 | /** 32 | * A plain_text only text object that defines a line of descriptive text shown below the text field beside the 33 | * radio button. Maximum length for the text object within this field is 75 characters. 34 | * 35 | * @see https://api.slack.com/reference/block-kit/composition-objects#option__fields 36 | * 37 | * @param TextObject $textObject 38 | * @return $this 39 | */ 40 | public function withDescription(TextObject $textObject): self 41 | { 42 | $this->description = $textObject; 43 | return $this; 44 | } 45 | 46 | /** 47 | * A URL to load in the user's browser when the option is clicked. 48 | * The url attribute is only available in overflow menus. Maximum length for this field is 3000 characters. 49 | * 50 | * @see https://api.slack.com/reference/block-kit/composition-objects#option__fields 51 | * 52 | * @param string $url 53 | * @return $this 54 | */ 55 | public function withUrl(string $url): self 56 | { 57 | $this->url = $url; 58 | return $this; 59 | } 60 | 61 | public function toArray(): array 62 | { 63 | return $this->toMergedArray([ 64 | 'text' => $this->text, 65 | 'value' => $this->value, 66 | 'description' => ($this->description) ? $this->description->toArray() : null, 67 | 'url' => $this->url, 68 | ]); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Composition/TextObject.php: -------------------------------------------------------------------------------- 1 | text = $text; 28 | } 29 | 30 | /** 31 | * Indicates whether emojis in a text field should be escaped into the colon emoji format. 32 | * This field is only usable when type is plain_text. 33 | * 34 | * @see https://api.slack.com/reference/block-kit/composition-objects#text__fields 35 | * 36 | * @return $this 37 | */ 38 | public function escapeEmojis(): self 39 | { 40 | $this->emoji = true; 41 | return $this; 42 | } 43 | 44 | /** 45 | * When set to false (as is default) URLs will be auto-converted into links, conversation names will be link-ified, 46 | * and certain mentions will be automatically parsed. Using a value of true will skip any preprocessing of this 47 | * nature, although you can still include manual parsing strings. 48 | * This field is only usable when type is mrkdwn. 49 | * 50 | * @see https://api.slack.com/reference/block-kit/composition-objects#text__fields 51 | * 52 | * @return $this 53 | */ 54 | public function verbatim(): self 55 | { 56 | if ($this->type !== 'mrkdwn') return $this; 57 | 58 | $this->verbatim = true; 59 | return $this; 60 | } 61 | 62 | public function toArray(): array 63 | { 64 | return $this->toMergedArray([ 65 | 'type' => $this->type, 66 | 'text' => $this->text, 67 | 'emoji' => $this->emoji, 68 | 'verbatim' => $this->verbatim, 69 | ]); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/CompositionObject.php: -------------------------------------------------------------------------------- 1 | $this->getType(), 56 | ]; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/ButtonElement.php: -------------------------------------------------------------------------------- 1 | text = $text; 34 | $this->actionId = $actionId; 35 | } 36 | 37 | /** 38 | * Sets the button style to `primary` 39 | * @see https://api.slack.com/reference/block-kit/block-elements#button__fields 40 | * 41 | * @return $this 42 | */ 43 | public function primary(): self 44 | { 45 | $this->style = 'primary'; 46 | return $this; 47 | } 48 | 49 | /** 50 | * Sets the button style to `danger` 51 | * 52 | * @see https://api.slack.com/reference/block-kit/block-elements#button__fields 53 | * 54 | * @return $this 55 | */ 56 | public function danger(): self 57 | { 58 | $this->style = 'danger'; 59 | return $this; 60 | } 61 | 62 | public function getType(): string 63 | { 64 | return static::TYPE_BUTTON; 65 | } 66 | 67 | /** 68 | * A URL to load in the user's browser when the button is clicked. 69 | * Maximum length for this field is 3000 characters. 70 | * 71 | * @see https://api.slack.com/reference/block-kit/block-elements#button__fields 72 | * 73 | * @return $this 74 | */ 75 | public function withUrl(string $url, ?string $value = null): self 76 | { 77 | $this->url = $url; 78 | $this->value = $value; 79 | return $this; 80 | } 81 | 82 | /** 83 | * A label for longer descriptive text about a button element. 84 | * This label will be read out by screen readers instead of the button text object. 85 | * Maximum length for this field is 75 characters. 86 | * 87 | * @see https://api.slack.com/reference/block-kit/block-elements#button__fields 88 | * 89 | * @return $this 90 | */ 91 | public function withAccessibilityLabel(string $accessibilityLabel): self 92 | { 93 | $this->accessibilityLabel = $accessibilityLabel; 94 | return $this; 95 | } 96 | 97 | public function compatibleWith(): array 98 | { 99 | return [ 100 | Block::TYPE_ACTIONS, 101 | Block::TYPE_SECTION, 102 | ]; 103 | } 104 | 105 | public function toArray(): array 106 | { 107 | return $this->toMergedArray($this->mergeConfirmationDialog([ 108 | 'text' => $this->text->toArray(), 109 | 'action_id' => $this->actionId, 110 | 'style' => $this->style, 111 | 'url' => $this->url, 112 | 'value' => $this->value, 113 | 'accessibility_label' => $this->accessibilityLabel, 114 | ])); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/CheckboxesElement.php: -------------------------------------------------------------------------------- 1 | actionId = $actionId; 40 | $this->options = $options; 41 | } 42 | 43 | /** 44 | * An array of option objects that exactly matches one or more of the options within options. 45 | * These options will be selected when the checkbox group initially loads. 46 | * 47 | * @see https://api.slack.com/reference/block-kit/block-elements#checkboxes__fields 48 | * 49 | * @param OptionObject[] $options 50 | * @return $this 51 | */ 52 | public function withInitialOptions(array $options): self 53 | { 54 | $this->initialOptions = $options; 55 | return $this; 56 | } 57 | 58 | public function getType(): string 59 | { 60 | return static::TYPE_CHECKBOXES; 61 | } 62 | 63 | public function compatibleWith(): array 64 | { 65 | return [ 66 | Block::TYPE_ACTIONS, 67 | Block::TYPE_SECTION, 68 | ]; 69 | } 70 | 71 | public function toArray(): array 72 | { 73 | return $this->toMergedArray($this->mergeConfirmationDialog([ 74 | 'action_id' => $this->actionId, 75 | 'options' => array_map(function (OptionObject $option): array { 76 | return $option->toArray(); 77 | }, $this->options), 78 | 'initial_options' => ($this->initialOptions) ? array_map(function (OptionObject $option): array { 79 | return $option->toArray(); 80 | }, $this->initialOptions) : null, 81 | 'focus_on_load' => $this->focusOnLoad, 82 | ])); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/DatePickerElement.php: -------------------------------------------------------------------------------- 1 | actionId = $actionId; 36 | $this->initialDate = $initialDate; 37 | } 38 | 39 | /** 40 | * A plain_text only text object that defines the placeholder text shown on the datepicker. 41 | * Maximum length for the text in this field is 150 characters. 42 | * 43 | * @see https://api.slack.com/reference/block-kit/block-elements#datepicker__fields 44 | * 45 | * @param TextObject $textObject 46 | * @return $this 47 | */ 48 | public function withPlaceholder(TextObject $textObject): self 49 | { 50 | $this->placeholder = $textObject; 51 | return $this; 52 | } 53 | 54 | public function getType(): string 55 | { 56 | return static::TYPE_DATEPICKER; 57 | } 58 | 59 | public function compatibleWith(): array 60 | { 61 | return [ 62 | Block::TYPE_SECTION, 63 | Block::TYPE_ACTIONS, 64 | ]; 65 | } 66 | 67 | public function toArray(): array 68 | { 69 | return $this->toMergedArray($this->mergeConfirmationDialog([ 70 | 'action_id' => $this->actionId, 71 | 'initial_date' => $this->initialDate, 72 | 'placeholder' => ($this->placeholder) ? $this->placeholder->toArray() : null, 73 | 'focus_on_load' => $this->focusOnLoad, 74 | ])); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/ImageElement.php: -------------------------------------------------------------------------------- 1 | imageUrl = $imageUrl; 25 | $this->altText = $altText; 26 | } 27 | 28 | public function getType(): string 29 | { 30 | return Element::TYPE_IMAGE; 31 | } 32 | 33 | public function compatibleWith(): array 34 | { 35 | return [ 36 | Block::TYPE_SECTION, 37 | Block::TYPE_CONTEXT, 38 | ]; 39 | } 40 | 41 | public function toArray(): array 42 | { 43 | return $this->toMergedArray([ 44 | 'image_url' => $this->imageUrl, 45 | 'alt_text' => $this->altText, 46 | ]); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/MultiSelect/MultiSelect.php: -------------------------------------------------------------------------------- 1 | actionId = $actionId; 26 | $this->placeholder = $placeholder; 27 | } 28 | 29 | public function maxSelectedItems(int $maxSelectedItems): self 30 | { 31 | $this->maxSelectedItems = $maxSelectedItems; 32 | return $this; 33 | } 34 | 35 | public function toArray(): array 36 | { 37 | return $this->mergeConfirmationDialog([ 38 | 'type' => $this->getType(), 39 | 'action_id' => $this->actionId, 40 | 'placeholder' => $this->placeholder->toArray(), 41 | 'focus_on_load' => $this->focusOnLoad, 42 | 'max_selected_items' => $this->maxSelectedItems, 43 | ]); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectConversationElement.php: -------------------------------------------------------------------------------- 1 | defaultToCurrent = $defaultToCurrent; 40 | return $this; 41 | } 42 | 43 | /** 44 | * An array of one or more IDs of any valid conversations to be pre-selected when the menu loads. 45 | * If defaultToCurrent is also supplied, initial conversations will be ignored. 46 | * 47 | * @see https://api.slack.com/reference/block-kit/block-elements#conversation_multi_select__fields 48 | * 49 | * @param string[] $initialConversations 50 | * @return $this 51 | */ 52 | public function withInitialConversations(array $initialConversations): self 53 | { 54 | $this->initialConversations = $initialConversations; 55 | return $this; 56 | } 57 | 58 | public function getType(): string 59 | { 60 | return static::TYPE_MULTI_SELECT_CONVERSATIONS; 61 | } 62 | 63 | public function compatibleWith(): array 64 | { 65 | return [ 66 | Block::TYPE_SECTION, 67 | ]; 68 | } 69 | 70 | public function toArray(): array 71 | { 72 | return $this->toMergedArray($this->mergeFilter([ 73 | 'initial_conversations' => $this->initialConversations, 74 | 'default_to_current_conversation' => $this->defaultToCurrent, 75 | ])); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectExternalElement.php: -------------------------------------------------------------------------------- 1 | minQueryLength = $minQueryLength; 42 | return $this; 43 | } 44 | 45 | /** 46 | * An array of options that exactly match one or more of the options within options. 47 | * These options will be selected when the menu initially loads. 48 | * 49 | * @see https://api.slack.com/reference/block-kit/block-elements#external_multi_select__fields 50 | * 51 | * @param OptionObject[] $options 52 | * @return $this 53 | */ 54 | public function withInitialOptions(array $options): self 55 | { 56 | $this->initialOptions = $options; 57 | return $this; 58 | } 59 | 60 | public function getType(): string 61 | { 62 | return Element::TYPE_MULTI_SELECT_EXTERNAL; 63 | } 64 | 65 | public function compatibleWith(): array 66 | { 67 | return [ 68 | Block::TYPE_SECTION, 69 | ]; 70 | } 71 | 72 | public function toArray(): array 73 | { 74 | return $this->toMergedArray([ 75 | 'min_query_length' => $this->minQueryLength, 76 | 'initial_options' => ($this->initialOptions) ? array_map(function (OptionObject $option): array { 77 | return $option->toArray(); 78 | }, $this->initialOptions) : null, 79 | ]); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectPublicChannelElement.php: -------------------------------------------------------------------------------- 1 | initialChannels = $initialChannels; 36 | return $this; 37 | } 38 | 39 | public function getType(): string 40 | { 41 | return static::TYPE_MULTI_SELECT_CHANNELS; 42 | } 43 | 44 | public function compatibleWith(): array 45 | { 46 | return [ 47 | Block::TYPE_SECTION, 48 | ]; 49 | } 50 | 51 | public function toArray(): array 52 | { 53 | return $this->toMergedArray([ 54 | 'initial_channels' => $this->initialChannels, 55 | ]); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectStaticElement.php: -------------------------------------------------------------------------------- 1 | options = $options; 40 | } 41 | 42 | /** 43 | * An array of options that exactly match one or more of the options within options. 44 | * These options will be selected when the menu initially loads. 45 | * 46 | * @see https://api.slack.com/reference/block-kit/block-elements#static_multi_select__fields 47 | * 48 | * @param OptionObject[] $options 49 | * @return $this 50 | */ 51 | public function withInitialOptions(array $options): self 52 | { 53 | $this->initialOptions = $options; 54 | return $this; 55 | } 56 | 57 | public function getType(): string 58 | { 59 | return static::TYPE_MULTI_SELECT_STATIC; 60 | } 61 | 62 | public function toArray(): array 63 | { 64 | return $this->toMergedArray([ 65 | 'options' => array_map(function (OptionObject $option): array { 66 | return $option->toArray(); 67 | }, $this->options), 68 | 'initial_options' => ($this->initialOptions) ? array_map(function (OptionObject $option): array { 69 | return $option->toArray(); 70 | }, $this->initialOptions) : null, 71 | ]); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectUserElement.php: -------------------------------------------------------------------------------- 1 | initialUsers = $initialUsers; 36 | return $this; 37 | } 38 | 39 | public function getType(): string 40 | { 41 | return static::TYPE_MULTI_SELECT_USERS; 42 | } 43 | 44 | public function compatibleWith(): array 45 | { 46 | return [ 47 | Block::TYPE_SECTION, 48 | ]; 49 | } 50 | 51 | public function toArray(): array 52 | { 53 | return $this->toMergedArray([ 54 | 'initial_users' => $this->initialUsers, 55 | ]); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/OverflowMenuElement.php: -------------------------------------------------------------------------------- 1 | actionId = $actionId; 29 | $this->options = $options; 30 | } 31 | 32 | public function getType(): string 33 | { 34 | return static::TYPE_OVERFLOW_MENU; 35 | } 36 | 37 | public function compatibleWith(): array 38 | { 39 | return [ 40 | Block::TYPE_SECTION, 41 | Block::TYPE_ACTIONS, 42 | ]; 43 | } 44 | 45 | public function toArray(): array 46 | { 47 | return $this->toMergedArray($this->mergeConfirmationDialog([ 48 | 'action_id' => $this->actionId, 49 | 'options' => array_map(function (OptionObject $option): array { 50 | return $option->toArray(); 51 | }, $this->options), 52 | ])); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/PlainTextInputElement.php: -------------------------------------------------------------------------------- 1 | actionId = $actionId; 34 | } 35 | 36 | /** 37 | * The initial value in the plain-text input when it is loaded. 38 | * 39 | * @see https://api.slack.com/reference/block-kit/block-elements#input__fields 40 | * 41 | * @param string $initialValue 42 | * @return $this 43 | */ 44 | public function withInitialValue(string $initialValue): self 45 | { 46 | $this->initialValue = $initialValue; 47 | return $this; 48 | } 49 | 50 | /** 51 | * The minimum length of input that the user must provide. If the user provides less, they will receive an error. 52 | * Maximum value is 3000. 53 | * 54 | * @see https://api.slack.com/reference/block-kit/block-elements#input__fields 55 | * 56 | * @param int $minLength 57 | * @return $this 58 | */ 59 | public function withMinLength(int $minLength): self 60 | { 61 | $this->minLength = $minLength; 62 | return $this; 63 | } 64 | 65 | /** 66 | * The maximum length of input that the user can provide. If the user provides more, they will receive an error. 67 | * 68 | * @see https://api.slack.com/reference/block-kit/block-elements#input__fields 69 | * 70 | * @param int $maxLength 71 | * @return $this 72 | */ 73 | public function withMaxLength(int $maxLength): self 74 | { 75 | $this->maxLength = $maxLength; 76 | return $this; 77 | } 78 | 79 | /** 80 | * Indicates whether the input will be a single line (false) or a larger textarea (true). Defaults to false. 81 | * 82 | * @see https://api.slack.com/reference/block-kit/block-elements#input__fields 83 | * 84 | * @param bool $multiline 85 | * @return $this 86 | */ 87 | public function multiline(bool $multiline = true): self 88 | { 89 | $this->multiline = $multiline; 90 | return $this; 91 | } 92 | 93 | public function getType(): string 94 | { 95 | return static::TYPE_PLAIN_TEXT_INPUT; 96 | } 97 | 98 | public function compatibleWith(): array 99 | { 100 | return [ 101 | Block::TYPE_INPUT, 102 | ]; 103 | } 104 | 105 | public function toArray(): array 106 | { 107 | return $this->toMergedArray([ 108 | 'action_id' => $this->actionId, 109 | 'placeholder' => ($this->placeholder) ? $this->placeholder->toArray() : null, 110 | 'initial_value' => $this->initialValue, 111 | 'multiline' => $this->multiline, 112 | 'min_length' => $this->minLength, 113 | 'max_length' => $this->maxLength, 114 | 'focus_on_load' => $this->focusOnLoad, 115 | ]); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/RadioButtonGroupElement.php: -------------------------------------------------------------------------------- 1 | actionId = $actionId; 36 | $this->options = $options; 37 | } 38 | 39 | /** 40 | * An option object that exactly matches one of the options within options. 41 | * This option will be selected when the radio button group initially loads. 42 | * 43 | * @see https://api.slack.com/reference/block-kit/block-elements#radio__fields 44 | * 45 | * @param OptionObject $option 46 | * @return $this 47 | */ 48 | public function withInitialOption(OptionObject $option): self 49 | { 50 | $this->initialOption = $option; 51 | return $this; 52 | } 53 | 54 | public function getType(): string 55 | { 56 | return static::TYPE_RADIO_BUTTON_GROUP; 57 | } 58 | 59 | public function compatibleWith(): array 60 | { 61 | return [ 62 | Block::TYPE_SECTION, 63 | Block::TYPE_ACTIONS, 64 | Block::TYPE_INPUT, 65 | ]; 66 | } 67 | 68 | public function toArray(): array 69 | { 70 | return $this->toMergedArray($this->mergeConfirmationDialog([ 71 | 'action_id' => $this->actionId, 72 | 'options' => array_map(function (OptionObject $option): array { 73 | return $option->toArray(); 74 | }, $this->options), 75 | 'initial_option' => ($this->initialOption) ? $this->initialOption->toArray() : null, 76 | 'focus_on_load' => $this->focusOnLoad, 77 | ])); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/SelectMenu/SelectMenu.php: -------------------------------------------------------------------------------- 1 | actionId = $actionId; 23 | $this->placeholder = $placeholder; 24 | } 25 | 26 | public function compatibleWith(): array 27 | { 28 | return [ 29 | Block::TYPE_SECTION, 30 | Block::TYPE_ACTIONS, 31 | Block::TYPE_INPUT, 32 | ]; 33 | } 34 | 35 | public function toArray(): array 36 | { 37 | return $this->mergeConfirmationDialog([ 38 | 'type' => $this->getType(), 39 | 'action_id' => $this->actionId, 40 | 'placeholder' => $this->placeholder->toArray(), 41 | 'focus_on_load' => $this->focusOnLoad, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuConversationElement.php: -------------------------------------------------------------------------------- 1 | initialOptionKeyName = 'initial_conversation'; 27 | } 28 | 29 | /** 30 | * Pre-populates the select menu with the conversation that the user was viewing when they opened 31 | * the modal, if available. 32 | * 33 | * @see https://api.slack.com/reference/block-kit/block-elements#conversation_select__fields 34 | * 35 | * @param bool $defaultToCurrent 36 | * @return $this 37 | */ 38 | public function defaultToCurrent(bool $defaultToCurrent = true): self 39 | { 40 | $this->defaultToCurrent = $defaultToCurrent; 41 | return $this; 42 | } 43 | 44 | /** 45 | * This field only works with menus in input blocks in modals. 46 | * When set to true, the view_submission payload from the menu's parent view will contain a response_url. 47 | * This response_url can be used for message responses. The target conversation for the message will be 48 | * determined by the value of this select menu. 49 | * 50 | * @see https://api.slack.com/reference/block-kit/block-elements#conversation_select__fields 51 | * 52 | * @param bool $responseUrlEnabled 53 | * @return $this 54 | */ 55 | public function withResponseUrlEnabled(bool $responseUrlEnabled = true): self 56 | { 57 | $this->responseUrlEnabled = $responseUrlEnabled; 58 | return $this; 59 | } 60 | 61 | public function getType(): string 62 | { 63 | return static::TYPE_SELECT_MENU_CONVERSATIONS; 64 | } 65 | 66 | public function toArray(): array 67 | { 68 | return $this->toMergedArray($this->mergeInitialOption($this->mergeFilter([ 69 | 'default_to_current_conversation' => $this->defaultToCurrent, 70 | 'response_url_enabled' => $this->responseUrlEnabled, 71 | ]))); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuExternalElement.php: -------------------------------------------------------------------------------- 1 | minQueryLength = $length; 38 | return $this; 39 | } 40 | 41 | public function getType(): string 42 | { 43 | return static::TYPE_SELECT_MENU_EXTERNAL; 44 | } 45 | 46 | public function toArray(): array 47 | { 48 | return $this->toMergedArray($this->mergeInitialOption([ 49 | 'min_query_length' => $this->minQueryLength, 50 | ])); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuPublicChannelElement.php: -------------------------------------------------------------------------------- 1 | initialOptionKeyName = 'initial_channel'; 24 | } 25 | 26 | /** 27 | * This field only works with menus in input blocks in modals. 28 | * When set to true, the view_submission payload from the menu's parent view will contain a response_url. 29 | * This response_url can be used for message responses. 30 | * The target channel for the message will be determined by the value of this select menu. 31 | * 32 | * @see https://api.slack.com/reference/block-kit/block-elements#channel_select__fields 33 | * 34 | * @param bool $responseUrlEnabled 35 | * @return $this 36 | */ 37 | public function withResponseUrlEnabled(bool $responseUrlEnabled = true): self 38 | { 39 | $this->responseUrlEnabled = $responseUrlEnabled; 40 | return $this; 41 | } 42 | 43 | public function getType(): string 44 | { 45 | return static::TYPE_SELECT_MENU_CHANNELS; 46 | } 47 | 48 | public function toArray(): array 49 | { 50 | return $this->toMergedArray($this->mergeInitialOption([ 51 | 'response_url_enabled' => $this->responseUrlEnabled, 52 | ])); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuStaticElement.php: -------------------------------------------------------------------------------- 1 | options = $options; 33 | } 34 | 35 | public function getType(): string 36 | { 37 | return static::TYPE_SELECT_MENU_STATIC; 38 | } 39 | 40 | public function toArray(): array 41 | { 42 | return $this->toMergedArray($this->mergeInitialOption([ 43 | 'options' => array_map(function (OptionObject $option): array { 44 | return $option->toArray(); 45 | }, $this->options), 46 | ])); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuUserElement.php: -------------------------------------------------------------------------------- 1 | initialOptionKeyName = 'initial_user'; 22 | } 23 | 24 | public function getType(): string 25 | { 26 | return static::TYPE_SELECT_MENU_USERS; 27 | } 28 | 29 | public function toArray(): array 30 | { 31 | return $this->toMergedArray($this->mergeInitialOption()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Elements/TimePickerElement.php: -------------------------------------------------------------------------------- 1 | actionId = $actionId; 29 | $this->placeholder = $placeholder; 30 | } 31 | 32 | /** 33 | * The initial time that is selected when the element is loaded. 34 | * This should be in the format HH:mm, where HH is the 24-hour format of an hour (00 to 23) and mm is minutes 35 | * with leading zeros (00 to 59), for example 22:25 for 10:25pm. 36 | * 37 | * @see https://api.slack.com/reference/block-kit/block-elements#timepicker__fields 38 | * 39 | * @param string $initialTime 40 | * @return $this 41 | */ 42 | public function withInitialTime(string $initialTime): self 43 | { 44 | $this->initialTime = $initialTime; 45 | return $this; 46 | } 47 | 48 | public function getType(): string 49 | { 50 | return static::TYPE_TIME_PICKER; 51 | } 52 | 53 | public function compatibleWith(): array 54 | { 55 | return [ 56 | Block::TYPE_SECTION, 57 | Block::TYPE_ACTIONS, 58 | Block::TYPE_INPUT, 59 | ]; 60 | } 61 | 62 | public function toArray(): array 63 | { 64 | return $this->toMergedArray($this->mergeConfirmationDialog([ 65 | 'action_id' => $this->actionId, 66 | 'placeholder' => $this->placeholder->toArray(), 67 | 'initial_time' => $this->initialTime, 68 | 'focus_on_load' => $this->focusOnLoad, 69 | ])); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Traits/Composition/WithFocusOnLoad.php: -------------------------------------------------------------------------------- 1 | focusOnLoad = $focusOnLoad; 20 | return $this; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Traits/Elements/HasInitialOption.php: -------------------------------------------------------------------------------- 1 | initialOption = $initialOption; 25 | return $this; 26 | } 27 | 28 | protected function mergeInitialOption(array $toMergeWith = []): array 29 | { 30 | $formattedOption = null; 31 | if (is_string($this->initialOption)) { 32 | $formattedOption = $this->initialOption; 33 | } elseif ($this->initialOption instanceof Arrayable) { 34 | $formattedOption = $this->initialOption->toArray(); 35 | } 36 | 37 | return array_merge($toMergeWith, [ 38 | $this->initialOptionKeyName => $formattedOption, 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Traits/Elements/WithConfirmationDialog.php: -------------------------------------------------------------------------------- 1 | confirmationDialog = $confirmationDialog; 23 | return $this; 24 | } 25 | 26 | protected function mergeConfirmationDialog(array $mergeWith): array 27 | { 28 | return array_merge($mergeWith, [ 29 | 'confirm' => ($this->confirmationDialog) ? $this->confirmationDialog->toArray() : null, 30 | ]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Traits/Elements/WithFilter.php: -------------------------------------------------------------------------------- 1 | filter = $filter; 23 | return $this; 24 | } 25 | 26 | protected function mergeFilter(array $mergeWith): array 27 | { 28 | return array_filter(array_merge($mergeWith, [ 29 | 'filter' => ($this->filter) ? $this->filter->toArray() : null, 30 | ])); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Traits/HasActionId.php: -------------------------------------------------------------------------------- 1 | toArray(); 19 | }, $this->elements); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Support/LayoutBlocks/Traits/MergesArrays.php: -------------------------------------------------------------------------------- 1 | blocks[] = $block; 24 | return $this; 25 | } 26 | 27 | public function header(string $text): self 28 | { 29 | $this->blocks[] = new HeaderBlock($this->withPlainText($text)); 30 | return $this; 31 | } 32 | 33 | public function divider(): self 34 | { 35 | $this->blocks[] = new DividerBlock(); 36 | return $this; 37 | } 38 | 39 | public function withPlainText(string $text): TextObject 40 | { 41 | return new TextObject($text); 42 | } 43 | 44 | public function withMarkdownText(string $text): MarkdownObject 45 | { 46 | return new MarkdownObject($text); 47 | } 48 | 49 | /** 50 | * @return Block[] 51 | */ 52 | public function getBlocks(): array 53 | { 54 | return $this->blocks; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Support/Paginator.php: -------------------------------------------------------------------------------- 1 | items = $items; 17 | $this->nextCursor = $nextCursor; 18 | } 19 | 20 | public function hasMorePages(): bool 21 | { 22 | return !is_null($this->nextCursor); 23 | } 24 | 25 | public function nextCursor(): ?string 26 | { 27 | return $this->nextCursor; 28 | } 29 | 30 | public function toArray(): array 31 | { 32 | return [ 33 | 'items' => $this->items, 34 | 'next_cursor' => $this->nextCursor, 35 | 'has_more_pages' => $this->hasMorePages(), 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Support/SlackCommandRequest.php: -------------------------------------------------------------------------------- 1 | options['username'] = $username; 15 | return $this; 16 | } 17 | 18 | public function iconUrl(string $url): self 19 | { 20 | if (empty($this->options['icon'])) { 21 | $this->options['icon'] = []; 22 | } 23 | 24 | $this->options['icon']['url'] = $url; 25 | return $this; 26 | } 27 | 28 | public function iconEmoji(string $emoji): self 29 | { 30 | if (empty($this->options['icon'])) { 31 | $this->options['icon'] = []; 32 | } 33 | 34 | $this->options['icon']['emoji'] = $emoji; 35 | return $this; 36 | } 37 | 38 | public function unfurlMedia(bool $unfurl = true): self 39 | { 40 | if (empty($this->options['unfurl'])) { 41 | $this->options['unfurl'] = []; 42 | } 43 | 44 | $this->options['unfurl']['media'] = $unfurl; 45 | return $this; 46 | } 47 | 48 | public function unfurlLinks(bool $unfurl = true): self 49 | { 50 | if (empty($this->options['unfurl'])) { 51 | $this->options['unfurl'] = []; 52 | } 53 | 54 | $this->options['unfurl']['links'] = $unfurl; 55 | return $this; 56 | } 57 | 58 | public function threadTs(string $ts): self 59 | { 60 | if (empty($this->options['thread'])) { 61 | $this->options['thread'] = []; 62 | } 63 | 64 | $this->options['thread']['ts'] = $ts; 65 | return $this; 66 | } 67 | 68 | public function threadReplySendToChannel(bool $sendToChannel = true): self 69 | { 70 | if (empty($this->options['thread'])) { 71 | $this->options['thread'] = []; 72 | } 73 | 74 | $this->options['thread']['send_to_channel'] = $sendToChannel; 75 | return $this; 76 | } 77 | 78 | public function linkNames(bool $linkNames = true): self 79 | { 80 | $this->options['link_names'] = $linkNames; 81 | return $this; 82 | } 83 | 84 | public function metadata(array $metadata): self 85 | { 86 | $this->options['metadata'] = $metadata; 87 | return $this; 88 | } 89 | 90 | public function parse(string $parse): self 91 | { 92 | $this->options['parse'] = $parse; 93 | return $this; 94 | } 95 | 96 | public function markdown(bool $markdown = true): self 97 | { 98 | $this->options['markdown'] = $markdown; 99 | return $this; 100 | } 101 | 102 | public function toArray(): array 103 | { 104 | return $this->options; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | addToAssertionCount($container->mockery_getExpectationCount()); 15 | } 16 | 17 | \Mockery::close(); 18 | } 19 | 20 | parent::tearDown(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Traits/BasicMultiSelectTests.php: -------------------------------------------------------------------------------- 1 | elementClass('action-id', \Mockery::mock(TextObject::class), ...$this->additionalSetupArgs); 22 | $this->assertEquals([ 23 | Block::TYPE_SECTION, 24 | ], $element->compatibleWith()); 25 | } 26 | 27 | public function testElementType() 28 | { 29 | /** @var Element $element */ 30 | $element = new $this->elementClass('action-id', \Mockery::mock(TextObject::class), ...$this->additionalSetupArgs); 31 | $this->assertSame($this->expectedType, $element->getType()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Traits/BasicSelectMenuTests.php: -------------------------------------------------------------------------------- 1 | elementClass('action-id', \Mockery::mock(TextObject::class), ...$this->additionalSetupArgs); 22 | $this->assertEquals([ 23 | Block::TYPE_SECTION, 24 | Block::TYPE_ACTIONS, 25 | Block::TYPE_INPUT, 26 | ], $element->compatibleWith()); 27 | } 28 | 29 | public function testElementType() 30 | { 31 | /** @var SelectMenu $element */ 32 | $element = new $this->elementClass('action-id', \Mockery::mock(TextObject::class), ...$this->additionalSetupArgs); 33 | $this->assertSame($this->expectedType, $element->getType()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Unit/Services/SlackCommandHandlerFactoryServiceTest.php: -------------------------------------------------------------------------------- 1 | laravel = \Mockery::mock(Application::class); 23 | $this->service = new SlackCommandHandlerFactoryService($this->laravel); 24 | } 25 | 26 | public function testRegisterAndGetHandlerSuccess() 27 | { 28 | $command = 'test'; 29 | $handlerClass = 'App\\TestClass'; 30 | 31 | $this->service->register($handlerClass, $command); 32 | 33 | $handlerMock = \Mockery::mock(SlackCommandHandlerContract::class); 34 | $this->laravel->shouldReceive('make') 35 | ->once() 36 | ->with($handlerClass) 37 | ->andReturn($handlerMock); 38 | 39 | $result = $this->service->getHandler($command); 40 | $this->assertSame($result, $handlerMock); 41 | } 42 | 43 | public function testRegisterAndGetHandlerFailsForMissingCommandHandler() 44 | { 45 | $this->expectException(\InvalidArgumentException::class); 46 | $this->expectExceptionMessage('No handler found for command invalid-command'); 47 | 48 | $this->service->getHandler('invalid-command'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Blocks/ActionsBlockTest.php: -------------------------------------------------------------------------------- 1 | 'val']; 16 | $element = \Mockery::mock(Element::class); 17 | $element->shouldReceive('toArray')->andReturn($expectedElementArray); 18 | 19 | $block = new ActionsBlock([$element], 'block-id'); 20 | $this->assertSame(Block::TYPE_ACTIONS, $block->getType()); 21 | 22 | $this->assertEquals([ 23 | 'type' => Block::TYPE_ACTIONS, 24 | 'block_id' => 'block-id', 25 | 'elements' => [$expectedElementArray] 26 | ], $block->toArray()); 27 | } 28 | 29 | public function testBlockNoBlockId() 30 | { 31 | $block = new ActionsBlock([]); 32 | $this->assertSame(Block::TYPE_ACTIONS, $block->getType()); 33 | 34 | $this->assertEquals([ 35 | 'type' => Block::TYPE_ACTIONS, 36 | ], $block->toArray()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Blocks/ContextBlockTest.php: -------------------------------------------------------------------------------- 1 | 'val']; 17 | $expectedElementArray = ['key2' => 'val2']; 18 | 19 | $textBlockElement = \Mockery::mock(TextObject::class); 20 | $textBlockElement->shouldReceive('toArray')->andReturn($expectedTextElementArray); 21 | 22 | $element = \Mockery::mock(Element::class); 23 | $element->shouldReceive('toArray')->andReturn($expectedElementArray); 24 | 25 | $block = new ContextBlock([$textBlockElement], [$element], 'block-id'); 26 | $this->assertSame(Block::TYPE_CONTEXT, $block->getType()); 27 | 28 | $this->assertEquals([ 29 | 'type' => Block::TYPE_CONTEXT, 30 | 'block_id' => 'block-id', 31 | 'elements' => [$expectedElementArray, $expectedTextElementArray] 32 | ], $block->toArray()); 33 | } 34 | 35 | public function testBlockNoBlockId() 36 | { 37 | $block = new ContextBlock([], []); 38 | $this->assertSame(Block::TYPE_CONTEXT, $block->getType()); 39 | 40 | $this->assertEquals([ 41 | 'type' => Block::TYPE_CONTEXT, 42 | ], $block->toArray()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Blocks/DividerBlockTest.php: -------------------------------------------------------------------------------- 1 | assertSame(Block::TYPE_DIVIDER, $block->getType()); 16 | $this->assertEquals([ 17 | 'type' => Block::TYPE_DIVIDER, 18 | 'block_id' => 'block-id', 19 | ], $block->toArray()); 20 | } 21 | 22 | public function testBlockNoBlockId() 23 | { 24 | $block = new DividerBlock(); 25 | $this->assertSame(Block::TYPE_DIVIDER, $block->getType()); 26 | $this->assertEquals([ 27 | 'type' => Block::TYPE_DIVIDER, 28 | ], $block->toArray()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Blocks/FileBlockTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([ 20 | 'type' => Block::TYPE_FILE, 21 | 'external_id' => $externalId, 22 | 'block_id' => $blockId, 23 | 'source' => 'remote', 24 | ], $block->toArray()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Blocks/HeaderBlockTest.php: -------------------------------------------------------------------------------- 1 | 'val']; 16 | 17 | $textObject = \Mockery::mock(TextObject::class); 18 | $textObject->shouldReceive('toArray')->andReturn($expectedTextObjectArray); 19 | 20 | $block = new HeaderBlock($textObject, 'block-id'); 21 | $this->assertSame(Block::TYPE_HEADER, $block->getType()); 22 | $this->assertEquals([ 23 | 'type' => Block::TYPE_HEADER, 24 | 'block_id' => 'block-id', 25 | 'text' => $expectedTextObjectArray, 26 | ], $block->toArray()); 27 | } 28 | 29 | public function testBlockNoBlockId() 30 | { 31 | $expectedTextObjectArray = ['key' => 'val']; 32 | 33 | $textObject = \Mockery::mock(TextObject::class); 34 | $textObject->shouldReceive('toArray')->andReturn($expectedTextObjectArray); 35 | 36 | $block = new HeaderBlock($textObject); 37 | $this->assertSame(Block::TYPE_HEADER, $block->getType()); 38 | $this->assertEquals([ 39 | 'type' => Block::TYPE_HEADER, 40 | 'text' => $expectedTextObjectArray, 41 | ], $block->toArray()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Blocks/ImageBlockTest.php: -------------------------------------------------------------------------------- 1 | 'val']; 16 | 17 | $textObject = \Mockery::mock(TextObject::class); 18 | $textObject->shouldReceive('toArray')->andReturn($expectedTextObjectArray); 19 | 20 | $block = new ImageBlock('https://example.com', 'test alt text', 'block-id'); 21 | $this->assertSame(Block::TYPE_IMAGE, $block->getType()); 22 | $this->assertEquals([ 23 | 'type' => Block::TYPE_IMAGE, 24 | 'block_id' => 'block-id', 25 | 'image_url' => 'https://example.com', 26 | 'alt_text' => 'test alt text', 27 | ], $block->toArray()); 28 | } 29 | 30 | public function testBlockNoBlockId() 31 | { 32 | $block = new ImageBlock('https://example.com', 'test alt text'); 33 | $this->assertSame(Block::TYPE_IMAGE, $block->getType()); 34 | $this->assertEquals([ 35 | 'type' => Block::TYPE_IMAGE, 36 | 'image_url' => 'https://example.com', 37 | 'alt_text' => 'test alt text', 38 | ], $block->toArray()); 39 | } 40 | 41 | public function testBlockWithTitle() 42 | { 43 | $expectedTextObjectArray = ['key' => 'val']; 44 | 45 | $textObject = \Mockery::mock(TextObject::class); 46 | $textObject->shouldReceive('toArray')->andReturn($expectedTextObjectArray); 47 | 48 | $block = new ImageBlock('https://example.com', 'test alt text'); 49 | $block->withTitle($textObject); 50 | 51 | $this->assertSame(Block::TYPE_IMAGE, $block->getType()); 52 | $this->assertEquals([ 53 | 'type' => Block::TYPE_IMAGE, 54 | 'image_url' => 'https://example.com', 55 | 'alt_text' => 'test alt text', 56 | 'title' => $expectedTextObjectArray, 57 | ], $block->toArray()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Blocks/InputBlockTest.php: -------------------------------------------------------------------------------- 1 | 'v1']; 16 | $expectedElementArray = ['k2' => 'v2']; 17 | 18 | $label = \Mockery::mock(TextObject::class); 19 | $element = \Mockery::mock(Element::class); 20 | 21 | $label->shouldReceive('toArray')->andReturn($expectedLabelArray); 22 | $element->shouldReceive('toArray')->andReturn($expectedElementArray); 23 | 24 | $block = new InputBlock($label, $element); 25 | 26 | $this->assertEquals([ 27 | 'type' => Block::TYPE_INPUT, 28 | 'label' => $expectedLabelArray, 29 | 'element' => $expectedElementArray, 30 | ], $block->toArray()); 31 | } 32 | 33 | public function testBlockWithOptions() 34 | { 35 | $expectedLabelArray = ['k1' => 'v1']; 36 | $expectedHintArray = ['k2' => 'v2']; 37 | $expectedElementArray = ['k3' => 'v3']; 38 | 39 | $label = \Mockery::mock(TextObject::class); 40 | $hint = \Mockery::mock(TextObject::class); 41 | $element = \Mockery::mock(Element::class); 42 | 43 | $label->shouldReceive('toArray')->andReturn($expectedLabelArray); 44 | $hint->shouldReceive('toArray')->andReturn($expectedHintArray); 45 | $element->shouldReceive('toArray')->andReturn($expectedElementArray); 46 | 47 | $block = new InputBlock($label, $element, 'block-id'); 48 | 49 | $block->withHint($hint); 50 | $block->dispatchAction(); 51 | $block->optional(); 52 | 53 | $this->assertEquals([ 54 | 'type' => Block::TYPE_INPUT, 55 | 'label' => $expectedLabelArray, 56 | 'element' => $expectedElementArray, 57 | 'block_id' => 'block-id', 58 | 'hint' => $expectedHintArray, 59 | 'dispatch_action' => true, 60 | 'optional' => true, 61 | ], $block->toArray()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Blocks/SectionBlockTest.php: -------------------------------------------------------------------------------- 1 | 'val']; 17 | 18 | $textObject = \Mockery::mock(TextObject::class); 19 | $textObject->shouldReceive('toArray')->andReturn($expectedTextObjectArray); 20 | 21 | $block = new SectionBlock(); 22 | $block->withText($textObject); 23 | 24 | $this->assertSame(Block::TYPE_SECTION, $block->getType()); 25 | $this->assertEquals([ 26 | 'type' => Block::TYPE_SECTION, 27 | 'text' => $expectedTextObjectArray, 28 | ], $block->toArray()); 29 | } 30 | 31 | public function testBlockWithFields() 32 | { 33 | $expectedTextObject1Array = ['key' => 'val']; 34 | $expectedTextObject2Array = ['key' => 'val']; 35 | $expectedTextObject3Array = ['key' => 'val']; 36 | 37 | $textObject1 = \Mockery::mock(TextObject::class); 38 | $textObject1->shouldReceive('toArray')->andReturn($expectedTextObject1Array); 39 | 40 | $textObject2 = \Mockery::mock(TextObject::class); 41 | $textObject2->shouldReceive('toArray')->andReturn($expectedTextObject2Array); 42 | 43 | $textObject3 = \Mockery::mock(TextObject::class); 44 | $textObject3->shouldReceive('toArray')->andReturn($expectedTextObject3Array); 45 | 46 | $block = new SectionBlock(); 47 | $block->withFields([$textObject1, $textObject2, $textObject3]); 48 | 49 | $this->assertSame(Block::TYPE_SECTION, $block->getType()); 50 | $this->assertEquals([ 51 | 'type' => Block::TYPE_SECTION, 52 | 'fields' => [$expectedTextObject1Array, $expectedTextObject2Array, $expectedTextObject3Array], 53 | ], $block->toArray()); 54 | } 55 | 56 | public function testBlockWithAccessory() 57 | { 58 | $expectedTextObjectArray = ['key' => 'val']; 59 | $expectedElementArray = ['key2' => 'val2']; 60 | 61 | $textObject = \Mockery::mock(TextObject::class); 62 | $textObject->shouldReceive('toArray')->andReturn($expectedTextObjectArray); 63 | 64 | $element = \Mockery::mock(Element::class); 65 | $element->shouldReceive('toArray')->andReturn($expectedElementArray); 66 | 67 | $block = new SectionBlock('block-id'); 68 | $block->withText($textObject); 69 | $block->withAccessory($element); 70 | 71 | $this->assertSame(Block::TYPE_SECTION, $block->getType()); 72 | $this->assertEquals([ 73 | 'type' => Block::TYPE_SECTION, 74 | 'text' => $expectedTextObjectArray, 75 | 'accessory' => $expectedElementArray, 76 | 'block_id' => 'block-id', 77 | ], $block->toArray()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Composition/ConfirmationDialogObjectTest.php: -------------------------------------------------------------------------------- 1 | 'v1']; 15 | $expectedTextArray = ['k2' => 'v2']; 16 | $expectedConfirmArray = ['k3' => 'v3']; 17 | $expectedDenyArray = ['k4' => 'v4']; 18 | 19 | $title = \Mockery::mock(TextObject::class); 20 | $text = \Mockery::mock(TextObject::class); 21 | $confirm = \Mockery::mock(TextObject::class); 22 | $deny = \Mockery::mock(TextObject::class); 23 | 24 | $title->shouldReceive('toArray')->andReturn($expectedTitleArray); 25 | $text->shouldReceive('toArray')->andReturn($expectedTextArray); 26 | $confirm->shouldReceive('toArray')->andReturn($expectedConfirmArray); 27 | $deny->shouldReceive('toArray')->andReturn($expectedDenyArray); 28 | 29 | $object = new ConfirmationDialogObject($title, $text, $confirm, $deny); 30 | 31 | $this->assertEquals([ 32 | 'title' => $expectedTitleArray, 33 | 'text' => $expectedTextArray, 34 | 'confirm' => $expectedConfirmArray, 35 | 'deny' => $expectedDenyArray, 36 | ], $object->toArray()); 37 | } 38 | 39 | public function testObjectWithButtonPrimary() 40 | { 41 | $expectedTitleArray = ['k1' => 'v1']; 42 | $expectedTextArray = ['k2' => 'v2']; 43 | $expectedConfirmArray = ['k3' => 'v3']; 44 | $expectedDenyArray = ['k4' => 'v4']; 45 | 46 | $title = \Mockery::mock(TextObject::class); 47 | $text = \Mockery::mock(TextObject::class); 48 | $confirm = \Mockery::mock(TextObject::class); 49 | $deny = \Mockery::mock(TextObject::class); 50 | 51 | $title->shouldReceive('toArray')->andReturn($expectedTitleArray); 52 | $text->shouldReceive('toArray')->andReturn($expectedTextArray); 53 | $confirm->shouldReceive('toArray')->andReturn($expectedConfirmArray); 54 | $deny->shouldReceive('toArray')->andReturn($expectedDenyArray); 55 | 56 | $object = new ConfirmationDialogObject($title, $text, $confirm, $deny); 57 | $object->confirmButtonPrimary(); 58 | 59 | $this->assertEquals([ 60 | 'title' => $expectedTitleArray, 61 | 'text' => $expectedTextArray, 62 | 'confirm' => $expectedConfirmArray, 63 | 'deny' => $expectedDenyArray, 64 | 'style' => 'primary', 65 | ], $object->toArray()); 66 | } 67 | 68 | public function testObjectWithButtonDanger() 69 | { 70 | $expectedTitleArray = ['k1' => 'v1']; 71 | $expectedTextArray = ['k2' => 'v2']; 72 | $expectedConfirmArray = ['k3' => 'v3']; 73 | $expectedDenyArray = ['k4' => 'v4']; 74 | 75 | $title = \Mockery::mock(TextObject::class); 76 | $text = \Mockery::mock(TextObject::class); 77 | $confirm = \Mockery::mock(TextObject::class); 78 | $deny = \Mockery::mock(TextObject::class); 79 | 80 | $title->shouldReceive('toArray')->andReturn($expectedTitleArray); 81 | $text->shouldReceive('toArray')->andReturn($expectedTextArray); 82 | $confirm->shouldReceive('toArray')->andReturn($expectedConfirmArray); 83 | $deny->shouldReceive('toArray')->andReturn($expectedDenyArray); 84 | 85 | $object = new ConfirmationDialogObject($title, $text, $confirm, $deny); 86 | $object->confirmButtonDanger(); 87 | 88 | $this->assertEquals([ 89 | 'title' => $expectedTitleArray, 90 | 'text' => $expectedTextArray, 91 | 'confirm' => $expectedConfirmArray, 92 | 'deny' => $expectedDenyArray, 93 | 'style' => 'danger', 94 | ], $object->toArray()); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Composition/DispatchActionConfigurationObjectTest.php: -------------------------------------------------------------------------------- 1 | onEnterPressed(); 17 | $object->onCharacterEntered(); 18 | 19 | $this->assertEquals([ 20 | 'trigger_actions_on' => $triggerActionsOn, 21 | ], $object->toArray()); 22 | } 23 | 24 | public function testObjectNoTriggers() 25 | { 26 | $object = new DispatchActionConfigurationObject(); 27 | $this->assertEquals([], $object->toArray()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Composition/FilterObjectTest.php: -------------------------------------------------------------------------------- 1 | assertEmpty($object->toArray()); 15 | } 16 | 17 | public function testObjectWithAllOptions() 18 | { 19 | $object = new FilterObject(); 20 | 21 | $object->includeConversationTypes(['public', 'private']); 22 | $object->excludeExternalSharedChannels(); 23 | $object->excludeBotUsers(); 24 | 25 | $this->assertEquals([ 26 | 'include' => ['public', 'private'], 27 | 'exclude_external_shared_channels' => true, 28 | 'exclude_bot_users' => true, 29 | ], $object->toArray()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Composition/MarkdownObjectTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([ 17 | 'type' => 'mrkdwn', 18 | 'text' => 'test text', 19 | ], $object->toArray()); 20 | } 21 | 22 | public function testObjectWithOptions() 23 | { 24 | $text = 'test text'; 25 | $object = new MarkdownObject($text); 26 | 27 | $object->verbatim(); 28 | $object->escapeEmojis(); 29 | 30 | $this->assertEquals([ 31 | 'type' => 'mrkdwn', 32 | 'text' => 'test text', 33 | 'emoji' => true, 34 | 'verbatim' => true, 35 | ], $object->toArray()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Composition/OptionGroupObjectTest.php: -------------------------------------------------------------------------------- 1 | 'v1']; 16 | $expectedOption1Array = ['k2' => 'v2']; 17 | $expectedOption2Array = ['k3' => 'v3']; 18 | 19 | $label = \Mockery::mock(TextObject::class); 20 | $option1 = \Mockery::mock(OptionObject::class); 21 | $option2 = \Mockery::mock(OptionObject::class); 22 | 23 | $label->shouldReceive('toArray')->andReturn($expectedLabelArray); 24 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 25 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 26 | 27 | $object = new OptionGroupObject($label, [$option1, $option2]); 28 | 29 | $this->assertEquals([ 30 | 'label' => $expectedLabelArray, 31 | 'options' => [$expectedOption1Array, $expectedOption2Array], 32 | ], $object->toArray()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Composition/OptionObjectTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([ 20 | 'text' => $text, 21 | 'value' => $value, 22 | ], $option->toArray()); 23 | } 24 | 25 | public function testObjectWithOptions() 26 | { 27 | $text = 'test text'; 28 | $value = 'test value'; 29 | 30 | $option = new OptionObject($text, $value); 31 | 32 | $expectedTextObjectArray = ['key' => 'value']; 33 | 34 | $descriptionTextObject = \Mockery::mock(TextObject::class); 35 | $descriptionTextObject->shouldReceive('toArray')->andReturn($expectedTextObjectArray); 36 | 37 | $option->withDescription($descriptionTextObject); 38 | $option->withUrl('https://example.com'); 39 | 40 | $this->assertEquals([ 41 | 'text' => $text, 42 | 'value' => $value, 43 | 'url' => 'https://example.com', 44 | 'description' => $expectedTextObjectArray, 45 | ], $option->toArray()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Composition/TextObjectTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([ 17 | 'type' => 'plain_text', 18 | 'text' => 'test text', 19 | ], $object->toArray()); 20 | } 21 | 22 | public function testObjectWithOptions() 23 | { 24 | $text = 'test text'; 25 | $object = new TextObject($text); 26 | 27 | $object->verbatim(); 28 | $object->escapeEmojis(); 29 | 30 | $this->assertEquals([ 31 | 'type' => 'plain_text', 32 | 'text' => 'test text', 33 | 'emoji' => true, 34 | ], $object->toArray()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/CheckboxesElementTest.php: -------------------------------------------------------------------------------- 1 | 'val']; 20 | $expectedOption2Array = ['key2' => 'val2']; 21 | 22 | $option1 = \Mockery::mock(OptionObject::class); 23 | $option2 = \Mockery::mock(OptionObject::class); 24 | 25 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 26 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 27 | 28 | $element = new CheckboxesElement($actionId, [$option1, $option2]); 29 | 30 | $this->assertEquals([ 31 | 'type' => Element::TYPE_CHECKBOXES, 32 | 'action_id' => $actionId, 33 | 'options' => [ 34 | $expectedOption1Array, 35 | $expectedOption2Array, 36 | ], 37 | ], $element->toArray()); 38 | } 39 | 40 | public function testElementWithInitialOptions() 41 | { 42 | $actionId = 'action-id'; 43 | 44 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 45 | $expectedOption1Array = ['key' => 'val']; 46 | $expectedOption2Array = ['key2' => 'val2']; 47 | 48 | $option1 = \Mockery::mock(OptionObject::class); 49 | $option2 = \Mockery::mock(OptionObject::class); 50 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 51 | 52 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 53 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 54 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 55 | 56 | $element = new CheckboxesElement($actionId, [$option1, $option2]); 57 | 58 | $element->withInitialOptions([$option1, $option2]); 59 | $element->withFocusOnLoad(); 60 | $element->withConfirmationDialog($confirmationDialogObject); 61 | 62 | $this->assertEquals([ 63 | 'type' => Element::TYPE_CHECKBOXES, 64 | 'action_id' => $actionId, 65 | 'options' => [ 66 | $expectedOption1Array, 67 | $expectedOption2Array, 68 | ], 69 | 'initial_options' => [ 70 | $expectedOption1Array, 71 | $expectedOption2Array, 72 | ], 73 | 'focus_on_load' => true, 74 | 'confirm' => $expectedConfirmationDialogArray, 75 | ], $element->toArray()); 76 | } 77 | 78 | public function testCompatibleWith() 79 | { 80 | $element = new CheckboxesElement('', []); 81 | $this->assertSame([ 82 | Block::TYPE_ACTIONS, 83 | Block::TYPE_SECTION, 84 | ], $element->compatibleWith()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/DatePickerElementTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([ 22 | 'type' => Element::TYPE_DATEPICKER, 23 | 'action_id' => $actionId, 24 | ], $element->toArray()); 25 | } 26 | 27 | public function testElementWithOptions() 28 | { 29 | $actionId = 'action-id'; 30 | $initialDate = '2022-01-01'; 31 | 32 | $element = new DatePickerElement($actionId, $initialDate); 33 | 34 | $expectedPlaceholderTextArray = ['key' => 'val']; 35 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 36 | 37 | $placeholderTextObject = \Mockery::mock(TextObject::class); 38 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 39 | 40 | $placeholderTextObject->shouldReceive('toArray')->andReturn($expectedPlaceholderTextArray); 41 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 42 | 43 | $element->withPlaceholder($placeholderTextObject); 44 | $element->withFocusOnLoad(); 45 | $element->withConfirmationDialog($confirmationDialogObject); 46 | 47 | $this->assertEquals([ 48 | 'type' => Element::TYPE_DATEPICKER, 49 | 'action_id' => $actionId, 50 | 'initial_date' => $initialDate, 51 | 'placeholder' => $expectedPlaceholderTextArray, 52 | 'focus_on_load' => true, 53 | 'confirm' => $expectedConfirmationDialogArray, 54 | ], $element->toArray()); 55 | } 56 | 57 | public function testCompatibleWith() 58 | { 59 | $element = new DatePickerElement(''); 60 | $this->assertSame([ 61 | Block::TYPE_SECTION, 62 | Block::TYPE_ACTIONS, 63 | ], $element->compatibleWith()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/ImageElementTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([ 21 | 'type' => Element::TYPE_IMAGE, 22 | 'image_url' => $imageUrl, 23 | 'alt_text' => $altText, 24 | ], $element->toArray()); 25 | 26 | $this->assertEquals([ 27 | Block::TYPE_SECTION, 28 | Block::TYPE_CONTEXT, 29 | ], $element->compatibleWith()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectConversationElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = MultiSelectConversationElement::class; 23 | $this->expectedType = Element::TYPE_MULTI_SELECT_CONVERSATIONS; 24 | } 25 | 26 | public function testElement() 27 | { 28 | $expectedPlaceholderArray = ['key' => 'value']; 29 | 30 | $actionId = 'action-id'; 31 | $placeholder = \Mockery::mock(TextObject::class); 32 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 33 | 34 | $element = new MultiSelectConversationElement($actionId, $placeholder); 35 | 36 | $this->assertEquals([ 37 | 'type' => Element::TYPE_MULTI_SELECT_CONVERSATIONS, 38 | 'action_id' => $actionId, 39 | 'placeholder' => $expectedPlaceholderArray, 40 | ], $element->toArray()); 41 | } 42 | 43 | public function testElementWithOptions() 44 | { 45 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 46 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 47 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 48 | 49 | $expectedFilterArray = ['filter' => 'value']; 50 | $filter = \Mockery::mock(FilterObject::class); 51 | $filter->shouldReceive('toArray')->andReturn($expectedFilterArray); 52 | 53 | $expectedPlaceholderArray = ['key' => 'value']; 54 | 55 | $actionId = 'action-id'; 56 | $placeholder = \Mockery::mock(TextObject::class); 57 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 58 | 59 | $initialConversations = ['channel1', 'channel2']; 60 | 61 | $element = new MultiSelectConversationElement($actionId, $placeholder); 62 | 63 | $element->withInitialConversations($initialConversations); 64 | $element->maxSelectedItems(5); 65 | $element->withFocusOnLoad(); 66 | $element->defaultToCurrent(); 67 | $element->withConfirmationDialog($confirmationDialogObject); 68 | $element->withFilter($filter); 69 | 70 | $this->assertEquals([ 71 | 'type' => Element::TYPE_MULTI_SELECT_CONVERSATIONS, 72 | 'action_id' => $actionId, 73 | 'placeholder' => $expectedPlaceholderArray, 74 | 'focus_on_load' => true, 75 | 'max_selected_items' => 5, 76 | 'initial_conversations' => $initialConversations, 77 | 'default_to_current_conversation' => true, 78 | 'confirm' => $expectedConfirmationDialogArray, 79 | 'filter' => $expectedFilterArray, 80 | ], $element->toArray()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectExternalElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = MultiSelectExternalElement::class; 24 | $this->expectedType = Element::TYPE_MULTI_SELECT_EXTERNAL; 25 | } 26 | 27 | public function testElement() 28 | { 29 | $expectedPlaceholderArray = ['key' => 'val']; 30 | 31 | $actionId = 'action-id'; 32 | $placeholder = \Mockery::mock(TextObject::class); 33 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 34 | 35 | $element = new MultiSelectExternalElement($actionId, $placeholder); 36 | 37 | $this->assertEquals([ 38 | 'type' => Element::TYPE_MULTI_SELECT_EXTERNAL, 39 | 'action_id' => $actionId, 40 | 'placeholder' => $expectedPlaceholderArray, 41 | ], $element->toArray()); 42 | } 43 | 44 | public function testElementWithOptions() 45 | { 46 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 47 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 48 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 49 | 50 | $expectedPlaceholderArray = ['key' => 'val']; 51 | 52 | $actionId = 'action-id'; 53 | $placeholder = \Mockery::mock(TextObject::class); 54 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 55 | 56 | $expectedOption1Array = ['o1' => 'k1']; 57 | $expectedOption2Array = ['o2' => 'k2']; 58 | 59 | $option1 = \Mockery::mock(OptionObject::class); 60 | $option2 = \Mockery::mock(OptionObject::class); 61 | 62 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 63 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 64 | 65 | $element = new MultiSelectExternalElement($actionId, $placeholder); 66 | 67 | $element->withMinQueryLength(5); 68 | $element->withFocusOnLoad(); 69 | $element->withInitialOptions([$option1, $option2]); 70 | $element->maxSelectedItems(1); 71 | $element->withConfirmationDialog($confirmationDialogObject); 72 | 73 | $this->assertEquals([ 74 | 'type' => Element::TYPE_MULTI_SELECT_EXTERNAL, 75 | 'action_id' => $actionId, 76 | 'placeholder' => $expectedPlaceholderArray, 77 | 'initial_options' => [ 78 | $expectedOption1Array, 79 | $expectedOption2Array, 80 | ], 81 | 'focus_on_load' => true, 82 | 'max_selected_items' => 1, 83 | 'min_query_length' => 5, 84 | 'confirm' => $expectedConfirmationDialogArray, 85 | ], $element->toArray()); 86 | } 87 | 88 | public function testCompatibleWith() 89 | { 90 | $actionId = 'action-id'; 91 | $element = new MultiSelectExternalElement($actionId, \Mockery::mock(TextObject::class)); 92 | 93 | $this->assertSame([ 94 | Block::TYPE_SECTION, 95 | ], $element->compatibleWith()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectPublicChannelElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = MultiSelectPublicChannelElement::class; 23 | $this->expectedType = Element::TYPE_MULTI_SELECT_CHANNELS; 24 | } 25 | 26 | public function testElement() 27 | { 28 | $expectedPlaceholderArray = ['key' => 'value']; 29 | 30 | $actionId = 'action-id'; 31 | $placeholder = \Mockery::mock(TextObject::class); 32 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 33 | 34 | $element = new MultiSelectPublicChannelElement($actionId, $placeholder); 35 | 36 | $this->assertEquals([ 37 | 'type' => Element::TYPE_MULTI_SELECT_CHANNELS, 38 | 'action_id' => $actionId, 39 | 'placeholder' => $expectedPlaceholderArray, 40 | ], $element->toArray()); 41 | } 42 | 43 | public function testElementWithOptions() 44 | { 45 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 46 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 47 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 48 | 49 | $expectedPlaceholderArray = ['key' => 'value']; 50 | 51 | $actionId = 'action-id'; 52 | $placeholder = \Mockery::mock(TextObject::class); 53 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 54 | 55 | $initialChannels = ['channel1', 'channel2']; 56 | 57 | $element = new MultiSelectPublicChannelElement($actionId, $placeholder); 58 | 59 | $element->withInitialChannels($initialChannels); 60 | $element->maxSelectedItems(5); 61 | $element->withFocusOnLoad(); 62 | $element->withConfirmationDialog($confirmationDialogObject); 63 | 64 | $this->assertEquals([ 65 | 'type' => Element::TYPE_MULTI_SELECT_CHANNELS, 66 | 'action_id' => $actionId, 67 | 'placeholder' => $expectedPlaceholderArray, 68 | 'focus_on_load' => true, 69 | 'max_selected_items' => 5, 70 | 'initial_channels' => $initialChannels, 71 | 'confirm' => $expectedConfirmationDialogArray, 72 | ], $element->toArray()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectStaticElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = MultiSelectStaticElement::class; 24 | $this->additionalSetupArgs = [[]]; 25 | $this->expectedType = Element::TYPE_MULTI_SELECT_STATIC; 26 | } 27 | 28 | public function testElement() 29 | { 30 | $expectedPlaceholderArray = ['key' => 'val']; 31 | 32 | $actionId = 'action-id'; 33 | $placeholder = \Mockery::mock(TextObject::class); 34 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 35 | 36 | $expectedOption1Array = ['o1' => 'k1']; 37 | $expectedOption2Array = ['o2' => 'k2']; 38 | 39 | $option1 = \Mockery::mock(OptionObject::class); 40 | $option2 = \Mockery::mock(OptionObject::class); 41 | 42 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 43 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 44 | 45 | $element = new MultiSelectStaticElement($actionId, $placeholder, [$option1, $option2]); 46 | 47 | $this->assertEquals([ 48 | 'type' => Element::TYPE_MULTI_SELECT_STATIC, 49 | 'action_id' => $actionId, 50 | 'placeholder' => $expectedPlaceholderArray, 51 | 'options' => [ 52 | $expectedOption1Array, 53 | $expectedOption2Array, 54 | ], 55 | ], $element->toArray()); 56 | } 57 | 58 | public function testElementWithOptions() 59 | { 60 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 61 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 62 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 63 | 64 | $expectedPlaceholderArray = ['key' => 'val']; 65 | 66 | $actionId = 'action-id'; 67 | $placeholder = \Mockery::mock(TextObject::class); 68 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 69 | 70 | $expectedOption1Array = ['o1' => 'k1']; 71 | $expectedOption2Array = ['o2' => 'k2']; 72 | 73 | $option1 = \Mockery::mock(OptionObject::class); 74 | $option2 = \Mockery::mock(OptionObject::class); 75 | 76 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 77 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 78 | 79 | $element = new MultiSelectStaticElement($actionId, $placeholder, [$option1, $option2]); 80 | 81 | $element->withFocusOnLoad(); 82 | $element->withInitialOptions([$option1, $option2]); 83 | $element->maxSelectedItems(1); 84 | $element->withConfirmationDialog($confirmationDialogObject); 85 | 86 | $this->assertEquals([ 87 | 'type' => Element::TYPE_MULTI_SELECT_STATIC, 88 | 'action_id' => $actionId, 89 | 'placeholder' => $expectedPlaceholderArray, 90 | 'options' => [ 91 | $expectedOption1Array, 92 | $expectedOption2Array, 93 | ], 94 | 'initial_options' => [ 95 | $expectedOption1Array, 96 | $expectedOption2Array, 97 | ], 98 | 'focus_on_load' => true, 99 | 'max_selected_items' => 1, 100 | 'confirm' => $expectedConfirmationDialogArray, 101 | ], $element->toArray()); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/MultiSelect/MultiSelectUserElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = MultiSelectUserElement::class; 22 | $this->expectedType = Element::TYPE_MULTI_SELECT_USERS; 23 | } 24 | 25 | public function testElement() 26 | { 27 | $expectedPlaceholderArray = ['key' => 'value']; 28 | 29 | $actionId = 'action-id'; 30 | $placeholder = \Mockery::mock(TextObject::class); 31 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 32 | 33 | $element = new MultiSelectUserElement($actionId, $placeholder); 34 | 35 | $this->assertEquals([ 36 | 'type' => Element::TYPE_MULTI_SELECT_USERS, 37 | 'action_id' => $actionId, 38 | 'placeholder' => $expectedPlaceholderArray, 39 | ], $element->toArray()); 40 | } 41 | 42 | public function testElementWithOptions() 43 | { 44 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 45 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 46 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 47 | 48 | $expectedPlaceholderArray = ['key' => 'value']; 49 | 50 | $actionId = 'action-id'; 51 | $placeholder = \Mockery::mock(TextObject::class); 52 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 53 | 54 | $initialUsers = ['user1', 'user2']; 55 | 56 | $element = new MultiSelectUserElement($actionId, $placeholder); 57 | 58 | $element->withInitialUsers($initialUsers); 59 | $element->maxSelectedItems(5); 60 | $element->withFocusOnLoad(); 61 | $element->withConfirmationDialog($confirmationDialogObject); 62 | 63 | $this->assertEquals([ 64 | 'type' => Element::TYPE_MULTI_SELECT_USERS, 65 | 'action_id' => $actionId, 66 | 'placeholder' => $expectedPlaceholderArray, 67 | 'focus_on_load' => true, 68 | 'max_selected_items' => 5, 69 | 'initial_users' => $initialUsers, 70 | 'confirm' => $expectedConfirmationDialogArray, 71 | ], $element->toArray()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/OverflowMenuElementTest.php: -------------------------------------------------------------------------------- 1 | 'v1']; 18 | $expectedOption2Array = ['k2' => 'v2']; 19 | 20 | $option1 = \Mockery::mock(OptionObject::class); 21 | $option2 = \Mockery::mock(OptionObject::class); 22 | 23 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 24 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 25 | 26 | $actionId = 'action-id'; 27 | 28 | $element = new OverflowMenuElement($actionId, [$option1, $option2]); 29 | 30 | $this->assertEquals([ 31 | 'type' => Element::TYPE_OVERFLOW_MENU, 32 | 'action_id' => $actionId, 33 | 'options' => [$expectedOption1Array, $expectedOption2Array], 34 | ], $element->toArray()); 35 | 36 | $this->assertEquals([ 37 | Block::TYPE_SECTION, 38 | Block::TYPE_ACTIONS, 39 | ], $element->compatibleWith()); 40 | } 41 | 42 | public function testElementWithOptions() 43 | { 44 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 45 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 46 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 47 | 48 | $expectedOption1Array = ['k1' => 'v1']; 49 | $expectedOption2Array = ['k2' => 'v2']; 50 | 51 | $option1 = \Mockery::mock(OptionObject::class); 52 | $option2 = \Mockery::mock(OptionObject::class); 53 | 54 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 55 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 56 | 57 | $actionId = 'action-id'; 58 | 59 | $element = new OverflowMenuElement($actionId, [$option1, $option2]); 60 | $element->withConfirmationDialog($confirmationDialogObject); 61 | 62 | $this->assertEquals([ 63 | 'type' => Element::TYPE_OVERFLOW_MENU, 64 | 'action_id' => $actionId, 65 | 'options' => [$expectedOption1Array, $expectedOption2Array], 66 | 'confirm' => $expectedConfirmationDialogArray, 67 | ], $element->toArray()); 68 | 69 | $this->assertEquals([ 70 | Block::TYPE_SECTION, 71 | Block::TYPE_ACTIONS, 72 | ], $element->compatibleWith()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/PlainTextInputElementTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([ 21 | 'type' => Element::TYPE_PLAIN_TEXT_INPUT, 22 | 'action_id' => $actionId, 23 | ], $element->toArray()); 24 | } 25 | 26 | public function testElementWithOptions() 27 | { 28 | $actionId = 'action-id'; 29 | 30 | $element = new PlainTextInputElement($actionId); 31 | 32 | $expectedPlaceholderArray = ['key' => 'value']; 33 | 34 | $placeholder = \Mockery::mock(TextObject::class); 35 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 36 | 37 | $element->multiline(); 38 | $element->withMinLength(1); 39 | $element->withMaxLength(10); 40 | $element->withInitialValue('initial value'); 41 | $element->withFocusOnLoad(); 42 | 43 | $this->assertEquals([ 44 | 'type' => Element::TYPE_PLAIN_TEXT_INPUT, 45 | 'action_id' => $actionId, 46 | 'initial_value' => 'initial value', 47 | 'multiline' => true, 48 | 'min_length' => 1, 49 | 'max_length' => 10, 50 | 'focus_on_load' => true, 51 | ], $element->toArray()); 52 | } 53 | 54 | public function testCompatibleWith() 55 | { 56 | $actionId = 'action-id'; 57 | $element = new PlainTextInputElement($actionId); 58 | 59 | $this->assertEquals([ 60 | Block::TYPE_INPUT, 61 | ], $element->compatibleWith()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/RadioButtonGroupElementTest.php: -------------------------------------------------------------------------------- 1 | 'v1']; 18 | $expectedOption2Array = ['k2' => 'v2']; 19 | 20 | $option1 = \Mockery::mock(OptionObject::class); 21 | $option2 = \Mockery::mock(OptionObject::class); 22 | 23 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 24 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 25 | 26 | $actionId = 'action-id'; 27 | 28 | $element = new RadioButtonGroupElement($actionId, [$option1, $option2]); 29 | 30 | $this->assertEquals([ 31 | 'type' => Element::TYPE_RADIO_BUTTON_GROUP, 32 | 'action_id' => $actionId, 33 | 'options' => [$expectedOption1Array, $expectedOption2Array], 34 | ], $element->toArray()); 35 | } 36 | 37 | public function testElementWithOptions() 38 | { 39 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 40 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 41 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 42 | 43 | $expectedOption1Array = ['k1' => 'v1']; 44 | $expectedOption2Array = ['k2' => 'v2']; 45 | 46 | $option1 = \Mockery::mock(OptionObject::class); 47 | $option2 = \Mockery::mock(OptionObject::class); 48 | 49 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 50 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 51 | 52 | $actionId = 'action-id'; 53 | 54 | $element = new RadioButtonGroupElement($actionId, [$option1, $option2]); 55 | 56 | $element->withInitialOption($option1); 57 | $element->withFocusOnLoad(); 58 | $element->withConfirmationDialog($confirmationDialogObject); 59 | 60 | $this->assertEquals([ 61 | 'type' => Element::TYPE_RADIO_BUTTON_GROUP, 62 | 'action_id' => $actionId, 63 | 'options' => [$expectedOption1Array, $expectedOption2Array], 64 | 'focus_on_load' => true, 65 | 'initial_option' => $expectedOption1Array, 66 | 'confirm' => $expectedConfirmationDialogArray, 67 | ], $element->toArray()); 68 | } 69 | 70 | public function testCompatibleWith() 71 | { 72 | $actionId = 'action-id'; 73 | $element = new RadioButtonGroupElement($actionId, []); 74 | 75 | $this->assertEquals([ 76 | Block::TYPE_SECTION, 77 | Block::TYPE_ACTIONS, 78 | Block::TYPE_INPUT, 79 | ], $element->compatibleWith()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuConversationElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = SelectMenuConversationElement::class; 23 | $this->expectedType = Element::TYPE_SELECT_MENU_CONVERSATIONS; 24 | } 25 | 26 | public function testElement() 27 | { 28 | $expectedPlaceholderArray = ['key' => 'value']; 29 | 30 | $placeholder = \Mockery::mock(TextObject::class); 31 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 32 | 33 | $actionId = 'action-id'; 34 | $element = new SelectMenuConversationElement($actionId, $placeholder); 35 | 36 | $this->assertEquals([ 37 | 'type' => Element::TYPE_SELECT_MENU_CONVERSATIONS, 38 | 'action_id' => $actionId, 39 | 'placeholder' => $expectedPlaceholderArray, 40 | ], $element->toArray()); 41 | } 42 | 43 | public function testElementWithOptions() 44 | { 45 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 46 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 47 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 48 | 49 | $expectedFilterArray = ['filter' => 'value']; 50 | $filter = \Mockery::mock(FilterObject::class); 51 | $filter->shouldReceive('toArray')->andReturn($expectedFilterArray); 52 | 53 | $expectedPlaceholderArray = ['key' => 'value']; 54 | 55 | $placeholder = \Mockery::mock(TextObject::class); 56 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 57 | 58 | $actionId = 'action-id'; 59 | $element = new SelectMenuConversationElement($actionId, $placeholder); 60 | 61 | $element->withInitialOption('general'); 62 | $element->withFocusOnLoad(); 63 | $element->withResponseUrlEnabled(); 64 | $element->defaultToCurrent(); 65 | $element->withConfirmationDialog($confirmationDialogObject); 66 | $element->withFilter($filter); 67 | 68 | $this->assertEquals([ 69 | 'type' => Element::TYPE_SELECT_MENU_CONVERSATIONS, 70 | 'action_id' => $actionId, 71 | 'placeholder' => $expectedPlaceholderArray, 72 | 'initial_conversation' => 'general', 73 | 'focus_on_load' => true, 74 | 'response_url_enabled' => true, 75 | 'default_to_current_conversation' => true, 76 | 'confirm' => $expectedConfirmationDialogArray, 77 | 'filter' => $expectedFilterArray, 78 | ], $element->toArray()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuExternalElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = SelectMenuExternalElement::class; 23 | $this->expectedType = Element::TYPE_SELECT_MENU_EXTERNAL; 24 | } 25 | 26 | public function testElement() 27 | { 28 | $expectedPlaceholderArray = ['key' => 'value']; 29 | 30 | $placeholder = \Mockery::mock(TextObject::class); 31 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 32 | 33 | $actionId = 'action-id'; 34 | $element = new SelectMenuExternalElement($actionId, $placeholder); 35 | 36 | $this->assertEquals([ 37 | 'type' => Element::TYPE_SELECT_MENU_EXTERNAL, 38 | 'action_id' => $actionId, 39 | 'placeholder' => $expectedPlaceholderArray, 40 | ], $element->toArray()); 41 | } 42 | 43 | public function testElementWithOptions() 44 | { 45 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 46 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 47 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 48 | 49 | $expectedPlaceholderArray = ['key' => 'value']; 50 | 51 | $placeholder = \Mockery::mock(TextObject::class); 52 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 53 | 54 | $expectedOption1Array = ['k1' => 'v1']; 55 | $expectedOption2Array = ['k2' => 'v2']; 56 | 57 | $option1 = \Mockery::mock(OptionObject::class); 58 | $option2 = \Mockery::mock(OptionObject::class); 59 | 60 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 61 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 62 | 63 | $actionId = 'action-id'; 64 | $element = new SelectMenuExternalElement($actionId, $placeholder); 65 | 66 | $element->withMinQueryLength(5); 67 | $element->withInitialOption($option1); 68 | $element->withFocusOnLoad(); 69 | $element->withConfirmationDialog($confirmationDialogObject); 70 | 71 | $this->assertEquals([ 72 | 'type' => Element::TYPE_SELECT_MENU_EXTERNAL, 73 | 'action_id' => $actionId, 74 | 'placeholder' => $expectedPlaceholderArray, 75 | 'initial_option' => $expectedOption1Array, 76 | 'focus_on_load' => true, 77 | 'min_query_length' => 5, 78 | 'confirm' => $expectedConfirmationDialogArray, 79 | ], $element->toArray()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuPublicChannelElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = SelectMenuPublicChannelElement::class; 22 | $this->expectedType = Element::TYPE_SELECT_MENU_CHANNELS; 23 | } 24 | 25 | public function testElement() 26 | { 27 | $expectedPlaceholderArray = ['key' => 'value']; 28 | 29 | $placeholder = \Mockery::mock(TextObject::class); 30 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 31 | 32 | $actionId = 'action-id'; 33 | $element = new SelectMenuPublicChannelElement($actionId, $placeholder); 34 | 35 | $this->assertEquals([ 36 | 'type' => Element::TYPE_SELECT_MENU_CHANNELS, 37 | 'action_id' => $actionId, 38 | 'placeholder' => $expectedPlaceholderArray, 39 | ], $element->toArray()); 40 | } 41 | 42 | public function testElementWithOptions() 43 | { 44 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 45 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 46 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 47 | 48 | $expectedPlaceholderArray = ['key' => 'value']; 49 | 50 | $placeholder = \Mockery::mock(TextObject::class); 51 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 52 | 53 | $actionId = 'action-id'; 54 | $element = new SelectMenuPublicChannelElement($actionId, $placeholder); 55 | 56 | $element->withInitialOption('general'); 57 | $element->withFocusOnLoad(); 58 | $element->withResponseUrlEnabled(); 59 | $element->withConfirmationDialog($confirmationDialogObject); 60 | 61 | $this->assertEquals([ 62 | 'type' => Element::TYPE_SELECT_MENU_CHANNELS, 63 | 'action_id' => $actionId, 64 | 'placeholder' => $expectedPlaceholderArray, 65 | 'initial_channel' => 'general', 66 | 'focus_on_load' => true, 67 | 'response_url_enabled' => true, 68 | 'confirm' => $expectedConfirmationDialogArray, 69 | ], $element->toArray()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuStaticElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = SelectMenuStaticElement::class; 23 | $this->expectedType = Element::TYPE_SELECT_MENU_STATIC; 24 | $this->additionalSetupArgs = [[]]; 25 | } 26 | 27 | public function testElement() 28 | { 29 | $expectedPlaceholderArray = ['key' => 'value']; 30 | 31 | $placeholder = \Mockery::mock(TextObject::class); 32 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 33 | 34 | $expectedOption1Array = ['k1' => 'v1']; 35 | $expectedOption2Array = ['k2' => 'v2']; 36 | 37 | $option1 = \Mockery::mock(OptionObject::class); 38 | $option2 = \Mockery::mock(OptionObject::class); 39 | 40 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 41 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 42 | 43 | $actionId = 'action-id'; 44 | $element = new SelectMenuStaticElement($actionId, $placeholder, [$option1, $option2]); 45 | 46 | $this->assertEquals([ 47 | 'type' => Element::TYPE_SELECT_MENU_STATIC, 48 | 'action_id' => $actionId, 49 | 'placeholder' => $expectedPlaceholderArray, 50 | 'options' => [$expectedOption1Array, $expectedOption2Array], 51 | ], $element->toArray()); 52 | } 53 | 54 | public function testElementWithOptions() 55 | { 56 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 57 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 58 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 59 | 60 | $expectedPlaceholderArray = ['key' => 'value']; 61 | 62 | $placeholder = \Mockery::mock(TextObject::class); 63 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 64 | 65 | $expectedOption1Array = ['k1' => 'v1']; 66 | $expectedOption2Array = ['k2' => 'v2']; 67 | 68 | $option1 = \Mockery::mock(OptionObject::class); 69 | $option2 = \Mockery::mock(OptionObject::class); 70 | 71 | $option1->shouldReceive('toArray')->andReturn($expectedOption1Array); 72 | $option2->shouldReceive('toArray')->andReturn($expectedOption2Array); 73 | 74 | $actionId = 'action-id'; 75 | $element = new SelectMenuStaticElement($actionId, $placeholder, [$option1, $option2]); 76 | 77 | $element->withInitialOption($option1); 78 | $element->withFocusOnLoad(); 79 | $element->withConfirmationDialog($confirmationDialogObject); 80 | 81 | $this->assertEquals([ 82 | 'type' => Element::TYPE_SELECT_MENU_STATIC, 83 | 'action_id' => $actionId, 84 | 'placeholder' => $expectedPlaceholderArray, 85 | 'options' => [$expectedOption1Array, $expectedOption2Array], 86 | 'initial_option' => $expectedOption1Array, 87 | 'focus_on_load' => true, 88 | 'confirm' => $expectedConfirmationDialogArray, 89 | ], $element->toArray()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/SelectMenu/SelectMenuUserElementTest.php: -------------------------------------------------------------------------------- 1 | elementClass = SelectMenuUserElement::class; 23 | $this->expectedType = Element::TYPE_SELECT_MENU_USERS; 24 | } 25 | 26 | public function testElement() 27 | { 28 | $expectedPlaceholderArray = ['key' => 'value']; 29 | 30 | $placeholder = \Mockery::mock(TextObject::class); 31 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 32 | 33 | $actionId = 'action-id'; 34 | $element = new SelectMenuUserElement($actionId, $placeholder); 35 | 36 | $this->assertEquals([ 37 | 'type' => Element::TYPE_SELECT_MENU_USERS, 38 | 'action_id' => $actionId, 39 | 'placeholder' => $expectedPlaceholderArray, 40 | ], $element->toArray()); 41 | } 42 | 43 | public function testElementWithOptions() 44 | { 45 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 46 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 47 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 48 | 49 | $expectedPlaceholderArray = ['key' => 'value']; 50 | 51 | $placeholder = \Mockery::mock(TextObject::class); 52 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 53 | 54 | $actionId = 'action-id'; 55 | $element = new SelectMenuUserElement($actionId, $placeholder); 56 | 57 | $element->withInitialOption('username'); 58 | $element->withFocusOnLoad(); 59 | $element->withConfirmationDialog($confirmationDialogObject); 60 | 61 | $this->assertEquals([ 62 | 'type' => Element::TYPE_SELECT_MENU_USERS, 63 | 'action_id' => $actionId, 64 | 'placeholder' => $expectedPlaceholderArray, 65 | 'initial_user' => 'username', 66 | 'focus_on_load' => true, 67 | 'confirm' => $expectedConfirmationDialogArray, 68 | ], $element->toArray()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBlocks/Elements/TimePickerElementTest.php: -------------------------------------------------------------------------------- 1 | 'value']; 18 | 19 | $placeholder = \Mockery::mock(TextObject::class); 20 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 21 | 22 | $actionId = 'action-id'; 23 | 24 | $element = new TimePickerElement($actionId, $placeholder); 25 | 26 | $this->assertEquals([ 27 | 'type' => Element::TYPE_TIME_PICKER, 28 | 'action_id' => $actionId, 29 | 'placeholder' => $expectedPlaceholderArray, 30 | ], $element->toArray()); 31 | } 32 | 33 | public function testElementWithOptions() 34 | { 35 | $expectedConfirmationDialogArray = ['key2' => 'value2']; 36 | $confirmationDialogObject = \Mockery::mock(ConfirmationDialogObject::class); 37 | $confirmationDialogObject->shouldReceive('toArray')->andReturn($expectedConfirmationDialogArray); 38 | 39 | $expectedPlaceholderArray = ['key' => 'value']; 40 | 41 | $placeholder = \Mockery::mock(TextObject::class); 42 | $placeholder->shouldReceive('toArray')->andReturn($expectedPlaceholderArray); 43 | 44 | $actionId = 'action-id'; 45 | 46 | $element = new TimePickerElement($actionId, $placeholder); 47 | 48 | $element->withFocusOnLoad(); 49 | $element->withInitialTime('10:00:00'); 50 | $element->withConfirmationDialog($confirmationDialogObject); 51 | 52 | $this->assertEquals([ 53 | 'type' => Element::TYPE_TIME_PICKER, 54 | 'action_id' => $actionId, 55 | 'placeholder' => $expectedPlaceholderArray, 56 | 'focus_on_load' => true, 57 | 'initial_time' => '10:00:00', 58 | 'confirm' => $expectedConfirmationDialogArray, 59 | ], $element->toArray()); 60 | } 61 | 62 | public function testCompatibleWith() 63 | { 64 | $element = new TimePickerElement('action-id', \Mockery::mock(TextObject::class)); 65 | $this->assertEquals([ 66 | Block::TYPE_SECTION, 67 | Block::TYPE_ACTIONS, 68 | Block::TYPE_INPUT, 69 | ], $element->compatibleWith()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/Unit/Support/LayoutBuilder/BuilderTest.php: -------------------------------------------------------------------------------- 1 | withPlainText($text); 23 | 24 | $this->assertInstanceOf(TextObject::class, $result); 25 | $this->assertEquals([ 26 | 'type' => 'plain_text', 27 | 'text' => $text, 28 | ], $result->toArray()); 29 | } 30 | 31 | public function testWithMarkdownText() 32 | { 33 | $text = 'test text'; 34 | 35 | $builder = new Builder(); 36 | $result = $builder->withMarkdownText($text); 37 | 38 | $this->assertInstanceOf(MarkdownObject::class, $result); 39 | $this->assertEquals([ 40 | 'type' => 'mrkdwn', 41 | 'text' => $text, 42 | ], $result->toArray()); 43 | } 44 | 45 | public function testDivider() 46 | { 47 | $builder = new Builder(); 48 | $builder->divider(); 49 | 50 | $blocks = $builder->getBlocks(); 51 | $this->assertCount(1, $blocks); 52 | $this->assertInstanceOf(DividerBlock::class, $blocks[0]); 53 | } 54 | 55 | public function testHeader() 56 | { 57 | $text = 'test header'; 58 | 59 | $builder = new Builder(); 60 | $builder->header($text); 61 | 62 | $blocks = $builder->getBlocks(); 63 | $this->assertCount(1, $blocks); 64 | $this->assertInstanceOf(HeaderBlock::class, $blocks[0]); 65 | $this->assertEquals([ 66 | 'type' => 'header', 67 | 'text' => [ 68 | 'type' => 'plain_text', 69 | 'text' => $text, 70 | ], 71 | ], $blocks[0]->toArray()); 72 | } 73 | 74 | public function testAddBlock() 75 | { 76 | $block1 = new ActionsBlock([]); 77 | $block2 = new DividerBlock(); 78 | $block3 = new ContextBlock([], []); 79 | 80 | $builder = new Builder(); 81 | 82 | $builder 83 | ->addBlock($block3) 84 | ->addBlock($block1) 85 | ->addBlock($block2); 86 | 87 | $blocks = $builder->getBlocks(); 88 | $this->assertCount(3, $blocks); 89 | 90 | $first = $blocks[0]; 91 | $second = $blocks[1]; 92 | $third = $blocks[2]; 93 | 94 | $this->assertSame($block3, $first); 95 | $this->assertSame($block1, $second); 96 | $this->assertSame($block2, $third); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/Unit/Support/PaginatorTest.php: -------------------------------------------------------------------------------- 1 | assertFalse($paginator->hasMorePages()); 17 | $this->assertNull($paginator->nextCursor()); 18 | $this->assertEquals([ 19 | 'items' => $items, 20 | 'next_cursor' => null, 21 | 'has_more_pages' => false, 22 | ], $paginator->toArray()); 23 | } 24 | 25 | public function testWithMultiplePages() 26 | { 27 | $items = [1, 2, 3]; 28 | $nextCursor = 'next-cursor'; 29 | 30 | $paginator = new Paginator($items, $nextCursor); 31 | 32 | $this->assertTrue($paginator->hasMorePages()); 33 | $this->assertSame($nextCursor, $paginator->nextCursor()); 34 | $this->assertEquals([ 35 | 'items' => $items, 36 | 'next_cursor' => $nextCursor, 37 | 'has_more_pages' => true, 38 | ], $paginator->toArray()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Unit/Support/SlackOptionsBuilderTest.php: -------------------------------------------------------------------------------- 1 | assertEmpty($builder->toArray()); 15 | } 16 | 17 | public function testAllOptions() 18 | { 19 | $builder = new SlackOptionsBuilder(); 20 | 21 | $builder 22 | ->username('Test Bot') 23 | ->iconUrl('https://example.com') 24 | ->iconEmoji(':test:') 25 | ->unfurlMedia() 26 | ->unfurlLinks() 27 | ->threadTs('test-ts') 28 | ->threadReplySendToChannel() 29 | ->linkNames() 30 | ->metadata(['key' => 'val']) 31 | ->parse('test-parse') 32 | ->markdown(); 33 | 34 | $this->assertEquals([ 35 | 'username' => 'Test Bot', 36 | 'icon' => [ 37 | 'url' => 'https://example.com', 38 | 'emoji' => ':test:', 39 | ], 40 | 'unfurl' => [ 41 | 'media' => true, 42 | 'links' => true, 43 | ], 44 | 'thread' => [ 45 | 'ts' => 'test-ts', 46 | 'send_to_channel' => true, 47 | ], 48 | 'link_names' => true, 49 | 'metadata' => ['key' => 'val'], 50 | 'parse' => 'test-parse', 51 | 'markdown' => true, 52 | ], $builder->toArray()); 53 | } 54 | } 55 | --------------------------------------------------------------------------------