line-message-builder 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +19 -0
- data/README.md +42 -18
- data/lib/line/message/builder/actions/message.rb +1 -1
- data/lib/line/message/builder/actions/postback.rb +1 -1
- data/lib/line/message/builder/container.rb +4 -4
- data/lib/line/message/builder/flex/box.rb +11 -2
- data/lib/line/message/builder/flex/bubble.rb +7 -2
- data/lib/line/message/builder/flex/builder.rb +6 -4
- data/lib/line/message/builder/flex/button.rb +12 -6
- data/lib/line/message/builder/flex/image.rb +14 -16
- data/lib/line/message/builder/flex/text.rb +15 -9
- data/lib/line/message/builder/quick_reply.rb +2 -2
- data/lib/line/message/builder/text.rb +5 -2
- data/lib/line/message/builder/version.rb +1 -1
- data/lib/line/message/builder.rb +1 -0
- data/lib/line/message/rspec/matchers/have_flex_bubble.rb +60 -0
- data/lib/line/message/rspec/matchers/have_flex_component.rb +26 -17
- data/lib/line/message/rspec/matchers/have_flex_message.rb +4 -3
- data/lib/line/message/rspec/matchers/have_quick_reply.rb +5 -4
- data/lib/line/message/rspec/matchers/have_text_message.rb +16 -11
- data/lib/line/message/rspec/matchers/utils.rb +26 -0
- data/lib/line/message/rspec/matchers.rb +2 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bbeb0eeec944a0886b6e6dec3b171c88182c0097ad31c03cc16d7270f1eb471
|
4
|
+
data.tar.gz: 8f08afeab2e40b152e8e4218b00f03f725be5f3cfa70b687ebaf20e7d1dd2d7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0afd2ab03e4bbe7df364ebc27764380e48b9fd00a0a78ba345655f9227b7ff1cac00831024fd4ca923d2df1fb195c7e3c4947e6290f530a3466d002b325af4f3
|
7
|
+
data.tar.gz: 91455d81ccc9d6aa646e5a5851ad55baa19e89d4c427b4c9e4fa951676f6cf5d6e8005983d5a76610c05ced666a353d06f54ef7020516ad67250000cb4379ba7
|
@@ -1 +1 @@
|
|
1
|
-
{".":"0.
|
1
|
+
{".":"0.4.0"}
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.4.0](https://github.com/elct9620/line-message-builder/compare/v0.3.0...v0.4.0) (2025-04-06)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* Add flex and margin options to Line Flex Button with improved error handling ([c8cd142](https://github.com/elct9620/line-message-builder/commit/c8cd1427528d5495d13bf93898da4638588065ae))
|
9
|
+
* Add flex box options and validation for LINE message builder ([133f87d](https://github.com/elct9620/line-message-builder/commit/133f87ddb3fd2a093947db0aef6363cd4c150701))
|
10
|
+
* Add flex bubble options and RSpec matcher for Line message builder ([605ad89](https://github.com/elct9620/line-message-builder/commit/605ad89870a53c55182f96db5f2738b5c347f0dc))
|
11
|
+
* Add margin option to flex text builder ([1d44522](https://github.com/elct9620/line-message-builder/commit/1d44522a44218bedab3d2009ef483ed112784248))
|
12
|
+
* Add quote token support for Line text messages ([7871999](https://github.com/elct9620/line-message-builder/commit/7871999dfc7032e75e5407c8b16bed7ef7dd28b8))
|
13
|
+
* Add support for flex, margin, and align options in Flex image builder ([693d0ab](https://github.com/elct9620/line-message-builder/commit/693d0abf5ef524b2e6d075f3c7e65eca49cf7f27))
|
14
|
+
|
15
|
+
## [0.3.0](https://github.com/elct9620/line-message-builder/compare/v0.2.0...v0.3.0) (2025-04-05)
|
16
|
+
|
17
|
+
|
18
|
+
### Miscellaneous Chores
|
19
|
+
|
20
|
+
* release 0.3.0 ([2f0b00c](https://github.com/elct9620/line-message-builder/commit/2f0b00c875a2d547c576e383aaeeae3bf777e898))
|
21
|
+
|
3
22
|
## [0.2.0](https://github.com/elct9620/line-message-builder/compare/v0.1.0...v0.2.0) (2025-04-05)
|
4
23
|
|
5
24
|
|
data/README.md
CHANGED
@@ -62,8 +62,8 @@ For Rails, you can use `view_context` to make `Builder` to access Rails helpers.
|
|
62
62
|
builder = Line::MessageBuilder::Builder.with(view_context) do
|
63
63
|
text "Anything you want?" do
|
64
64
|
quick_reply do
|
65
|
-
|
66
|
-
|
65
|
+
message "Yes", label: "Yes", image_url: image_url("yes.png")
|
66
|
+
message "No", label: "No", image_url: image_url("no.png")
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -82,8 +82,8 @@ context = ActionView::Base.new(
|
|
82
82
|
builder = Line::MessageBuilder::Builder.with(context) do
|
83
83
|
text "Anything you want?" do
|
84
84
|
quick_reply do
|
85
|
-
|
86
|
-
|
85
|
+
message "Yes", label: "Yes", image_url: image_url("yes.png")
|
86
|
+
message "No", label: "No", image_url: image_url("no.png")
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
@@ -91,6 +91,17 @@ end
|
|
91
91
|
|
92
92
|
### RSpec Matcher
|
93
93
|
|
94
|
+
| Matcher | Description |
|
95
|
+
| --------------------------- | ------------------------------------ |
|
96
|
+
| `have_line_text_message` | Match a text message |
|
97
|
+
| `have_line_flex_message` | Match a flex message |
|
98
|
+
| `have_line_flex_bubble` | Match a flex message with bubble |
|
99
|
+
| `have_line_flex_text` | Match a flex message with text |
|
100
|
+
| `have_line_flex_image` | Match a flex message with image |
|
101
|
+
| `have_line_flex_button` | Match a flex message with button |
|
102
|
+
| `have_line_flex_box` | Match a flex message with box |
|
103
|
+
|
104
|
+
|
94
105
|
Add `line/message/rspec` to your `spec_helper.rb` or `rails_helper.rb`:
|
95
106
|
|
96
107
|
```ruby
|
@@ -121,6 +132,19 @@ it { is_expected.to have_line_text_message("Hello, world!") }
|
|
121
132
|
it { is_expected.to have_line_text_message(/Nice to meet you!/) }
|
122
133
|
```
|
123
134
|
|
135
|
+
The matchers can work with webmock `a_request`:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
it "reply with message" do
|
139
|
+
expect(a_request(:post, "https://api.line.me/v2/bot/message/reply")
|
140
|
+
.with(
|
141
|
+
body: hash_including({
|
142
|
+
messages: have_line_text_message(/Hello, world!/),
|
143
|
+
},
|
144
|
+
))).to have_been_made.once
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
124
148
|
## Capabilities
|
125
149
|
|
126
150
|
- ✅ Supported
|
@@ -131,7 +155,7 @@ it { is_expected.to have_line_text_message(/Nice to meet you!/) }
|
|
131
155
|
|
132
156
|
| Type | Supported |
|
133
157
|
| ---- | --------- |
|
134
|
-
| Text |
|
158
|
+
| Text | 🚧 |
|
135
159
|
| Text v2 | ❌ |
|
136
160
|
| Sticker | ❌ |
|
137
161
|
| Sticker | ❌ |
|
@@ -166,19 +190,19 @@ it { is_expected.to have_line_text_message(/Nice to meet you!/) }
|
|
166
190
|
|
167
191
|
### Flex Components
|
168
192
|
|
169
|
-
| Component | Supported
|
170
|
-
| --------- | ---------
|
171
|
-
| Bubble | 🚧
|
172
|
-
| Carousel | ❌
|
173
|
-
| Box | 🚧
|
174
|
-
| Button | 🚧
|
175
|
-
| Image | 🚧
|
176
|
-
| Video | ❌
|
177
|
-
| Icon | ❌
|
178
|
-
| Text | 🚧
|
179
|
-
| Span | ❌
|
180
|
-
| Separator | ❌
|
181
|
-
| Filler | ❌
|
193
|
+
| Component | Supported |
|
194
|
+
| --------- | --------- |
|
195
|
+
| Bubble | 🚧 |
|
196
|
+
| Carousel | ❌ |
|
197
|
+
| Box | 🚧 |
|
198
|
+
| Button | 🚧 |
|
199
|
+
| Image | 🚧 |
|
200
|
+
| Video | ❌ |
|
201
|
+
| Icon | ❌ |
|
202
|
+
| Text | 🚧 |
|
203
|
+
| Span | ❌ |
|
204
|
+
| Separator | ❌ |
|
205
|
+
| Filler | ❌ Deprecated |
|
182
206
|
|
183
207
|
## Development
|
184
208
|
|
@@ -6,7 +6,7 @@ module Line
|
|
6
6
|
module Actions
|
7
7
|
# The Message class is used to build message actions for quick replies.
|
8
8
|
class Message < Line::Message::Builder::Base
|
9
|
-
def initialize(text
|
9
|
+
def initialize(text, label: nil, context: nil, &)
|
10
10
|
@text = text
|
11
11
|
@label = label
|
12
12
|
|
@@ -6,7 +6,7 @@ module Line
|
|
6
6
|
module Actions
|
7
7
|
# The Postback class is used to build postback actions for quick replies.
|
8
8
|
class Postback < Line::Message::Builder::Base
|
9
|
-
def initialize(data
|
9
|
+
def initialize(data, label: nil, display_text: nil, context: nil, &)
|
10
10
|
@data = data
|
11
11
|
@label = label
|
12
12
|
@display_text = display_text
|
@@ -13,12 +13,12 @@ module Line
|
|
13
13
|
super
|
14
14
|
end
|
15
15
|
|
16
|
-
def text(text, &)
|
17
|
-
@messages << Text.new(text, context: context, &)
|
16
|
+
def text(text, **options, &)
|
17
|
+
@messages << Text.new(text, context: context, **options, &)
|
18
18
|
end
|
19
19
|
|
20
|
-
def flex(**
|
21
|
-
@messages << Flex::Builder.new(
|
20
|
+
def flex(**options, &)
|
21
|
+
@messages << Flex::Builder.new(context: context, **options, &)
|
22
22
|
end
|
23
23
|
|
24
24
|
def build
|
@@ -6,7 +6,11 @@ module Line
|
|
6
6
|
module Flex
|
7
7
|
# The box is a component for the Flex message.
|
8
8
|
class Box < Line::Message::Builder::Base
|
9
|
+
attr_reader :contents
|
10
|
+
|
9
11
|
option :layout, default: :horizontal
|
12
|
+
option :spacing, default: nil
|
13
|
+
option :flex, default: nil
|
10
14
|
|
11
15
|
def initialize(context: nil, **options, &)
|
12
16
|
@contents = []
|
@@ -31,10 +35,15 @@ module Line
|
|
31
35
|
end
|
32
36
|
|
33
37
|
def to_h
|
38
|
+
raise RequiredError, "layout is required" if layout.nil?
|
39
|
+
raise RequiredError, "contents is required" if contents.empty?
|
40
|
+
|
34
41
|
{
|
35
42
|
type: "box",
|
36
|
-
layout:
|
37
|
-
|
43
|
+
layout: layout,
|
44
|
+
spacing: spacing,
|
45
|
+
flex: flex,
|
46
|
+
contents: contents.map(&:to_h)
|
38
47
|
}.compact
|
39
48
|
end
|
40
49
|
end
|
@@ -6,7 +6,10 @@ module Line
|
|
6
6
|
module Flex
|
7
7
|
# The bubble is container for the Flex message.
|
8
8
|
class Bubble < Line::Message::Builder::Base
|
9
|
-
|
9
|
+
option :size, default: nil
|
10
|
+
option :styles, default: nil
|
11
|
+
|
12
|
+
def initialize(context: nil, **options, &)
|
10
13
|
@header = nil
|
11
14
|
@hero = nil
|
12
15
|
@body = nil
|
@@ -41,7 +44,9 @@ module Line
|
|
41
44
|
header: @header&.to_h,
|
42
45
|
hero: @hero&.to_h,
|
43
46
|
body: @body&.to_h,
|
44
|
-
footer: @footer&.to_h
|
47
|
+
footer: @footer&.to_h,
|
48
|
+
size: size,
|
49
|
+
styles: styles
|
45
50
|
}.compact
|
46
51
|
end
|
47
52
|
end
|
@@ -9,20 +9,22 @@ module Line
|
|
9
9
|
option :alt_text, default: nil
|
10
10
|
|
11
11
|
def initialize(context: nil, **options, &)
|
12
|
-
@contents =
|
12
|
+
@contents = nil
|
13
13
|
|
14
14
|
super
|
15
15
|
end
|
16
16
|
|
17
|
-
def bubble(&)
|
18
|
-
@contents
|
17
|
+
def bubble(**options, &)
|
18
|
+
@contents = Line::Message::Builder::Flex::Bubble.new(context: context, **options, &)
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_h
|
22
|
+
raise Error, "contents should be bubble or carousel" if @contents.nil?
|
23
|
+
|
22
24
|
{
|
23
25
|
type: "flex",
|
24
26
|
altText: @alt_text,
|
25
|
-
contents: @contents.
|
27
|
+
contents: @contents.to_h,
|
26
28
|
quickReply: @quick_reply&.to_h
|
27
29
|
}.compact
|
28
30
|
end
|
@@ -6,6 +6,10 @@ module Line
|
|
6
6
|
module Flex
|
7
7
|
# The button is a component of the Flex message.
|
8
8
|
class Button < Line::Message::Builder::Base
|
9
|
+
attr_reader :action
|
10
|
+
|
11
|
+
option :flex, default: nil
|
12
|
+
option :margin, default: nil
|
9
13
|
option :style, default: :link
|
10
14
|
option :height, default: :md
|
11
15
|
|
@@ -16,21 +20,23 @@ module Line
|
|
16
20
|
end
|
17
21
|
|
18
22
|
def message(text, label:, display_text: nil, &)
|
19
|
-
@action = Actions::Message.new(text
|
23
|
+
@action = Actions::Message.new(text, label: label, display_text: display_text, &)
|
20
24
|
end
|
21
25
|
|
22
26
|
def postback(data, label: nil, display_text: nil, &)
|
23
|
-
@action = Actions::Postback.new(data
|
27
|
+
@action = Actions::Postback.new(data, label: label, display_text: display_text, &)
|
24
28
|
end
|
25
29
|
|
26
30
|
def to_h
|
27
|
-
raise
|
31
|
+
raise RequiredError, "action is required" if action.nil?
|
28
32
|
|
29
33
|
{
|
30
34
|
type: "button",
|
31
|
-
action:
|
32
|
-
|
33
|
-
|
35
|
+
action: action.to_h,
|
36
|
+
flex: flex,
|
37
|
+
margin: margin,
|
38
|
+
style: style,
|
39
|
+
height: height
|
34
40
|
}.compact
|
35
41
|
end
|
36
42
|
end
|
@@ -6,9 +6,14 @@ module Line
|
|
6
6
|
module Flex
|
7
7
|
# The image is a component for the Flex message.
|
8
8
|
class Image < Line::Message::Builder::Base
|
9
|
+
attr_reader :url
|
10
|
+
|
9
11
|
option :size, default: nil
|
10
12
|
option :aspect_ratio, default: nil
|
11
13
|
option :aspect_mode, default: nil
|
14
|
+
option :flex, default: nil
|
15
|
+
option :margin, default: nil
|
16
|
+
option :align, default: nil
|
12
17
|
|
13
18
|
def initialize(url, context: nil, **options, &)
|
14
19
|
@url = url
|
@@ -16,25 +21,18 @@ module Line
|
|
16
21
|
super(context: context, **options, &)
|
17
22
|
end
|
18
23
|
|
19
|
-
def
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
def aspect_ratio(aspect_ratio)
|
24
|
-
@aspect_ratio = aspect_ratio
|
25
|
-
end
|
26
|
-
|
27
|
-
def aspect_mode(aspect_mode)
|
28
|
-
@aspect_mode = aspect_mode
|
29
|
-
end
|
24
|
+
def to_h # rubocop:disable Metrics/MethodLength
|
25
|
+
raise RequiredError, "url is required" if url.nil?
|
30
26
|
|
31
|
-
def to_h
|
32
27
|
{
|
33
28
|
type: "image",
|
34
|
-
url:
|
35
|
-
size:
|
36
|
-
|
37
|
-
|
29
|
+
url: url,
|
30
|
+
size: size,
|
31
|
+
flex: flex,
|
32
|
+
margin: margin,
|
33
|
+
align: align,
|
34
|
+
aspectRatio: aspect_ratio,
|
35
|
+
aspectMode: aspect_mode
|
38
36
|
}.compact
|
39
37
|
end
|
40
38
|
end
|
@@ -6,12 +6,15 @@ module Line
|
|
6
6
|
module Flex
|
7
7
|
# The text is a component of the Flex message.
|
8
8
|
class Text < Line::Message::Builder::Base
|
9
|
+
attr_reader :text
|
10
|
+
|
11
|
+
option :size, default: nil
|
9
12
|
option :wrap, default: false
|
10
13
|
option :line_spacing, default: nil
|
11
14
|
option :color, default: nil
|
12
|
-
option :size, default: nil
|
13
15
|
option :align, default: nil
|
14
16
|
option :flex, default: nil
|
17
|
+
option :margin, default: nil
|
15
18
|
|
16
19
|
def initialize(text, context: nil, **options, &)
|
17
20
|
@text = text
|
@@ -23,16 +26,19 @@ module Line
|
|
23
26
|
@wrap = true
|
24
27
|
end
|
25
28
|
|
26
|
-
def to_h
|
29
|
+
def to_h # rubocop:disable Metrics/MethodLength
|
30
|
+
raise RequiredError, "text is required" if text.nil?
|
31
|
+
|
27
32
|
{
|
28
33
|
type: "text",
|
29
|
-
text:
|
30
|
-
wrap:
|
31
|
-
lineSpacing:
|
32
|
-
color:
|
33
|
-
size:
|
34
|
-
align:
|
35
|
-
|
34
|
+
text: text,
|
35
|
+
wrap: wrap,
|
36
|
+
lineSpacing: line_spacing,
|
37
|
+
color: color,
|
38
|
+
size: size,
|
39
|
+
align: align,
|
40
|
+
margin: margin,
|
41
|
+
flex: flex
|
36
42
|
}.compact
|
37
43
|
end
|
38
44
|
end
|
@@ -13,14 +13,14 @@ module Line
|
|
13
13
|
|
14
14
|
def message(text, label:, image_url: nil, &)
|
15
15
|
action(
|
16
|
-
Actions::Message.new(text
|
16
|
+
Actions::Message.new(text, label: label, &),
|
17
17
|
image_url
|
18
18
|
)
|
19
19
|
end
|
20
20
|
|
21
21
|
def postback(data, label: nil, display_text: nil, image_url: nil, &)
|
22
22
|
action(
|
23
|
-
Actions::Postback.new(data
|
23
|
+
Actions::Postback.new(data, label: label, display_text: display_text, &),
|
24
24
|
image_url
|
25
25
|
)
|
26
26
|
end
|
@@ -5,16 +5,19 @@ module Line
|
|
5
5
|
module Builder
|
6
6
|
# Text message builder.
|
7
7
|
class Text < Base
|
8
|
-
|
8
|
+
option :quote_token, default: nil
|
9
|
+
|
10
|
+
def initialize(text, context: nil, **options, &block)
|
9
11
|
@text = text
|
10
12
|
|
11
|
-
super(context: context, &block)
|
13
|
+
super(context: context, **options, &block)
|
12
14
|
end
|
13
15
|
|
14
16
|
def to_h
|
15
17
|
{
|
16
18
|
type: "text",
|
17
19
|
text: @text,
|
20
|
+
quoteToken: quote_token,
|
18
21
|
quickReply: @quick_reply&.to_h
|
19
22
|
}.compact
|
20
23
|
end
|
data/lib/line/message/builder.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module RSpec
|
6
|
+
# :nodoc:
|
7
|
+
module Matchers
|
8
|
+
# The flex component matcher for RSpec to search nested flex components in the message array.
|
9
|
+
class HaveFlexBubble
|
10
|
+
def initialize(expected)
|
11
|
+
@expected = Utils.stringify_keys!(expected || {}, deep: true)
|
12
|
+
end
|
13
|
+
|
14
|
+
def description
|
15
|
+
return "have flex bubble" if @expected.empty?
|
16
|
+
|
17
|
+
"have flex bubble matching #{@expected.inspect}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(actual)
|
21
|
+
@actual = Utils.stringify_keys!(actual, deep: true)
|
22
|
+
@actual.any? { |message| match_flex_component?(message) }
|
23
|
+
end
|
24
|
+
alias == matches?
|
25
|
+
|
26
|
+
def failure_message
|
27
|
+
return "expected to find a flex bubble" if @expected.empty?
|
28
|
+
|
29
|
+
"expected to find a flex bubble matching #{@expected.inspect}"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def match_flex_component?(message)
|
35
|
+
return false unless message["type"] == "flex"
|
36
|
+
|
37
|
+
match_content?(message["contents"])
|
38
|
+
end
|
39
|
+
|
40
|
+
def match_content?(content)
|
41
|
+
return match_options?(content) if content["type"] == "bubble"
|
42
|
+
return false unless content["contents"]
|
43
|
+
|
44
|
+
content["contents"].any? { |nested_content| match_content?(nested_content) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def match_options?(content)
|
48
|
+
return false unless content["type"] == "bubble"
|
49
|
+
|
50
|
+
::RSpec::Matchers::BuiltIn::Include.new(@expected).matches?(content)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def have_line_flex_bubble(expected = nil) # rubocop:disable Naming/PredicateName
|
55
|
+
HaveFlexBubble.new(expected)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -20,9 +20,10 @@ module Line
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def matches?(actual)
|
23
|
-
@actual = actual
|
23
|
+
@actual = Utils.stringify_keys!(actual, deep: true)
|
24
24
|
@actual.any? { |message| match_flex_component?(message) }
|
25
25
|
end
|
26
|
+
alias == matches?
|
26
27
|
|
27
28
|
def failure_message
|
28
29
|
return "expected to find a flex component" if @expected.nil?
|
@@ -34,16 +35,16 @@ module Line
|
|
34
35
|
private
|
35
36
|
|
36
37
|
def match_flex_component?(message)
|
37
|
-
return false unless message[
|
38
|
+
return false unless message["type"] == "flex"
|
38
39
|
|
39
|
-
message[
|
40
|
+
match_content?(message["contents"])
|
40
41
|
end
|
41
42
|
|
42
43
|
def match_content?(content)
|
43
|
-
return match_bubble?(content) if content[
|
44
|
+
return match_bubble?(content) if content["type"] == "bubble"
|
44
45
|
|
45
|
-
if content[
|
46
|
-
return content[
|
46
|
+
if content["contents"]
|
47
|
+
return content["contents"].any? do |nested_content|
|
47
48
|
match_content?(nested_content)
|
48
49
|
end || @expected.call(content)
|
49
50
|
end
|
@@ -52,7 +53,7 @@ module Line
|
|
52
53
|
end
|
53
54
|
|
54
55
|
def match_bubble?(content)
|
55
|
-
%
|
56
|
+
%w[header hero body footer].any? do |key|
|
56
57
|
block = content[key]
|
57
58
|
next unless block
|
58
59
|
|
@@ -61,7 +62,7 @@ module Line
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def match_bubble_block?(block)
|
64
|
-
return block[
|
65
|
+
return block["contents"].any? { |nested_content| match_content?(nested_content) } if block["contents"]
|
65
66
|
|
66
67
|
match_content?(block)
|
67
68
|
end
|
@@ -72,37 +73,45 @@ module Line
|
|
72
73
|
end
|
73
74
|
|
74
75
|
def have_line_flex_box(**options) # rubocop:disable Naming/PredicateName
|
76
|
+
options = Utils.stringify_keys!(options, deep: true)
|
77
|
+
|
75
78
|
HaveFlexComponent.new(expected_desc: "box(#{options.inspect})") do |content|
|
76
|
-
next false unless content[
|
79
|
+
next false unless content["type"] == "box"
|
77
80
|
|
78
81
|
::RSpec::Matchers::BuiltIn::Include.new(options).matches?(content)
|
79
82
|
end
|
80
83
|
end
|
81
84
|
|
82
85
|
def have_line_flex_text(text, **options) # rubocop:disable Naming/PredicateName
|
86
|
+
options = Utils.stringify_keys!(options, deep: true)
|
87
|
+
|
83
88
|
HaveFlexComponent.new(expected_desc: "text(#{text.inspect})") do |content|
|
84
|
-
next false unless content[
|
89
|
+
next false unless content["type"] == "text"
|
85
90
|
|
86
|
-
content[
|
91
|
+
content["text"].match?(text) && ::RSpec::Matchers::BuiltIn::Include.new(options).matches?(content)
|
87
92
|
end
|
88
93
|
end
|
89
94
|
|
90
95
|
def have_line_flex_button(type, **options) # rubocop:disable Naming/PredicateName
|
96
|
+
options = Utils.stringify_keys!(options, deep: true)
|
97
|
+
|
91
98
|
HaveFlexComponent.new(expected_desc: "#{type} button(#{options.inspect})") do |content|
|
92
|
-
next false unless content[
|
99
|
+
next false unless content["type"] == "button"
|
93
100
|
|
94
|
-
::RSpec::Matchers::BuiltIn::Include.new({ type
|
95
|
-
::RSpec::Matchers::BuiltIn::Include.new({ **options, action
|
96
|
-
type
|
101
|
+
::RSpec::Matchers::BuiltIn::Include.new({ "type" => type, **options }).matches?(content["action"]) ||
|
102
|
+
::RSpec::Matchers::BuiltIn::Include.new({ **options, "action" => ::RSpec::Matchers::BuiltIn::Include.new(
|
103
|
+
"type" => type
|
97
104
|
) }).matches?(content)
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
101
108
|
def have_line_flex_image(url, **options) # rubocop:disable Naming/PredicateName
|
109
|
+
options = Utils.stringify_keys!(options, deep: true)
|
110
|
+
|
102
111
|
HaveFlexComponent.new(expected_desc: "image(#{url.inspect})") do |content|
|
103
|
-
next false unless content[
|
112
|
+
next false unless content["type"] == "image"
|
104
113
|
|
105
|
-
::RSpec::Matchers::BuiltIn::Include.new({ url
|
114
|
+
::RSpec::Matchers::BuiltIn::Include.new({ "url" => url, **options }).matches?(content)
|
106
115
|
end
|
107
116
|
end
|
108
117
|
end
|
@@ -18,9 +18,10 @@ module Line
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def matches?(actual)
|
21
|
-
@actual = actual
|
21
|
+
@actual = Utils.stringify_keys!(actual, deep: true)
|
22
22
|
@actual.any? { |message| match_alt_text?(message) }
|
23
23
|
end
|
24
|
+
alias == matches?
|
24
25
|
|
25
26
|
def failure_message
|
26
27
|
"expected to find a flex message alt text matching #{@expected}"
|
@@ -29,10 +30,10 @@ module Line
|
|
29
30
|
private
|
30
31
|
|
31
32
|
def match_alt_text?(message)
|
32
|
-
return false unless message[
|
33
|
+
return false unless message["type"] == "flex"
|
33
34
|
return true if @expected.nil?
|
34
35
|
|
35
|
-
message[
|
36
|
+
message["altText"].match?(@expected)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -8,7 +8,7 @@ module Line
|
|
8
8
|
# The quick reply matcher for RSpec to search for quick reply action in the message array.
|
9
9
|
class HaveQuickReply
|
10
10
|
def initialize(expected)
|
11
|
-
@expected = expected
|
11
|
+
@expected = Utils.stringify_keys!(expected, deep: true)
|
12
12
|
end
|
13
13
|
|
14
14
|
def description
|
@@ -18,9 +18,10 @@ module Line
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def matches?(actual)
|
21
|
-
@actual = actual
|
21
|
+
@actual = Utils.stringify_keys!(actual, deep: true)
|
22
22
|
@actual.any? { |message| match_message(message) }
|
23
23
|
end
|
24
|
+
alias == matches?
|
24
25
|
|
25
26
|
def failure_message
|
26
27
|
"expected to find a quick reply message matching #{@expected}"
|
@@ -29,10 +30,10 @@ module Line
|
|
29
30
|
private
|
30
31
|
|
31
32
|
def match_message(message)
|
32
|
-
reply = message[
|
33
|
+
reply = message["quickReply"]
|
33
34
|
return false unless reply
|
34
35
|
|
35
|
-
reply[
|
36
|
+
reply["items"].any? { |item| match_action(item["action"]) }
|
36
37
|
end
|
37
38
|
|
38
39
|
def match_action(action)
|
@@ -8,32 +8,37 @@ module Line
|
|
8
8
|
# The text message matcher for RSpec to search for text messages in the message array.
|
9
9
|
class HaveTextMessage
|
10
10
|
def initialize(expected)
|
11
|
-
@
|
11
|
+
@text, @options = expected
|
12
|
+
@options = Utils.stringify_keys!(@options || {})
|
12
13
|
end
|
13
14
|
|
14
15
|
def description
|
15
|
-
return "have text message" if @
|
16
|
+
return "have text message" if @text.nil?
|
17
|
+
return "have text message matching #{@text.inspect}" if @options.empty?
|
16
18
|
|
17
|
-
"have text message matching #{@
|
19
|
+
"have text message matching #{@text.inspect} with options #{@options.inspect}"
|
18
20
|
end
|
19
21
|
|
20
22
|
def matches?(actual)
|
21
|
-
@actual = actual
|
22
|
-
@actual.
|
23
|
-
next unless message[
|
23
|
+
@actual = Utils.stringify_keys!(actual, deep: true)
|
24
|
+
@actual.any? do |message|
|
25
|
+
next unless message["type"] == "text"
|
26
|
+
next true if @text.nil?
|
24
27
|
|
25
|
-
|
28
|
+
message["text"].match?(@text) && ::RSpec::Matchers::BuiltIn::Include.new(@options).matches?(message)
|
26
29
|
end
|
27
|
-
|
28
|
-
false
|
29
30
|
end
|
31
|
+
alias == matches?
|
30
32
|
|
31
33
|
def failure_message
|
32
|
-
"expected to find a text message
|
34
|
+
return "expected to find a text message" if @text.nil?
|
35
|
+
return "expected to find a text message matching #{@text.inspect}" if @options.empty?
|
36
|
+
|
37
|
+
"expected to find a text message matching #{@text.inspect} with options #{@options.inspect}"
|
33
38
|
end
|
34
39
|
end
|
35
40
|
|
36
|
-
def have_line_text_message(expected
|
41
|
+
def have_line_text_message(*expected) # rubocop:disable Naming/PredicateName
|
37
42
|
HaveTextMessage.new(expected)
|
38
43
|
end
|
39
44
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module RSpec
|
6
|
+
module Matchers
|
7
|
+
# :nodoc:
|
8
|
+
module Utils
|
9
|
+
def self.stringify_keys!(arg, deep: false) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
10
|
+
case arg
|
11
|
+
when Hash
|
12
|
+
Hash[
|
13
|
+
*arg.map { |key, value| [key.to_s, deep ? stringify_keys!(value, deep: deep) : value] }
|
14
|
+
.inject([]) { |memo, pair| memo + pair }
|
15
|
+
]
|
16
|
+
when Array
|
17
|
+
arg.map { |item| deep ? stringify_keys!(item, deep: deep) : item }
|
18
|
+
else
|
19
|
+
arg
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "matchers/utils"
|
3
4
|
require_relative "matchers/have_text_message"
|
4
5
|
require_relative "matchers/have_quick_reply"
|
5
|
-
|
6
6
|
require_relative "matchers/have_flex_message"
|
7
|
+
require_relative "matchers/have_flex_bubble"
|
7
8
|
require_relative "matchers/have_flex_component"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: line-message-builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aotokitsuruya
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: The LINE Messaging API message builder.
|
14
14
|
email:
|
@@ -43,10 +43,12 @@ files:
|
|
43
43
|
- lib/line/message/builder/version.rb
|
44
44
|
- lib/line/message/rspec.rb
|
45
45
|
- lib/line/message/rspec/matchers.rb
|
46
|
+
- lib/line/message/rspec/matchers/have_flex_bubble.rb
|
46
47
|
- lib/line/message/rspec/matchers/have_flex_component.rb
|
47
48
|
- lib/line/message/rspec/matchers/have_flex_message.rb
|
48
49
|
- lib/line/message/rspec/matchers/have_quick_reply.rb
|
49
50
|
- lib/line/message/rspec/matchers/have_text_message.rb
|
51
|
+
- lib/line/message/rspec/matchers/utils.rb
|
50
52
|
- release-please-config.json
|
51
53
|
- sig/line/message/builder.rbs
|
52
54
|
homepage: https://github.com/elct9620/line-message-builder
|