From 9f8fd042dc67aa33ecd1cb2dcfc4fd0a5524998f Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 27 Aug 2026 22:51:47 -0700 Subject: [PATCH] feat(block-kit): add composition object examples Add composition object examples to the block-kit showcase, mirroring the block element examples: one exampleNN() per documented reference payload, each returning the full host block/view so the reference JSON round-trips verbatim, with tests asserting the complete serialized payload. Ships 9 of the 10 documented composition objects (text, option, option-group, confirmation-dialog, dispatch-action-configuration, conversation-filter, slack-file, trigger, workflow). The slack-icon object is deferred: it is a card-block sub-object and the card block is absent from the SDK, so no example payload can be produced. Co-Authored-By: Claude --- block-kit/README.md | 12 +++ .../ConfirmationDialog.java | 37 ++++++++ .../ConversationFilter.java | 52 +++++++++++ .../DispatchActionConfiguration.java | 29 ++++++ .../main/java/compositionobjects/Option.java | 58 ++++++++++++ .../java/compositionobjects/OptionGroup.java | 56 +++++++++++ .../java/compositionobjects/SlackFile.java | 37 ++++++++ .../main/java/compositionobjects/Text.java | 21 +++++ .../main/java/compositionobjects/Trigger.java | 41 ++++++++ .../java/compositionobjects/Workflow.java | 41 ++++++++ .../ConfirmationDialogTest.java | 62 +++++++++++++ .../ConversationFilterTest.java | 63 +++++++++++++ .../DispatchActionConfigurationTest.java | 38 ++++++++ .../compositionobjects/OptionGroupTest.java | 93 +++++++++++++++++++ .../java/compositionobjects/OptionTest.java | 91 ++++++++++++++++++ .../compositionobjects/SlackFileTest.java | 52 +++++++++++ .../java/compositionobjects/TextTest.java | 26 ++++++ .../java/compositionobjects/TriggerTest.java | 49 ++++++++++ .../java/compositionobjects/WorkflowTest.java | 49 ++++++++++ 19 files changed, 907 insertions(+) create mode 100644 block-kit/src/main/java/compositionobjects/ConfirmationDialog.java create mode 100644 block-kit/src/main/java/compositionobjects/ConversationFilter.java create mode 100644 block-kit/src/main/java/compositionobjects/DispatchActionConfiguration.java create mode 100644 block-kit/src/main/java/compositionobjects/Option.java create mode 100644 block-kit/src/main/java/compositionobjects/OptionGroup.java create mode 100644 block-kit/src/main/java/compositionobjects/SlackFile.java create mode 100644 block-kit/src/main/java/compositionobjects/Text.java create mode 100644 block-kit/src/main/java/compositionobjects/Trigger.java create mode 100644 block-kit/src/main/java/compositionobjects/Workflow.java create mode 100644 block-kit/src/test/java/compositionobjects/ConfirmationDialogTest.java create mode 100644 block-kit/src/test/java/compositionobjects/ConversationFilterTest.java create mode 100644 block-kit/src/test/java/compositionobjects/DispatchActionConfigurationTest.java create mode 100644 block-kit/src/test/java/compositionobjects/OptionGroupTest.java create mode 100644 block-kit/src/test/java/compositionobjects/OptionTest.java create mode 100644 block-kit/src/test/java/compositionobjects/SlackFileTest.java create mode 100644 block-kit/src/test/java/compositionobjects/TextTest.java create mode 100644 block-kit/src/test/java/compositionobjects/TriggerTest.java create mode 100644 block-kit/src/test/java/compositionobjects/WorkflowTest.java diff --git a/block-kit/README.md b/block-kit/README.md index 3cd3768..e916f8d 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -20,3 +20,15 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes - **[Section](https://docs.slack.dev/reference/block-kit/blocks/section-block)**: Displays text, possibly alongside elements. [Implementation](./src/main/java/blocks/Section.java). - **[Video](https://docs.slack.dev/reference/block-kit/blocks/video-block)**: Displays an embedded video player. [Implementation](./src/main/java/blocks/Video.java). +### Composition objects + +- **[Confirmation dialog](https://docs.slack.dev/reference/block-kit/composition-objects/confirmation-dialog-object)**: Defines a dialog that adds a confirmation step to interactive elements. [Implementation](./src/main/java/compositionobjects/ConfirmationDialog.java). +- **[Conversation filter](https://docs.slack.dev/reference/block-kit/composition-objects/conversation-filter-object)**: Defines a filter for the list of options in a conversation selector menu. [Implementation](./src/main/java/compositionobjects/ConversationFilter.java). +- **[Dispatch action configuration](https://docs.slack.dev/reference/block-kit/composition-objects/dispatch-action-configuration-object)**: Defines when a plain-text input element will return a `block_actions` interaction payload. [Implementation](./src/main/java/compositionobjects/DispatchActionConfiguration.java). +- **[Option](https://docs.slack.dev/reference/block-kit/composition-objects/option-object)**: Defines a single item in a number of item selection elements. [Implementation](./src/main/java/compositionobjects/Option.java). +- **[Option group](https://docs.slack.dev/reference/block-kit/composition-objects/option-group-object)**: Defines a way to group options in a menu. [Implementation](./src/main/java/compositionobjects/OptionGroup.java). +- **[Slack file](https://docs.slack.dev/reference/block-kit/composition-objects/slack-file-object)**: Defines an object containing Slack file information to be used in an image block or image element. [Implementation](./src/main/java/compositionobjects/SlackFile.java). +- **[Text](https://docs.slack.dev/reference/block-kit/composition-objects/text-object)**: Defines an object containing some text. [Implementation](./src/main/java/compositionobjects/Text.java). +- **[Trigger](https://docs.slack.dev/reference/block-kit/composition-objects/trigger-object)**: Defines an object containing trigger information. [Implementation](./src/main/java/compositionobjects/Trigger.java). +- **[Workflow](https://docs.slack.dev/reference/block-kit/composition-objects/workflow-object)**: Defines an object containing workflow information. [Implementation](./src/main/java/compositionobjects/Workflow.java). + diff --git a/block-kit/src/main/java/compositionobjects/ConfirmationDialog.java b/block-kit/src/main/java/compositionobjects/ConfirmationDialog.java new file mode 100644 index 0000000..b8946bd --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/ConfirmationDialog.java @@ -0,0 +1,37 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.markdownText; +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.ActionsBlock; +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.composition.ConfirmationDialogObject; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Defines a dialog that adds a confirmation step to interactive elements. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/confirmation-dialog-object/} + */ +public class ConfirmationDialog { + /** + * An actions block with a button carrying a confirmation dialog. + */ + public static ActionsBlock example01() { + ActionsBlock block = Blocks.actions(a -> a.elements(List.of( + BlockElements.button( + b -> b.text(plainText(pt -> pt.text("Approve").emoji(true))) + .confirm(ConfirmationDialogObject.builder() + .title(plainText("Are you sure?")) + .text(markdownText("Would you not prefer a good game of _chess_?")) + .confirm(plainText("Do it")) + .deny(plainText("Stop, I changed my mind!")) + .build()) + .style("primary") + .value("click_me_123")), + BlockElements.button(b -> b.text(plainText(pt -> pt.text("Deny").emoji(true))) + .style("danger") + .value("click_me_123"))))); + return block; + } +} diff --git a/block-kit/src/main/java/compositionobjects/ConversationFilter.java b/block-kit/src/main/java/compositionobjects/ConversationFilter.java new file mode 100644 index 0000000..d063bbc --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/ConversationFilter.java @@ -0,0 +1,52 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.element.BlockElements; +import com.slack.api.model.block.element.ConversationsFilter; +import com.slack.api.model.view.View; +import com.slack.api.model.view.ViewClose; +import com.slack.api.model.view.ViewSubmit; +import com.slack.api.model.view.ViewTitle; +import java.util.List; + +/** + * Defines a filter for the list of options in a conversation selector menu. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/conversation-filter-object/} + */ +public class ConversationFilter { + /** + * A modal view with a conversations select input carrying a filter. + */ + public static View example01() { + View view = View.builder() + .type("modal") + .title(ViewTitle.builder() + .type("plain_text") + .text("My App") + .emoji(true) + .build()) + .submit(ViewSubmit.builder() + .type("plain_text") + .text("Submit") + .emoji(true) + .build()) + .close(ViewClose.builder() + .type("plain_text") + .text("Cancel") + .emoji(true) + .build()) + .blocks(List.of( + Blocks.input(i -> i.element(BlockElements.conversationsSelect(c -> c.placeholder(plainText(pt -> + pt.text("Select a conversation").emoji(true))) + .filter(ConversationsFilter.builder() + .include(List.of("public", "mpim")) + .excludeBotUsers(true) + .build()))) + .label(plainText(l -> l.text("Choose the conversation to publish your result to:") + .emoji(true)))))) + .build(); + return view; + } +} diff --git a/block-kit/src/main/java/compositionobjects/DispatchActionConfiguration.java b/block-kit/src/main/java/compositionobjects/DispatchActionConfiguration.java new file mode 100644 index 0000000..460b785 --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/DispatchActionConfiguration.java @@ -0,0 +1,29 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.InputBlock; +import com.slack.api.model.block.composition.DispatchActionConfig; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Defines when a plain-text input element will return a block_actions interaction payload. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/dispatch-action-configuration-object/} + */ +public class DispatchActionConfiguration { + /** + * An input block with a multiline plain-text input carrying a dispatch action configuration. + */ + public static InputBlock example01() { + InputBlock block = Blocks.input(i -> i.dispatchAction(true) + .element(BlockElements.plainTextInput(pt -> pt.multiline(true) + .dispatchActionConfig(DispatchActionConfig.builder() + .triggerActionsOn(List.of("on_character_entered")) + .build()))) + .label(plainText( + l -> l.text("This is a multiline plain-text input").emoji(true)))); + return block; + } +} diff --git a/block-kit/src/main/java/compositionobjects/Option.java b/block-kit/src/main/java/compositionobjects/Option.java new file mode 100644 index 0000000..8099958 --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/Option.java @@ -0,0 +1,58 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.markdownText; +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.LayoutBlock; +import com.slack.api.model.block.composition.OptionObject; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Defines a single item in a number of item selection elements. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/option-object/} + */ +public class Option { + /** + * A single option object. + */ + public static OptionObject example01() { + OptionObject option = OptionObject.builder() + .text(plainText(pt -> pt.text("Save it").emoji(true))) + .value("value-2") + .build(); + return option; + } + + /** + * Options hosted in a static select menu across several blocks. + */ + public static List example02() { + List blocks = List.of( + Blocks.section(s -> s.text(markdownText(":mag: Search results for *Cata*"))), + Blocks.divider(), + Blocks.section(s -> s.text( + markdownText( + "**\nUse Case Catalogue for the following departments/roles...")) + .accessory(BlockElements.staticSelect(m -> m.placeholder( + plainText(pt -> pt.text("Manage").emoji(true))) + .options(List.of( + OptionObject.builder() + .text(plainText( + pt -> pt.text("Edit it").emoji(true))) + .value("value-0") + .build(), + OptionObject.builder() + .text(plainText( + pt -> pt.text("Read it").emoji(true))) + .value("value-1") + .build(), + OptionObject.builder() + .text(plainText( + pt -> pt.text("Save it").emoji(true))) + .value("value-2") + .build())))))); + return blocks; + } +} diff --git a/block-kit/src/main/java/compositionobjects/OptionGroup.java b/block-kit/src/main/java/compositionobjects/OptionGroup.java new file mode 100644 index 0000000..9aedfcc --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/OptionGroup.java @@ -0,0 +1,56 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.markdownText; +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.LayoutBlock; +import com.slack.api.model.block.composition.OptionGroupObject; +import com.slack.api.model.block.composition.OptionObject; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Defines a way to group options in a menu. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/option-group-object/} + */ +public class OptionGroup { + /** + * Option groups hosted in a static select menu across several blocks. + */ + public static List example01() { + List blocks = List.of( + Blocks.section(s -> s.text(markdownText(":mag: Search results for *Cata*"))), + Blocks.divider(), + Blocks.section(s -> s.text( + markdownText( + "**\nUse Case Catalogue for the following departments/roles...")) + .accessory(BlockElements.staticSelect(m -> m.placeholder( + plainText(pt -> pt.text("Manage").emoji(true))) + .optionGroups(List.of( + OptionGroupObject.builder() + .label(plainText("Group 1")) + .options(List.of( + OptionObject.builder() + .text(plainText("*this is plain_text text*")) + .value("value-0") + .build(), + OptionObject.builder() + .text(plainText("*this is plain_text text*")) + .value("value-1") + .build(), + OptionObject.builder() + .text(plainText("*this is plain_text text*")) + .value("value-2") + .build())) + .build(), + OptionGroupObject.builder() + .label(plainText("Group 2")) + .options(List.of(OptionObject.builder() + .text(plainText("*this is plain_text text*")) + .value("value-3") + .build())) + .build())))))); + return blocks; + } +} diff --git a/block-kit/src/main/java/compositionobjects/SlackFile.java b/block-kit/src/main/java/compositionobjects/SlackFile.java new file mode 100644 index 0000000..7899b9f --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/SlackFile.java @@ -0,0 +1,37 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.ImageBlock; +import com.slack.api.model.block.composition.SlackFileObject; + +/** + * Defines an object containing Slack file information to be used in an image block or image element. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/slack-file-object/} + */ +public class SlackFile { + /** + * An image block referencing a Slack file by url. + */ + public static ImageBlock example01() { + ImageBlock block = Blocks.image(i -> i.title(plainText("Please enjoy this photo of a kitten")) + .blockId("image4") + .slackFile(SlackFileObject.builder() + .url("https://files.slack.com/files-pri/T0123456-F0123456/xyz.png") + .build()) + .altText("An incredibly cute kitten.")); + return block; + } + + /** + * An image block referencing a Slack file by id. + */ + public static ImageBlock example02() { + ImageBlock block = Blocks.image(i -> i.title(plainText("Please enjoy this photo of a kitten")) + .blockId("image4") + .slackFile(SlackFileObject.builder().id("F0123456").build()) + .altText("An incredibly cute kitten.")); + return block; + } +} diff --git a/block-kit/src/main/java/compositionobjects/Text.java b/block-kit/src/main/java/compositionobjects/Text.java new file mode 100644 index 0000000..a02b1b2 --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/Text.java @@ -0,0 +1,21 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.markdownText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.SectionBlock; + +/** + * Defines an object containing some text. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/text-object/} + */ +public class Text { + /** + * A section block hosting an mrkdwn text object. + */ + public static SectionBlock example01() { + SectionBlock block = Blocks.section( + s -> s.text(markdownText("A message *with some bold text* and _some italicized text_."))); + return block; + } +} diff --git a/block-kit/src/main/java/compositionobjects/Trigger.java b/block-kit/src/main/java/compositionobjects/Trigger.java new file mode 100644 index 0000000..b03c8ab --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/Trigger.java @@ -0,0 +1,41 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.markdownText; +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.SectionBlock; +import com.slack.api.model.block.composition.WorkflowObject; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Defines an object containing trigger information. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/trigger-object/} + */ +public class Trigger { + /** + * A section block with a workflow button whose trigger carries customizable input parameters. + */ + public static SectionBlock example01() { + SectionBlock block = + Blocks.section(s -> s.text(markdownText("A message *with some bold text* and _some italicized text_.")) + .accessory(BlockElements.workflowButton(w -> w.text(plainText("Run Workflow")) + .actionId("workflowbutton123") + .workflow(WorkflowObject.builder() + .trigger(WorkflowObject.Trigger.builder() + .url("https://slack.com/shortcuts/Ft0123ABC456/xyz...zyx") + .customizableInputParameters(List.of( + WorkflowObject.Trigger.InputParameter.builder() + .name("input_parameter_a") + .value("Value for input param A") + .build(), + WorkflowObject.Trigger.InputParameter.builder() + .name("input_parameter_b") + .value("Value for input param B") + .build())) + .build()) + .build())))); + return block; + } +} diff --git a/block-kit/src/main/java/compositionobjects/Workflow.java b/block-kit/src/main/java/compositionobjects/Workflow.java new file mode 100644 index 0000000..36f8187 --- /dev/null +++ b/block-kit/src/main/java/compositionobjects/Workflow.java @@ -0,0 +1,41 @@ +package compositionobjects; + +import static com.slack.api.model.block.composition.BlockCompositions.markdownText; +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.SectionBlock; +import com.slack.api.model.block.composition.WorkflowObject; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Defines an object containing workflow information. + * {@link https://docs.slack.dev/reference/block-kit/composition-objects/workflow-object/} + */ +public class Workflow { + /** + * A section block with a workflow button whose workflow carries a trigger. + */ + public static SectionBlock example01() { + SectionBlock block = + Blocks.section(s -> s.text(markdownText("A message *with some bold text* and _some italicized text_.")) + .accessory(BlockElements.workflowButton(w -> w.text(plainText("Run Workflow")) + .actionId("workflowbutton123") + .workflow(WorkflowObject.builder() + .trigger(WorkflowObject.Trigger.builder() + .url("https://slack.com/shortcuts/Ft0123ABC456/xyz...zyx") + .customizableInputParameters(List.of( + WorkflowObject.Trigger.InputParameter.builder() + .name("input_parameter_a") + .value("Value for input param A") + .build(), + WorkflowObject.Trigger.InputParameter.builder() + .name("input_parameter_b") + .value("Value for input param B") + .build())) + .build()) + .build())))); + return block; + } +} diff --git a/block-kit/src/test/java/compositionobjects/ConfirmationDialogTest.java b/block-kit/src/test/java/compositionobjects/ConfirmationDialogTest.java new file mode 100644 index 0000000..c6d9070 --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/ConfirmationDialogTest.java @@ -0,0 +1,62 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.ActionsBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class ConfirmationDialogTest { + @Test + public void testExample01() { + ActionsBlock block = ConfirmationDialog.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "emoji": true, + "text": "Approve" + }, + "confirm": { + "title": { + "type": "plain_text", + "text": "Are you sure?" + }, + "text": { + "type": "mrkdwn", + "text": "Would you not prefer a good game of _chess_?" + }, + "confirm": { + "type": "plain_text", + "text": "Do it" + }, + "deny": { + "type": "plain_text", + "text": "Stop, I changed my mind!" + } + }, + "style": "primary", + "value": "click_me_123" + }, + { + "type": "button", + "text": { + "type": "plain_text", + "emoji": true, + "text": "Deny" + }, + "style": "danger", + "value": "click_me_123" + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/compositionobjects/ConversationFilterTest.java b/block-kit/src/test/java/compositionobjects/ConversationFilterTest.java new file mode 100644 index 0000000..048564f --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/ConversationFilterTest.java @@ -0,0 +1,63 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.view.View; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class ConversationFilterTest { + @Test + public void testExample01() { + View view = ConversationFilter.example01(); + String actual = GsonFactory.createSnakeCase().toJson(view); + String expected = """ + { + "title": { + "type": "plain_text", + "text": "My App", + "emoji": true + }, + "submit": { + "type": "plain_text", + "text": "Submit", + "emoji": true + }, + "type": "modal", + "close": { + "type": "plain_text", + "text": "Cancel", + "emoji": true + }, + "blocks": [ + { + "type": "input", + "element": { + "type": "conversations_select", + "placeholder": { + "type": "plain_text", + "text": "Select a conversation", + "emoji": true + }, + "filter": { + "include": [ + "public", + "mpim" + ], + "exclude_bot_users": true + } + }, + "label": { + "type": "plain_text", + "text": "Choose the conversation to publish your result to:", + "emoji": true + }, + "optional": false + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/compositionobjects/DispatchActionConfigurationTest.java b/block-kit/src/test/java/compositionobjects/DispatchActionConfigurationTest.java new file mode 100644 index 0000000..817208b --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/DispatchActionConfigurationTest.java @@ -0,0 +1,38 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.InputBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class DispatchActionConfigurationTest { + @Test + public void testExample01() { + InputBlock block = DispatchActionConfiguration.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "input", + "dispatch_action": true, + "element": { + "type": "plain_text_input", + "multiline": true, + "dispatch_action_config": { + "trigger_actions_on": [ + "on_character_entered" + ] + } + }, + "label": { + "type": "plain_text", + "text": "This is a multiline plain-text input", + "emoji": true + }, + "optional": false + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/compositionobjects/OptionGroupTest.java b/block-kit/src/test/java/compositionobjects/OptionGroupTest.java new file mode 100644 index 0000000..36b06e0 --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/OptionGroupTest.java @@ -0,0 +1,93 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.LayoutBlock; +import com.slack.api.util.json.GsonFactory; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class OptionGroupTest { + @Test + public void testExample01() { + List blocks = OptionGroup.example01(); + String actual = GsonFactory.createSnakeCase().toJson(blocks); + String expected = """ + [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":mag: Search results for *Cata*" + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "**\\nUse Case Catalogue for the following departments/roles..." + }, + "accessory": { + "type": "static_select", + "placeholder": { + "type": "plain_text", + "emoji": true, + "text": "Manage" + }, + "option_groups": [ + { + "label": { + "type": "plain_text", + "text": "Group 1" + }, + "options": [ + { + "text": { + "type": "plain_text", + "text": "*this is plain_text text*" + }, + "value": "value-0" + }, + { + "text": { + "type": "plain_text", + "text": "*this is plain_text text*" + }, + "value": "value-1" + }, + { + "text": { + "type": "plain_text", + "text": "*this is plain_text text*" + }, + "value": "value-2" + } + ] + }, + { + "label": { + "type": "plain_text", + "text": "Group 2" + }, + "options": [ + { + "text": { + "type": "plain_text", + "text": "*this is plain_text text*" + }, + "value": "value-3" + } + ] + } + ] + } + } + ] + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/compositionobjects/OptionTest.java b/block-kit/src/test/java/compositionobjects/OptionTest.java new file mode 100644 index 0000000..b612ae8 --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/OptionTest.java @@ -0,0 +1,91 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.LayoutBlock; +import com.slack.api.model.block.composition.OptionObject; +import com.slack.api.util.json.GsonFactory; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class OptionTest { + @Test + public void testExample01() { + OptionObject option = Option.example01(); + String actual = GsonFactory.createSnakeCase().toJson(option); + String expected = """ + { + "text": { + "type": "plain_text", + "emoji": true, + "text": "Save it" + }, + "value": "value-2" + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample02() { + List blocks = Option.example02(); + String actual = GsonFactory.createSnakeCase().toJson(blocks); + String expected = """ + [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":mag: Search results for *Cata*" + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "**\\nUse Case Catalogue for the following departments/roles..." + }, + "accessory": { + "type": "static_select", + "placeholder": { + "type": "plain_text", + "emoji": true, + "text": "Manage" + }, + "options": [ + { + "text": { + "type": "plain_text", + "emoji": true, + "text": "Edit it" + }, + "value": "value-0" + }, + { + "text": { + "type": "plain_text", + "emoji": true, + "text": "Read it" + }, + "value": "value-1" + }, + { + "text": { + "type": "plain_text", + "emoji": true, + "text": "Save it" + }, + "value": "value-2" + } + ] + } + } + ] + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/compositionobjects/SlackFileTest.java b/block-kit/src/test/java/compositionobjects/SlackFileTest.java new file mode 100644 index 0000000..a88b1fc --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/SlackFileTest.java @@ -0,0 +1,52 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.ImageBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class SlackFileTest { + @Test + public void testExample01() { + ImageBlock block = SlackFile.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "image", + "title": { + "type": "plain_text", + "text": "Please enjoy this photo of a kitten" + }, + "block_id": "image4", + "slack_file": { + "url": "https://files.slack.com/files-pri/T0123456-F0123456/xyz.png" + }, + "alt_text": "An incredibly cute kitten." + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample02() { + ImageBlock block = SlackFile.example02(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "image", + "title": { + "type": "plain_text", + "text": "Please enjoy this photo of a kitten" + }, + "block_id": "image4", + "slack_file": { + "id": "F0123456" + }, + "alt_text": "An incredibly cute kitten." + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/compositionobjects/TextTest.java b/block-kit/src/test/java/compositionobjects/TextTest.java new file mode 100644 index 0000000..e94912a --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/TextTest.java @@ -0,0 +1,26 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.SectionBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class TextTest { + @Test + public void testExample01() { + SectionBlock block = Text.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "A message *with some bold text* and _some italicized text_." + } + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/compositionobjects/TriggerTest.java b/block-kit/src/test/java/compositionobjects/TriggerTest.java new file mode 100644 index 0000000..82b3aac --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/TriggerTest.java @@ -0,0 +1,49 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.SectionBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class TriggerTest { + @Test + public void testExample01() { + SectionBlock block = Trigger.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "section", + "text": { + "text": "A message *with some bold text* and _some italicized text_.", + "type": "mrkdwn" + }, + "accessory": { + "type": "workflow_button", + "text": { + "type": "plain_text", + "text": "Run Workflow" + }, + "action_id": "workflowbutton123", + "workflow": { + "trigger": { + "url": "https://slack.com/shortcuts/Ft0123ABC456/xyz...zyx", + "customizable_input_parameters": [ + { + "name": "input_parameter_a", + "value": "Value for input param A" + }, + { + "name": "input_parameter_b", + "value": "Value for input param B" + } + ] + } + } + } + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/compositionobjects/WorkflowTest.java b/block-kit/src/test/java/compositionobjects/WorkflowTest.java new file mode 100644 index 0000000..51f3988 --- /dev/null +++ b/block-kit/src/test/java/compositionobjects/WorkflowTest.java @@ -0,0 +1,49 @@ +package compositionobjects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.SectionBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class WorkflowTest { + @Test + public void testExample01() { + SectionBlock block = Workflow.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "section", + "text": { + "text": "A message *with some bold text* and _some italicized text_.", + "type": "mrkdwn" + }, + "accessory": { + "type": "workflow_button", + "text": { + "type": "plain_text", + "text": "Run Workflow" + }, + "action_id": "workflowbutton123", + "workflow": { + "trigger": { + "url": "https://slack.com/shortcuts/Ft0123ABC456/xyz...zyx", + "customizable_input_parameters": [ + { + "name": "input_parameter_a", + "value": "Value for input param A" + }, + { + "name": "input_parameter_b", + "value": "Value for input param B" + } + ] + } + } + } + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +}