From 4320158bce8d46b3a9a30a018494440c17847dad Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 4 Nov 2025 15:51:39 -0800 Subject: [PATCH 1/2] feat(block-kit): add examples of released blocks --- block-kit/README.md | 11 +- block-kit/src/main/java/blocks/Context.java | 24 + block-kit/src/main/java/blocks/Divider.java | 18 + block-kit/src/main/java/blocks/File.java | 18 + block-kit/src/main/java/blocks/Header.java | 19 + block-kit/src/main/java/blocks/Image.java | 47 ++ block-kit/src/main/java/blocks/Input.java | 21 + block-kit/src/main/java/blocks/Markdown.java | 18 + block-kit/src/main/java/blocks/RichText.java | 284 +++++++++ block-kit/src/main/java/blocks/Section.java | 46 ++ block-kit/src/main/java/blocks/Video.java | 27 + .../src/test/java/blocks/ContextTest.java | 34 ++ .../src/test/java/blocks/DividerTest.java | 22 + block-kit/src/test/java/blocks/FileTest.java | 25 + .../src/test/java/blocks/HeaderTest.java | 27 + block-kit/src/test/java/blocks/ImageTest.java | 74 +++ block-kit/src/test/java/blocks/InputTest.java | 33 ++ .../src/test/java/blocks/MarkdownTest.java | 24 + .../src/test/java/blocks/RichTextTest.java | 538 ++++++++++++++++++ .../src/test/java/blocks/SectionTest.java | 81 +++ block-kit/src/test/java/blocks/VideoTest.java | 37 ++ 21 files changed, 1427 insertions(+), 1 deletion(-) create mode 100644 block-kit/src/main/java/blocks/Context.java create mode 100644 block-kit/src/main/java/blocks/Divider.java create mode 100644 block-kit/src/main/java/blocks/File.java create mode 100644 block-kit/src/main/java/blocks/Header.java create mode 100644 block-kit/src/main/java/blocks/Image.java create mode 100644 block-kit/src/main/java/blocks/Input.java create mode 100644 block-kit/src/main/java/blocks/Markdown.java create mode 100644 block-kit/src/main/java/blocks/RichText.java create mode 100644 block-kit/src/main/java/blocks/Section.java create mode 100644 block-kit/src/main/java/blocks/Video.java create mode 100644 block-kit/src/test/java/blocks/ContextTest.java create mode 100644 block-kit/src/test/java/blocks/DividerTest.java create mode 100644 block-kit/src/test/java/blocks/FileTest.java create mode 100644 block-kit/src/test/java/blocks/HeaderTest.java create mode 100644 block-kit/src/test/java/blocks/ImageTest.java create mode 100644 block-kit/src/test/java/blocks/InputTest.java create mode 100644 block-kit/src/test/java/blocks/MarkdownTest.java create mode 100644 block-kit/src/test/java/blocks/RichTextTest.java create mode 100644 block-kit/src/test/java/blocks/SectionTest.java create mode 100644 block-kit/src/test/java/blocks/VideoTest.java diff --git a/block-kit/README.md b/block-kit/README.md index 117b943..a062add 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -9,4 +9,13 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes ### Blocks - **[Actions block](https://docs.slack.dev/reference/block-kit/blocks/actions-block)**: Holds multiple interactive elements. [Implementation](./src/main/java/blocks/Actions.java). - +- **[Context](https://docs.slack.dev/reference/block-kit/blocks/context-block)**: Provides contextual info, which can include both images and text. [Implementation](./src/main/java/blocks/Context.java). +- **[Divider](https://docs.slack.dev/reference/block-kit/blocks/divider-block)**: Visually separates pieces of info inside of a message. [Implementation](./src/main/java/blocks/Divider.java). +- **[File](https://docs.slack.dev/reference/block-kit/blocks/file-block)**: Displays info about remote files. [Implementation](./src/main/java/blocks/File.java). +- **[Header](https://docs.slack.dev/reference/block-kit/blocks/header-block)**: Displays a larger-sized text. [Implementation](./src/main/java/blocks/Header.java). +- **[Image](https://docs.slack.dev/reference/block-kit/blocks/image-block)**: Displays an image. [Implementation](./src/main/java/blocks/Image.java). +- **[Input](https://docs.slack.dev/reference/block-kit/blocks/input-block)**: Collects information from users via elements. [Implementation](./src/main/java/blocks/Input.java). +- **[Markdown](https://docs.slack.dev/reference/block-kit/blocks/markdown-block)**: Displays formatted markdown. [Implementation](./src/main/java/blocks/Markdown.java). +- **[Rich text](https://docs.slack.dev/reference/block-kit/blocks/rich-text-block)**: Displays formatted, structured representation of text. [Implementation](./src/main/java/blocks/RichText.java). +- **[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). diff --git a/block-kit/src/main/java/blocks/Context.java b/block-kit/src/main/java/blocks/Context.java new file mode 100644 index 0000000..a23a94f --- /dev/null +++ b/block-kit/src/main/java/blocks/Context.java @@ -0,0 +1,24 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.ContextBlock; +import com.slack.api.model.block.composition.BlockCompositions; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Provides contextual info, which can include both images and text. + * {@link https://docs.slack.dev/reference/block-kit/blocks/context-block/} + */ +public class Context { + /** + * A context block with an image and text. + */ + public static ContextBlock example01() { + ContextBlock block = Blocks.context(c -> c.elements(List.of( + BlockElements.image(i -> i.imageUrl("https://image.freepik.com/free-photo/red-drawing-pin_1156-445.jpg") + .altText("images")), + BlockCompositions.markdownText("Location: **Dogpatch**")))); + return block; + } +} diff --git a/block-kit/src/main/java/blocks/Divider.java b/block-kit/src/main/java/blocks/Divider.java new file mode 100644 index 0000000..e3bfc56 --- /dev/null +++ b/block-kit/src/main/java/blocks/Divider.java @@ -0,0 +1,18 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.DividerBlock; + +/** + * Visually separates pieces of info inside of a message. + * {@link https://docs.slack.dev/reference/block-kit/blocks/divider-block/} + */ +public class Divider { + /** + * A simple divider block. + */ + public static DividerBlock example01() { + DividerBlock block = Blocks.divider(); + return block; + } +} diff --git a/block-kit/src/main/java/blocks/File.java b/block-kit/src/main/java/blocks/File.java new file mode 100644 index 0000000..d395483 --- /dev/null +++ b/block-kit/src/main/java/blocks/File.java @@ -0,0 +1,18 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.FileBlock; + +/** + * Displays info about remote files. + * {@link https://docs.slack.dev/reference/block-kit/blocks/file-block/} + */ +public class File { + /** + * A file block for a remote file. + */ + public static FileBlock example01() { + FileBlock block = Blocks.file(f -> f.externalId("ABCD1").source("remote")); + return block; + } +} diff --git a/block-kit/src/main/java/blocks/Header.java b/block-kit/src/main/java/blocks/Header.java new file mode 100644 index 0000000..85351a5 --- /dev/null +++ b/block-kit/src/main/java/blocks/Header.java @@ -0,0 +1,19 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.HeaderBlock; +import com.slack.api.model.block.composition.BlockCompositions; + +/** + * Displays a larger-sized text. + * {@link https://docs.slack.dev/reference/block-kit/blocks/header-block/} + */ +public class Header { + /** + * A simple header block. + */ + public static HeaderBlock example01() { + HeaderBlock block = Blocks.header(h -> h.text(BlockCompositions.plainText("A Heartfelt Header"))); + return block; + } +} diff --git a/block-kit/src/main/java/blocks/Image.java b/block-kit/src/main/java/blocks/Image.java new file mode 100644 index 0000000..e8c2dae --- /dev/null +++ b/block-kit/src/main/java/blocks/Image.java @@ -0,0 +1,47 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.ImageBlock; +import com.slack.api.model.block.composition.BlockCompositions; +import com.slack.api.model.block.composition.SlackFileObject; + +/** + * Displays an image. + * {@link https://docs.slack.dev/reference/block-kit/blocks/image-block/} + */ +public class Image { + /** + * An image block using image_url. + */ + public static ImageBlock example01() { + ImageBlock block = Blocks.image(i -> i.title(BlockCompositions.plainText("Please enjoy this photo of a kitten")) + .blockId("image4") + .imageUrl("http://placekitten.com/500/500") + .altText("An incredibly cute kitten.")); + return block; + } + + /** + * An image block using slack_file with a url. + */ + public static ImageBlock example02() { + ImageBlock block = Blocks.image(i -> i.title(BlockCompositions.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 using slack_file with an id. + */ + public static ImageBlock example03() { + ImageBlock block = Blocks.image(i -> i.title(BlockCompositions.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/blocks/Input.java b/block-kit/src/main/java/blocks/Input.java new file mode 100644 index 0000000..611d770 --- /dev/null +++ b/block-kit/src/main/java/blocks/Input.java @@ -0,0 +1,21 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.InputBlock; +import com.slack.api.model.block.composition.BlockCompositions; +import com.slack.api.model.block.element.BlockElements; + +/** + * Collects information from users via elements. + * {@link https://docs.slack.dev/reference/block-kit/blocks/input-block/} + */ +public class Input { + /** + * An input block containing a plain-text input element. + */ + public static InputBlock example01() { + InputBlock block = Blocks.input(i -> i.element(BlockElements.plainTextInput(pt -> pt)) + .label(BlockCompositions.plainText(pt -> pt.text("Label").emoji(true)))); + return block; + } +} diff --git a/block-kit/src/main/java/blocks/Markdown.java b/block-kit/src/main/java/blocks/Markdown.java new file mode 100644 index 0000000..e5577b2 --- /dev/null +++ b/block-kit/src/main/java/blocks/Markdown.java @@ -0,0 +1,18 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.MarkdownBlock; + +/** + * Displays formatted markdown. + * {@link https://docs.slack.dev/reference/block-kit/blocks/markdown-block/} + */ +public class Markdown { + /** + * A markdown block. + */ + public static MarkdownBlock example01() { + MarkdownBlock block = Blocks.markdown(m -> m.text("**Lots of information here!!**")); + return block; + } +} diff --git a/block-kit/src/main/java/blocks/RichText.java b/block-kit/src/main/java/blocks/RichText.java new file mode 100644 index 0000000..63e9944 --- /dev/null +++ b/block-kit/src/main/java/blocks/RichText.java @@ -0,0 +1,284 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.RichTextBlock; +import com.slack.api.model.block.element.RichTextListElement; +import com.slack.api.model.block.element.RichTextPreformattedElement; +import com.slack.api.model.block.element.RichTextQuoteElement; +import com.slack.api.model.block.element.RichTextSectionElement; +import com.slack.api.model.block.element.RichTextSectionElement.TextStyle; +import java.util.List; + +/** + * Displays formatted, structured representation of text. + * {@link https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/} + */ +public class RichText { + /** + * Four basic rich text section examples (basic, bold, italic, strikethrough). + */ + public static List example01() { + List blocks = List.of( + Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Hello there, I am a basic rich text block!") + .build())) + .build()))), + Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of( + RichTextSectionElement.Text.builder() + .text("Hello there, ") + .build(), + RichTextSectionElement.Text.builder() + .text("I am a bold rich text block!") + .style(TextStyle.builder().bold(true).build()) + .build())) + .build()))), + Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of( + RichTextSectionElement.Text.builder() + .text("Hello there, ") + .build(), + RichTextSectionElement.Text.builder() + .text("I am an italic rich text block!") + .style(TextStyle.builder().italic(true).build()) + .build())) + .build()))), + Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of( + RichTextSectionElement.Text.builder() + .text("Hello there, ") + .build(), + RichTextSectionElement.Text.builder() + .text("I am a strikethrough rich text block!") + .style(TextStyle.builder().strike(true).build()) + .build())) + .build())))); + return blocks; + } + + /** + * A rich text block with a bullet list. + */ + public static RichTextBlock example02() { + RichTextBlock block = Blocks.richText(rt -> rt.blockId("block1") + .elements(List.of( + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("My favorite Slack features (in no particular order):") + .build())) + .build(), + RichTextListElement.builder() + .style("bullet") + .indent(0) + .border(1) + .elements(List.of( + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Huddles") + .build())) + .build(), + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Canvas") + .build())) + .build(), + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Developing with Block Kit") + .build())) + .build())) + .build()))); + return block; + } + + /** + * A rich text block with a nested bullet list. + */ + public static RichTextBlock example03() { + RichTextBlock block = Blocks.richText(rt -> rt.blockId("block1") + .elements(List.of( + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Breakfast foods I enjoy:") + .build())) + .build(), + RichTextListElement.builder() + .style("bullet") + .elements(List.of( + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Hashbrowns") + .build())) + .build(), + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Eggs") + .build())) + .build())) + .build(), + RichTextListElement.builder() + .style("bullet") + .indent(1) + .elements(List.of( + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Scrambled") + .build())) + .build(), + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Over easy") + .build())) + .build())) + .build(), + RichTextListElement.builder() + .style("bullet") + .elements(List.of(RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Pancakes, extra syrup") + .build())) + .build())) + .build()))); + return block; + } + + /** + * A rich text block with preformatted code. + */ + public static RichTextBlock example04() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextPreformattedElement.builder() + .border(0) + .elements(List.of(RichTextSectionElement.Text.builder() + .text( + "{\n \"object\": {\n \"description\": \"this is an example of a json object\"\n }\n}") + .build())) + .build()))); + return block; + } + + /** + * A rich text block with a quote. + */ + public static RichTextBlock example05() { + RichTextBlock block = Blocks.richText(rt -> rt.blockId("Vrzsu") + .elements(List.of( + RichTextQuoteElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("What we need is good examples in our documentation.") + .build())) + .build(), + RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Text.builder() + .text("Yes - I completely agree, Luke!") + .build())) + .build()))); + return block; + } + + /** + * A rich text block with a broadcast element. + */ + public static RichTextBlock example06() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Broadcast.builder() + .range("everyone") + .build())) + .build()))); + return block; + } + + /** + * A rich text block with a color element. + */ + public static RichTextBlock example07() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of( + RichTextSectionElement.Color.builder().value("#F405B3").build())) + .build()))); + return block; + } + + /** + * A rich text block with a channel element. + */ + public static RichTextBlock example08() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Channel.builder() + .channelId("C123ABC456") + .build())) + .build()))); + return block; + } + + /** + * A rich text block with a date element. + */ + public static RichTextBlock example09() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Date.builder() + .timestamp(1720710212) + .format("{date_num} at {time}") + .fallback("timey") + .build())) + .build()))); + return block; + } + + /** + * A rich text block with emoji elements. + */ + public static RichTextBlock example10() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of( + RichTextSectionElement.Emoji.builder() + .name("basketball") + .build(), + RichTextSectionElement.Text.builder().text(" ").build(), + RichTextSectionElement.Emoji.builder() + .name("snowboarder") + .build(), + RichTextSectionElement.Text.builder().text(" ").build(), + RichTextSectionElement.Emoji.builder() + .name("checkered_flag") + .build())) + .build()))); + return block; + } + + /** + * A rich text block with a link element. + */ + public static RichTextBlock example11() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.Link.builder() + .url("https://api.slack.com") + .build())) + .build()))); + return block; + } + + /** + * A rich text block with a user mention element. + */ + public static RichTextBlock example12() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.User.builder() + .userId("U123ABC456") + .build())) + .build()))); + return block; + } + + /** + * A rich text block with a usergroup mention element. + */ + public static RichTextBlock example13() { + RichTextBlock block = Blocks.richText(rt -> rt.elements(List.of(RichTextSectionElement.builder() + .elements(List.of(RichTextSectionElement.UserGroup.builder() + .usergroupId("G123ABC456") + .build())) + .build()))); + return block; + } +} diff --git a/block-kit/src/main/java/blocks/Section.java b/block-kit/src/main/java/blocks/Section.java new file mode 100644 index 0000000..357b78a --- /dev/null +++ b/block-kit/src/main/java/blocks/Section.java @@ -0,0 +1,46 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.SectionBlock; +import com.slack.api.model.block.composition.BlockCompositions; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Displays text, possibly alongside elements. + * {@link https://docs.slack.dev/reference/block-kit/blocks/section-block/} + */ +public class Section { + /** + * A text section block. + */ + public static SectionBlock example01() { + SectionBlock block = Blocks.section(s -> + s.text(BlockCompositions.markdownText("A message *with some bold text* and _some italicized text_."))); + return block; + } + + /** + * A section block containing text fields. + */ + public static SectionBlock example02() { + SectionBlock block = Blocks.section(s -> s.text( + BlockCompositions.markdownText("A message *with some bold text* and _some italicized text_.")) + .fields(List.of( + BlockCompositions.markdownText("High"), + BlockCompositions.plainText(pt -> pt.text("Silly").emoji(true))))); + return block; + } + + /** + * A section block containing a datepicker element. + */ + public static SectionBlock example03() { + SectionBlock block = Blocks.section(s -> s.text( + BlockCompositions.markdownText("*Haley* has requested you set a deadline for finding a house")) + .accessory(BlockElements.datePicker(d -> d.actionId("datepicker123") + .initialDate("1990-04-28") + .placeholder(BlockCompositions.plainText("Select a date"))))); + return block; + } +} diff --git a/block-kit/src/main/java/blocks/Video.java b/block-kit/src/main/java/blocks/Video.java new file mode 100644 index 0000000..f96f8b3 --- /dev/null +++ b/block-kit/src/main/java/blocks/Video.java @@ -0,0 +1,27 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.VideoBlock; +import com.slack.api.model.block.composition.BlockCompositions; + +/** + * Displays an embedded video player. + * {@link https://docs.slack.dev/reference/block-kit/blocks/video-block/} + */ +public class Video { + /** + * A video block. + */ + public static VideoBlock example01() { + VideoBlock block = Blocks.video(v -> v.title( + BlockCompositions.plainText(pt -> pt.text("Use the Events API to create a dynamic App Home") + .emoji(true))) + .titleUrl("https://www.youtube.com/watch?v=8876OZV_Yy0") + .description(BlockCompositions.plainText( + pt -> pt.text("Slack sure is nifty!").emoji(true))) + .videoUrl("https://www.youtube.com/embed/8876OZV_Yy0?feature=oembed&autoplay=1") + .altText("Use the Events API to create a dynamic App Home") + .thumbnailUrl("https://i.ytimg.com/vi/8876OZV_Yy0/hqdefault.jpg")); + return block; + } +} diff --git a/block-kit/src/test/java/blocks/ContextTest.java b/block-kit/src/test/java/blocks/ContextTest.java new file mode 100644 index 0000000..a237e82 --- /dev/null +++ b/block-kit/src/test/java/blocks/ContextTest.java @@ -0,0 +1,34 @@ +package blocks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.ContextBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class ContextTest { + @Test + public void testExample01() { + ContextBlock block = Context.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "context", + "elements": [ + { + "type": "image", + "image_url": "https://image.freepik.com/free-photo/red-drawing-pin_1156-445.jpg", + "alt_text": "images" + }, + { + "type": "mrkdwn", + "text": "Location: **Dogpatch**" + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/blocks/DividerTest.java b/block-kit/src/test/java/blocks/DividerTest.java new file mode 100644 index 0000000..c9f5042 --- /dev/null +++ b/block-kit/src/test/java/blocks/DividerTest.java @@ -0,0 +1,22 @@ +package blocks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.DividerBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class DividerTest { + @Test + public void testExample01() { + DividerBlock block = Divider.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "divider" + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/blocks/FileTest.java b/block-kit/src/test/java/blocks/FileTest.java new file mode 100644 index 0000000..910d7e5 --- /dev/null +++ b/block-kit/src/test/java/blocks/FileTest.java @@ -0,0 +1,25 @@ +package blocks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.FileBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class FileTest { + @Test + public void testExample01() { + FileBlock block = File.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "file", + "external_id": "ABCD1", + "source": "remote" + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/blocks/HeaderTest.java b/block-kit/src/test/java/blocks/HeaderTest.java new file mode 100644 index 0000000..45f9da1 --- /dev/null +++ b/block-kit/src/test/java/blocks/HeaderTest.java @@ -0,0 +1,27 @@ +package blocks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.HeaderBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class HeaderTest { + @Test + public void testExample01() { + HeaderBlock block = Header.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "A Heartfelt Header" + } + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/blocks/ImageTest.java b/block-kit/src/test/java/blocks/ImageTest.java new file mode 100644 index 0000000..8f53847 --- /dev/null +++ b/block-kit/src/test/java/blocks/ImageTest.java @@ -0,0 +1,74 @@ +package blocks; + +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 ImageTest { + @Test + public void testExample01() { + ImageBlock block = Image.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", + "image_url": "http://placekitten.com/500/500", + "alt_text": "An incredibly cute kitten." + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample02() { + ImageBlock block = Image.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": { + "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 testExample03() { + ImageBlock block = Image.example03(); + 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/blocks/InputTest.java b/block-kit/src/test/java/blocks/InputTest.java new file mode 100644 index 0000000..db8d6bc --- /dev/null +++ b/block-kit/src/test/java/blocks/InputTest.java @@ -0,0 +1,33 @@ +package blocks; + +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 InputTest { + @Test + public void testExample01() { + InputBlock block = Input.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "input", + "element": { + "type": "plain_text_input", + "multiline": false + }, + "label": { + "type": "plain_text", + "text": "Label", + "emoji": true + }, + "optional": false + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/blocks/MarkdownTest.java b/block-kit/src/test/java/blocks/MarkdownTest.java new file mode 100644 index 0000000..3b1fed8 --- /dev/null +++ b/block-kit/src/test/java/blocks/MarkdownTest.java @@ -0,0 +1,24 @@ +package blocks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.MarkdownBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class MarkdownTest { + @Test + public void testExample01() { + MarkdownBlock block = Markdown.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "markdown", + "text": "**Lots of information here!!**" + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/blocks/RichTextTest.java b/block-kit/src/test/java/blocks/RichTextTest.java new file mode 100644 index 0000000..67b1726 --- /dev/null +++ b/block-kit/src/test/java/blocks/RichTextTest.java @@ -0,0 +1,538 @@ +package blocks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.RichTextBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class RichTextTest { + @Test + public void testExample01() { + java.util.List blocks = RichText.example01(); + String actual = GsonFactory.createSnakeCase().toJson(blocks); + String expected = + """ + [ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, I am a basic rich text block!" + } + ] + } + ] + }, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, " + }, + { + "type": "text", + "text": "I am a bold rich text block!", + "style": { + "bold": true, + "italic": false, + "strike": false, + "highlight": false, + "client_highlight": false, + "underline": false, + "unlink": false, + "code": false + } + } + ] + } + ] + }, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, " + }, + { + "type": "text", + "text": "I am an italic rich text block!", + "style": { + "bold": false, + "italic": true, + "strike": false, + "highlight": false, + "client_highlight": false, + "underline": false, + "unlink": false, + "code": false + } + } + ] + } + ] + }, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, " + }, + { + "type": "text", + "text": "I am a strikethrough rich text block!", + "style": { + "bold": false, + "italic": false, + "strike": true, + "highlight": false, + "client_highlight": false, + "underline": false, + "unlink": false, + "code": false + } + } + ] + } + ] + } + ] + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample02() { + RichTextBlock block = RichText.example02(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "block_id": "block1", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "My favorite Slack features (in no particular order):" + } + ] + }, + { + "type": "rich_text_list", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Huddles" + } + ] + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Canvas" + } + ] + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Developing with Block Kit" + } + ] + } + ], + "style": "bullet", + "indent": 0, + "border": 1 + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample03() { + RichTextBlock block = RichText.example03(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "block_id": "block1", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Breakfast foods I enjoy:" + } + ] + }, + { + "type": "rich_text_list", + "style": "bullet", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hashbrowns" + } + ] + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Eggs" + } + ] + } + ] + }, + { + "type": "rich_text_list", + "style": "bullet", + "indent": 1, + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Scrambled" + } + ] + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Over easy" + } + ] + } + ] + }, + { + "type": "rich_text_list", + "style": "bullet", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Pancakes, extra syrup" + } + ] + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample04() { + RichTextBlock block = RichText.example04(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_preformatted", + "elements": [ + { + "type": "text", + "text": "{\\n \\"object\\": {\\n \\"description\\": \\"this is an example of a json object\\"\\n }\\n}" + } + ], + "border": 0 + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample05() { + RichTextBlock block = RichText.example05(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "block_id": "Vrzsu", + "elements": [ + { + "type": "rich_text_quote", + "elements": [ + { + "type": "text", + "text": "What we need is good examples in our documentation." + } + ] + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Yes - I completely agree, Luke!" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample06() { + RichTextBlock block = RichText.example06(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "broadcast", + "range": "everyone" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample07() { + RichTextBlock block = RichText.example07(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "color", + "value": "#F405B3" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample08() { + RichTextBlock block = RichText.example08(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "channel", + "channel_id": "C123ABC456" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample09() { + RichTextBlock block = RichText.example09(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "date", + "timestamp": 1720710212, + "format": "{date_num} at {time}", + "fallback": "timey" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample10() { + RichTextBlock block = RichText.example10(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "emoji", + "name": "basketball" + }, + { + "type": "text", + "text": " " + }, + { + "type": "emoji", + "name": "snowboarder" + }, + { + "type": "text", + "text": " " + }, + { + "type": "emoji", + "name": "checkered_flag" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample11() { + RichTextBlock block = RichText.example11(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "link", + "url": "https://api.slack.com" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample12() { + RichTextBlock block = RichText.example12(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "user", + "user_id": "U123ABC456" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample13() { + RichTextBlock block = RichText.example13(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "usergroup", + "usergroup_id": "G123ABC456" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/blocks/SectionTest.java b/block-kit/src/test/java/blocks/SectionTest.java new file mode 100644 index 0000000..15042e2 --- /dev/null +++ b/block-kit/src/test/java/blocks/SectionTest.java @@ -0,0 +1,81 @@ +package blocks; + +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 SectionTest { + @Test + public void testExample01() { + SectionBlock block = Section.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)); + } + + @Test + public void testExample02() { + SectionBlock block = Section.example02(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "section", + "text": { + "text": "A message *with some bold text* and _some italicized text_.", + "type": "mrkdwn" + }, + "fields": [ + { + "type": "mrkdwn", + "text": "High" + }, + { + "type": "plain_text", + "emoji": true, + "text": "Silly" + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } + + @Test + public void testExample03() { + SectionBlock block = Section.example03(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "section", + "text": { + "text": "*Haley* has requested you set a deadline for finding a house", + "type": "mrkdwn" + }, + "accessory": { + "type": "datepicker", + "action_id": "datepicker123", + "initial_date": "1990-04-28", + "placeholder": { + "type": "plain_text", + "text": "Select a date" + } + } + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} diff --git a/block-kit/src/test/java/blocks/VideoTest.java b/block-kit/src/test/java/blocks/VideoTest.java new file mode 100644 index 0000000..bcbf2cc --- /dev/null +++ b/block-kit/src/test/java/blocks/VideoTest.java @@ -0,0 +1,37 @@ +package blocks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.VideoBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class VideoTest { + @Test + public void testExample01() { + VideoBlock block = Video.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = + """ + { + "type": "video", + "title": { + "type": "plain_text", + "text": "Use the Events API to create a dynamic App Home", + "emoji": true + }, + "title_url": "https://www.youtube.com/watch?v=8876OZV_Yy0", + "description": { + "type": "plain_text", + "text": "Slack sure is nifty!", + "emoji": true + }, + "video_url": "https://www.youtube.com/embed/8876OZV_Yy0?feature=oembed&autoplay=1", + "alt_text": "Use the Events API to create a dynamic App Home", + "thumbnail_url": "https://i.ytimg.com/vi/8876OZV_Yy0/hqdefault.jpg" + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +} From 4d054d1a56f23f54ea2dfce29bee83ddb7e3444f Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 4 Nov 2025 15:58:37 -0800 Subject: [PATCH 2/2] chore: lint eof with new line --- block-kit/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/block-kit/README.md b/block-kit/README.md index a062add..c65c1fb 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -19,3 +19,4 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes - **[Rich text](https://docs.slack.dev/reference/block-kit/blocks/rich-text-block)**: Displays formatted, structured representation of text. [Implementation](./src/main/java/blocks/RichText.java). - **[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). +