diff --git a/block-kit/README.md b/block-kit/README.md index 796dc2e..0f2b017 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -9,3 +9,14 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes ### Blocks - **[Actions](https://docs.slack.dev/reference/block-kit/blocks/actions-block)**: Holds multiple interactive elements. [Implementation](./src/blocks/actions.py). +- **[Context actions](https://docs.slack.dev/reference/block-kit/blocks/context-actions-block)**: Holds interactive elements like feedback buttons and icon buttons. [Implementation](./src/blocks/context_actions.py). +- **[Context](https://docs.slack.dev/reference/block-kit/blocks/context-block)**: Provides contextual info, which can include both images and text. [Implementation](./src/blocks/context.py). +- **[Divider](https://docs.slack.dev/reference/block-kit/blocks/divider-block)**: Visually separates pieces of info inside of a message. [Implementation](./src/blocks/divider.py). +- **[File](https://docs.slack.dev/reference/block-kit/blocks/file-block)**: Displays info about remote files. [Implementation](./src/blocks/file.py). +- **[Header](https://docs.slack.dev/reference/block-kit/blocks/header-block)**: Displays a larger-sized text. [Implementation](./src/blocks/header.py). +- **[Image](https://docs.slack.dev/reference/block-kit/blocks/image-block)**: Displays an image. [Implementation](./src/blocks/image.py). +- **[Input](https://docs.slack.dev/reference/block-kit/blocks/input-block)**: Collects information from users via elements. [Implementation](./src/blocks/input.py). +- **[Markdown](https://docs.slack.dev/reference/block-kit/blocks/markdown-block)**: Displays formatted markdown. [Implementation](./src/blocks/markdown.py). +- **[Rich text](https://docs.slack.dev/reference/block-kit/blocks/rich-text-block)**: Displays formatted, structured representation of text. [Implementation](./src/blocks/rich_text.py). +- **[Section](https://docs.slack.dev/reference/block-kit/blocks/section-block)**: Displays text, possibly alongside elements. [Implementation](./src/blocks/section.py). +- **[Video](https://docs.slack.dev/reference/block-kit/blocks/video-block)**: Displays an embedded video player. [Implementation](./src/blocks/video.py). diff --git a/block-kit/src/blocks/context.py b/block-kit/src/blocks/context.py new file mode 100644 index 0000000..4a3c703 --- /dev/null +++ b/block-kit/src/blocks/context.py @@ -0,0 +1,20 @@ +from slack_sdk.models.blocks import ContextBlock, ImageElement, MarkdownTextObject + + +def example01() -> ContextBlock: + """ + Provides contextual info, which can include both images and text. + https://docs.slack.dev/reference/block-kit/blocks/context-block/ + + A context block with an image and text. + """ + block = ContextBlock( + elements=[ + ImageElement( + image_url="https://image.freepik.com/free-photo/red-drawing-pin_1156-445.jpg", + alt_text="images", + ), + MarkdownTextObject(text="Location: **Dogpatch**"), + ] + ) + return block diff --git a/block-kit/src/blocks/context_actions.py b/block-kit/src/blocks/context_actions.py new file mode 100644 index 0000000..ef23643 --- /dev/null +++ b/block-kit/src/blocks/context_actions.py @@ -0,0 +1,51 @@ +from slack_sdk.models.blocks import ContextActionsBlock +from slack_sdk.models.blocks.basic_components import ( + FeedbackButtonObject, + PlainTextObject, +) +from slack_sdk.models.blocks.block_elements import ( + FeedbackButtonsElement, + IconButtonElement, +) + + +def example01() -> ContextActionsBlock: + """ + Holds interactive elements like feedback buttons and icon buttons. + https://docs.slack.dev/reference/block-kit/blocks/context-actions-block/ + + Context actions block with feedback buttons. + """ + block = ContextActionsBlock( + elements=[ + FeedbackButtonsElement( + action_id="feedback_buttons_1", + positive_button=FeedbackButtonObject( + text=PlainTextObject(text="👍"), + value="positive_feedback", + ), + negative_button=FeedbackButtonObject( + text=PlainTextObject(text="👎"), + value="negative_feedback", + ), + ), + ], + ) + return block + + +def example02() -> ContextActionsBlock: + """ + Context actions block with an icon button. + """ + block = ContextActionsBlock( + elements=[ + IconButtonElement( + icon="trash", + text=PlainTextObject(text="Delete"), + action_id="delete_button_1", + value="delete_item", + ), + ], + ) + return block diff --git a/block-kit/src/blocks/divider.py b/block-kit/src/blocks/divider.py new file mode 100644 index 0000000..bd8cba7 --- /dev/null +++ b/block-kit/src/blocks/divider.py @@ -0,0 +1,12 @@ +from slack_sdk.models.blocks import DividerBlock + + +def example01() -> DividerBlock: + """ + Visually separates pieces of info inside of a message. + https://docs.slack.dev/reference/block-kit/blocks/divider-block/ + + A simple divider block. + """ + block = DividerBlock() + return block diff --git a/block-kit/src/blocks/file.py b/block-kit/src/blocks/file.py new file mode 100644 index 0000000..3cadc9f --- /dev/null +++ b/block-kit/src/blocks/file.py @@ -0,0 +1,12 @@ +from slack_sdk.models.blocks import FileBlock + + +def example01() -> FileBlock: + """ + Displays info about remote files. + https://docs.slack.dev/reference/block-kit/blocks/file-block/ + + A file block for a remote file. + """ + block = FileBlock(external_id="ABCD1", source="remote") + return block diff --git a/block-kit/src/blocks/header.py b/block-kit/src/blocks/header.py new file mode 100644 index 0000000..35decab --- /dev/null +++ b/block-kit/src/blocks/header.py @@ -0,0 +1,13 @@ +from slack_sdk.models.blocks import HeaderBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject + + +def example01() -> HeaderBlock: + """ + Displays a larger-sized text. + https://docs.slack.dev/reference/block-kit/blocks/header-block/ + + A simple header block. + """ + block = HeaderBlock(text=PlainTextObject(text="A Heartfelt Header")) + return block diff --git a/block-kit/src/blocks/image.py b/block-kit/src/blocks/image.py new file mode 100644 index 0000000..f768074 --- /dev/null +++ b/block-kit/src/blocks/image.py @@ -0,0 +1,46 @@ +from slack_sdk.models.blocks import ImageBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject, SlackFile + + +def example01() -> ImageBlock: + """ + Displays an image. + https://docs.slack.dev/reference/block-kit/blocks/image-block/ + + An image block using image_url. + """ + block = ImageBlock( + title=PlainTextObject(text="Please enjoy this photo of a kitten"), + block_id="image4", + image_url="http://placekitten.com/500/500", + alt_text="An incredibly cute kitten.", + ) + return block + + +def example02() -> ImageBlock: + """ + An image block using slack_file with a url. + """ + block = ImageBlock( + title=PlainTextObject(text="Please enjoy this photo of a kitten"), + block_id="image4", + slack_file=SlackFile( + url="https://files.slack.com/files-pri/T0123456-F0123456/xyz.png" + ), + alt_text="An incredibly cute kitten.", + ) + return block + + +def example03() -> ImageBlock: + """ + An image block using slack_file with an id. + """ + block = ImageBlock( + title=PlainTextObject(text="Please enjoy this photo of a kitten"), + block_id="image4", + slack_file=SlackFile(id="F0123456"), + alt_text="An incredibly cute kitten.", + ) + return block diff --git a/block-kit/src/blocks/input.py b/block-kit/src/blocks/input.py new file mode 100644 index 0000000..37ff0b6 --- /dev/null +++ b/block-kit/src/blocks/input.py @@ -0,0 +1,17 @@ +from slack_sdk.models.blocks import InputBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import PlainTextInputElement + + +def example01() -> InputBlock: + """ + Collects information from users via elements. + https://docs.slack.dev/reference/block-kit/blocks/input-block/ + + An input block containing a plain-text input element. + """ + block = InputBlock( + element=PlainTextInputElement(), + label=PlainTextObject(text="Label", emoji=True), + ) + return block diff --git a/block-kit/src/blocks/markdown.py b/block-kit/src/blocks/markdown.py new file mode 100644 index 0000000..18c8401 --- /dev/null +++ b/block-kit/src/blocks/markdown.py @@ -0,0 +1,12 @@ +from slack_sdk.models.blocks import MarkdownBlock + + +def example01() -> MarkdownBlock: + """ + Displays formatted markdown. + https://docs.slack.dev/reference/block-kit/blocks/markdown-block/ + + A markdown block. + """ + block = MarkdownBlock(text="**Lots of information here!!**") + return block diff --git a/block-kit/src/blocks/rich_text.py b/block-kit/src/blocks/rich_text.py new file mode 100644 index 0000000..d6e56db --- /dev/null +++ b/block-kit/src/blocks/rich_text.py @@ -0,0 +1,321 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextListElement, + RichTextPreformattedElement, + RichTextQuoteElement, + RichTextSectionElement, +) + + +def example01() -> list[RichTextBlock]: + """ + Displays formatted, structured representation of text. + https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/ + + Four basic rich text section examples (basic, bold, italic, strikethrough). + """ + blocks = [ + RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text( + text="Hello there, I am a basic rich text block!" + ) + ] + ) + ] + ), + RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Hello there, "), + RichTextElementParts.Text( + text="I am a bold rich text block!", style={"bold": True} + ), + ] + ) + ] + ), + RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Hello there, "), + RichTextElementParts.Text( + text="I am an italic rich text block!", + style={"italic": True}, + ), + ] + ) + ] + ), + RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Hello there, "), + RichTextElementParts.Text( + text="I am a strikethrough rich text block!", + style={"strike": True}, + ), + ] + ) + ] + ), + ] + return blocks + + +def example02() -> RichTextBlock: + """ + A rich text block with a bullet list. + """ + block = RichTextBlock( + block_id="block1", + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text( + text="My favorite Slack features (in no particular order):" + ) + ] + ), + RichTextListElement( + style="bullet", + indent=0, + border=1, + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Huddles")] + ), + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Canvas")] + ), + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Developing with Block Kit") + ] + ), + ], + ), + ], + ) + return block + + +def example03() -> RichTextBlock: + """ + A rich text block with a nested bullet list. + """ + block = RichTextBlock( + block_id="block1", + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Breakfast foods I enjoy:")] + ), + RichTextListElement( + style="bullet", + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Hashbrowns")] + ), + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Eggs")] + ), + ], + ), + RichTextListElement( + style="bullet", + indent=1, + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Scrambled")] + ), + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Over easy")] + ), + ], + ), + RichTextListElement( + style="bullet", + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Pancakes, extra syrup") + ] + ) + ], + ), + ], + ) + return block + + +def example04() -> RichTextBlock: + """ + A rich text block with preformatted code. + """ + block = RichTextBlock( + elements=[ + RichTextPreformattedElement( + border=0, + elements=[ + RichTextElementParts.Text( + text='{\n "object": {\n "description": "this is an example of a json object"\n }\n}' + ) + ], + ) + ] + ) + return block + + +def example05() -> RichTextBlock: + """ + A rich text block with a quote. + """ + block = RichTextBlock( + block_id="Vrzsu", + elements=[ + RichTextQuoteElement( + elements=[ + RichTextElementParts.Text( + text="What we need is good examples in our documentation." + ) + ] + ), + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Yes - I completely agree, Luke!") + ] + ), + ], + ) + return block + + +def example06() -> RichTextBlock: + """ + A rich text block with a broadcast element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Broadcast(range="everyone")] + ) + ] + ) + return block + + +def example07() -> RichTextBlock: + """ + A rich text block with a color element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Color(value="#F405B3")] + ) + ] + ) + return block + + +def example08() -> RichTextBlock: + """ + A rich text block with a channel element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Channel(channel_id="C123ABC456")] + ) + ] + ) + return block + + +def example09() -> RichTextBlock: + """ + A rich text block with a date element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Date( + timestamp=1720710212, + format="{date_num} at {time}", + fallback="timey", + ) + ] + ) + ] + ) + return block + + +def example10() -> RichTextBlock: + """ + A rich text block with emoji elements. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Emoji(name="basketball"), + RichTextElementParts.Text(text=" "), + RichTextElementParts.Emoji(name="snowboarder"), + RichTextElementParts.Text(text=" "), + RichTextElementParts.Emoji(name="checkered_flag"), + ] + ) + ] + ) + return block + + +def example11() -> RichTextBlock: + """ + A rich text block with a link element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Link(url="https://api.slack.com")] + ) + ] + ) + return block + + +def example12() -> RichTextBlock: + """ + A rich text block with a user mention element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.User(user_id="U123ABC456")] + ) + ] + ) + return block + + +def example13() -> RichTextBlock: + """ + A rich text block with a usergroup mention element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.UserGroup(usergroup_id="G123ABC456")] + ) + ] + ) + return block diff --git a/block-kit/src/blocks/section.py b/block-kit/src/blocks/section.py new file mode 100644 index 0000000..b8b4085 --- /dev/null +++ b/block-kit/src/blocks/section.py @@ -0,0 +1,51 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import MarkdownTextObject, PlainTextObject +from slack_sdk.models.blocks.block_elements import DatePickerElement + + +def example01() -> SectionBlock: + """ + Displays text, possibly alongside elements. + https://docs.slack.dev/reference/block-kit/blocks/section-block/ + + A text section block. + """ + block = SectionBlock( + text=MarkdownTextObject( + text="A message *with some bold text* and _some italicized text_." + ) + ) + return block + + +def example02() -> SectionBlock: + """ + A section block containing text fields. + """ + block = SectionBlock( + text=MarkdownTextObject( + text="A message *with some bold text* and _some italicized text_." + ), + fields=[ + MarkdownTextObject(text="High"), + PlainTextObject(text="Silly", emoji=True), + ], + ) + return block + + +def example03() -> SectionBlock: + """ + A section block containing a datepicker element. + """ + block = SectionBlock( + text=MarkdownTextObject( + text="*Haley* has requested you set a deadline for finding a house" + ), + accessory=DatePickerElement( + action_id="datepicker123", + initial_date="1990-04-28", + placeholder=PlainTextObject(text="Select a date"), + ), + ) + return block diff --git a/block-kit/src/blocks/video.py b/block-kit/src/blocks/video.py new file mode 100644 index 0000000..e2ccb42 --- /dev/null +++ b/block-kit/src/blocks/video.py @@ -0,0 +1,22 @@ +from slack_sdk.models.blocks import VideoBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject + + +def example01() -> VideoBlock: + """ + Displays an embedded video player. + https://docs.slack.dev/reference/block-kit/blocks/video-block/ + + A video block. + """ + block = VideoBlock( + title=PlainTextObject( + text="Use the Events API to create a dynamic App Home", emoji=True + ), + title_url="https://www.youtube.com/watch?v=8876OZV_Yy0", + description=PlainTextObject(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", + ) + return block diff --git a/block-kit/tests/blocks/test_actions.py b/block-kit/tests/blocks/test_actions.py index 74e675a..a40ac37 100644 --- a/block-kit/tests/blocks/test_actions.py +++ b/block-kit/tests/blocks/test_actions.py @@ -19,26 +19,41 @@ def test_example01(): "action_id": "select_2", "options": [ { - "text": {"type": "plain_text", "text": "Matilda"}, + "text": { + "type": "plain_text", + "text": "Matilda", + }, "value": "matilda", }, { - "text": {"type": "plain_text", "text": "Glinda"}, + "text": { + "type": "plain_text", + "text": "Glinda", + }, "value": "glinda", }, { - "text": {"type": "plain_text", "text": "Granny Weatherwax"}, + "text": { + "type": "plain_text", + "text": "Granny Weatherwax", + }, "value": "grannyWeatherwax", }, { - "text": {"type": "plain_text", "text": "Hermione"}, + "text": { + "type": "plain_text", + "text": "Hermione", + }, "value": "hermione", }, ], }, { "type": "button", - "text": {"type": "plain_text", "text": "Cancel"}, + "text": { + "type": "plain_text", + "text": "Cancel", + }, "value": "cancel", "action_id": "button_1", }, @@ -58,7 +73,10 @@ def test_example02(): "type": "datepicker", "action_id": "datepicker123", "initial_date": "1990-04-28", - "placeholder": {"type": "plain_text", "text": "Select a date"}, + "placeholder": { + "type": "plain_text", + "text": "Select a date", + }, }, { "type": "overflow", @@ -103,7 +121,10 @@ def test_example02(): }, { "type": "button", - "text": {"type": "plain_text", "text": "Click Me"}, + "text": { + "type": "plain_text", + "text": "Click Me", + }, "value": "click_me_123", "action_id": "button", }, diff --git a/block-kit/tests/blocks/test_context.py b/block-kit/tests/blocks/test_context.py new file mode 100644 index 0000000..ded9934 --- /dev/null +++ b/block-kit/tests/blocks/test_context.py @@ -0,0 +1,23 @@ +import json + +from src.blocks import context + + +def test_example01(): + block = context.example01() + actual = block.to_dict() + 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**", + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_context_actions.py b/block-kit/tests/blocks/test_context_actions.py new file mode 100644 index 0000000..8921ac7 --- /dev/null +++ b/block-kit/tests/blocks/test_context_actions.py @@ -0,0 +1,53 @@ +import json + +from src.blocks import context_actions + + +def test_example01(): + block = context_actions.example01() + actual = block.to_dict() + expected = { + "type": "context_actions", + "elements": [ + { + "type": "feedback_buttons", + "action_id": "feedback_buttons_1", + "positive_button": { + "text": { + "type": "plain_text", + "text": "👍", + }, + "value": "positive_feedback", + }, + "negative_button": { + "text": { + "type": "plain_text", + "text": "👎", + }, + "value": "negative_feedback", + }, + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = context_actions.example02() + actual = block.to_dict() + expected = { + "type": "context_actions", + "elements": [ + { + "type": "icon_button", + "icon": "trash", + "text": { + "type": "plain_text", + "text": "Delete", + }, + "action_id": "delete_button_1", + "value": "delete_item", + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_divider.py b/block-kit/tests/blocks/test_divider.py new file mode 100644 index 0000000..47b48ae --- /dev/null +++ b/block-kit/tests/blocks/test_divider.py @@ -0,0 +1,12 @@ +import json + +from src.blocks import divider + + +def test_example01(): + block = divider.example01() + actual = block.to_dict() + expected = { + "type": "divider", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_file.py b/block-kit/tests/blocks/test_file.py new file mode 100644 index 0000000..40c2bc3 --- /dev/null +++ b/block-kit/tests/blocks/test_file.py @@ -0,0 +1,14 @@ +import json + +from src.blocks import file + + +def test_example01(): + block = file.example01() + actual = block.to_dict() + expected = { + "type": "file", + "external_id": "ABCD1", + "source": "remote", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_header.py b/block-kit/tests/blocks/test_header.py new file mode 100644 index 0000000..3a50ea9 --- /dev/null +++ b/block-kit/tests/blocks/test_header.py @@ -0,0 +1,16 @@ +import json + +from src.blocks import header + + +def test_example01(): + block = header.example01() + actual = block.to_dict() + expected = { + "type": "header", + "text": { + "type": "plain_text", + "text": "A Heartfelt Header", + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_image.py b/block-kit/tests/blocks/test_image.py new file mode 100644 index 0000000..e1cfd7b --- /dev/null +++ b/block-kit/tests/blocks/test_image.py @@ -0,0 +1,55 @@ +import json + +from src.blocks import image + + +def test_example01(): + block = image.example01() + actual = block.to_dict() + 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.", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = image.example02() + actual = block.to_dict() + 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.", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example03(): + block = image.example03() + actual = block.to_dict() + 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.", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_input.py b/block-kit/tests/blocks/test_input.py new file mode 100644 index 0000000..0e71fa9 --- /dev/null +++ b/block-kit/tests/blocks/test_input.py @@ -0,0 +1,20 @@ +import json + +from src.blocks import input + + +def test_example01(): + block = input.example01() + actual = block.to_dict() + expected = { + "type": "input", + "element": { + "type": "plain_text_input", + }, + "label": { + "type": "plain_text", + "text": "Label", + "emoji": True, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_markdown.py b/block-kit/tests/blocks/test_markdown.py new file mode 100644 index 0000000..d7fb35b --- /dev/null +++ b/block-kit/tests/blocks/test_markdown.py @@ -0,0 +1,13 @@ +import json + +from src.blocks import markdown + + +def test_example01(): + block = markdown.example01() + actual = block.to_dict() + expected = { + "type": "markdown", + "text": "**Lots of information here!!**", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_rich_text.py b/block-kit/tests/blocks/test_rich_text.py new file mode 100644 index 0000000..fb6ef56 --- /dev/null +++ b/block-kit/tests/blocks/test_rich_text.py @@ -0,0 +1,428 @@ +import json + +from src.blocks import rich_text + + +def test_example01(): + blocks = rich_text.example01() + actual = [block.to_dict() for block in blocks] + 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, + }, + }, + ], + } + ], + }, + { + "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": { + "italic": True, + }, + }, + ], + } + ], + }, + { + "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": { + "strike": True, + }, + }, + ], + } + ], + }, + ] + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = rich_text.example02() + actual = block.to_dict() + 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, + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example03(): + block = rich_text.example03() + actual = block.to_dict() + 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", + }, + ], + } + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example04(): + block = rich_text.example04() + actual = block.to_dict() + 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, + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example05(): + block = rich_text.example05() + actual = block.to_dict() + 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!", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example06(): + block = rich_text.example06() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [{"type": "broadcast", "range": "everyone"}], + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example07(): + block = rich_text.example07() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [{"type": "color", "value": "#F405B3"}], + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example08(): + block = rich_text.example08() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [{"type": "channel", "channel_id": "C123ABC456"}], + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example09(): + block = rich_text.example09() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "date", + "timestamp": 1720710212, + "format": "{date_num} at {time}", + "fallback": "timey", + } + ], + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example10(): + block = rich_text.example10() + actual = block.to_dict() + 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", + }, + ], + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example11(): + block = rich_text.example11() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [{"type": "link", "url": "https://api.slack.com"}], + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example12(): + block = rich_text.example12() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [{"type": "user", "user_id": "U123ABC456"}], + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example13(): + block = rich_text.example13() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [{"type": "usergroup", "usergroup_id": "G123ABC456"}], + } + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_section.py b/block-kit/tests/blocks/test_section.py new file mode 100644 index 0000000..388b2ec --- /dev/null +++ b/block-kit/tests/blocks/test_section.py @@ -0,0 +1,62 @@ +import json + +from src.blocks import section + + +def test_example01(): + block = section.example01() + actual = block.to_dict() + expected = { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "A message *with some bold text* and _some italicized text_.", + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = section.example02() + actual = block.to_dict() + 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", + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example03(): + block = section.example03() + actual = block.to_dict() + 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", + }, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_video.py b/block-kit/tests/blocks/test_video.py new file mode 100644 index 0000000..a76a7e1 --- /dev/null +++ b/block-kit/tests/blocks/test_video.py @@ -0,0 +1,26 @@ +import json + +from src.blocks import video + + +def test_example01(): + block = video.example01() + actual = block.to_dict() + 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", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True)