github.com/line/line-bot-sdk-go/v7@v7.21.0/linebot/flex_test.go (about)

     1  // Copyright 2018 LINE Corporation
     2  //
     3  // LINE Corporation licenses this file to you under the Apache License,
     4  // version 2.0 (the "License"); you may not use this file except in compliance
     5  // with the License. You may obtain a copy of the License at:
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  package linebot
    16  
    17  import (
    18  	"encoding/json"
    19  	"reflect"
    20  	"strconv"
    21  	"testing"
    22  )
    23  
    24  func TestUnmarshalFlexMessageJSON(t *testing.T) {
    25  	testCases := []struct {
    26  		JSON []byte
    27  		Want FlexContainer
    28  	}{
    29  		{
    30  			JSON: []byte(`{
    31  	"type": "bubble",
    32  	"body": {
    33  		"type": "box",
    34  		"layout": "vertical",
    35  		"borderColor": "#000000",
    36  		"borderWidth": "2px",
    37  		"cornerRadius": "20px",
    38  		"contents": [
    39  			{
    40  				"type": "text",
    41  				"text": "hello",
    42  				"lineSpacing": "20px"
    43  			}
    44  		]
    45  	}
    46  }`),
    47  			Want: &BubbleContainer{
    48  				Type: FlexContainerTypeBubble,
    49  				Body: &BoxComponent{
    50  					Type:         FlexComponentTypeBox,
    51  					Layout:       FlexBoxLayoutTypeVertical,
    52  					BorderColor:  "#000000",
    53  					BorderWidth:  "2px",
    54  					CornerRadius: "20px",
    55  					Contents: []FlexComponent{
    56  						&TextComponent{
    57  							Type:        FlexComponentTypeText,
    58  							Text:        "hello",
    59  							LineSpacing: "20px",
    60  						},
    61  					},
    62  				},
    63  			},
    64  		},
    65  		{
    66  			JSON: []byte(`{
    67  	"type": "bubble",
    68  	"body": {
    69  		"type": "box",
    70  		"layout": "vertical",
    71  		"contents": [
    72  			{
    73  				"type": "text",
    74  				"text": "hello"
    75  			},
    76  			{
    77  				"type": "text",
    78  				"text": "world"
    79  			}
    80  		]
    81  	}
    82  }`),
    83  			Want: &BubbleContainer{
    84  				Type: FlexContainerTypeBubble,
    85  				Body: &BoxComponent{
    86  					Type:   FlexComponentTypeBox,
    87  					Layout: FlexBoxLayoutTypeVertical,
    88  					Contents: []FlexComponent{
    89  						&TextComponent{
    90  							Type: FlexComponentTypeText,
    91  							Text: "hello",
    92  						},
    93  						&TextComponent{
    94  							Type: FlexComponentTypeText,
    95  							Text: "world",
    96  						},
    97  					},
    98  				},
    99  			},
   100  		},
   101  		{
   102  			JSON: []byte(`{
   103  	"type": "carousel",
   104  	"contents": [
   105  		{
   106  			"type": "bubble",
   107  			"body": {
   108  				"type": "box",
   109  				"layout": "vertical",
   110  				"contents": [
   111  					{
   112  						"type": "text",
   113  						"text": "First bubble"
   114  					}
   115  				]
   116  			}
   117  		},
   118  		{
   119  			"type": "bubble",
   120  			"body": {
   121  				"type": "box",
   122  				"layout": "vertical",
   123  				"contents": [
   124  					{
   125  						"type": "text",
   126  						"text": "Second bubble"
   127  					}
   128  				],
   129  				"action": {
   130  					"type": "message",
   131  					"text": "Second bubble"
   132  				}
   133  			}
   134  		}
   135  	]
   136  }`),
   137  			Want: &CarouselContainer{
   138  				Type: FlexContainerTypeCarousel,
   139  				Contents: []*BubbleContainer{
   140  					{
   141  						Type: FlexContainerTypeBubble,
   142  						Body: &BoxComponent{
   143  							Type:   FlexComponentTypeBox,
   144  							Layout: FlexBoxLayoutTypeVertical,
   145  							Contents: []FlexComponent{
   146  								&TextComponent{
   147  									Type: FlexComponentTypeText,
   148  									Text: "First bubble",
   149  								},
   150  							},
   151  						},
   152  					},
   153  					{
   154  						Type: FlexContainerTypeBubble,
   155  						Body: &BoxComponent{
   156  							Type:   FlexComponentTypeBox,
   157  							Layout: FlexBoxLayoutTypeVertical,
   158  							Contents: []FlexComponent{
   159  								&TextComponent{
   160  									Type: FlexComponentTypeText,
   161  									Text: "Second bubble",
   162  								},
   163  							},
   164  							Action: &MessageAction{Text: "Second bubble"},
   165  						},
   166  					},
   167  				},
   168  			},
   169  		},
   170  		{
   171  			JSON: []byte(`{
   172  	"type": "bubble",
   173  	"size": "nano",
   174  	"body": {
   175  		"type": "box",
   176  		"layout": "vertical",
   177  		"contents": [
   178  			{
   179  				"type": "text",
   180  				"text": "hello"
   181  			},
   182  			{
   183  				"type": "text",
   184  				"text": "world"
   185  			}
   186  		]
   187  	}
   188  }`),
   189  			Want: &BubbleContainer{
   190  				Type: FlexContainerTypeBubble,
   191  				Size: FlexBubbleSizeTypeNano,
   192  				Body: &BoxComponent{
   193  					Type:   FlexComponentTypeBox,
   194  					Layout: FlexBoxLayoutTypeVertical,
   195  					Contents: []FlexComponent{
   196  						&TextComponent{
   197  							Type: FlexComponentTypeText,
   198  							Text: "hello",
   199  						},
   200  						&TextComponent{
   201  							Type: FlexComponentTypeText,
   202  							Text: "world",
   203  						},
   204  					},
   205  				},
   206  			},
   207  		},
   208  		{
   209  			JSON: []byte(`{
   210  	"type": "bubble",
   211  	"hero": {
   212  		"type": "image",
   213  		"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png",
   214  		"position": "absolute",
   215  		"size": "full",
   216  		"aspectRatio": "20:13",
   217  		"aspectMode": "cover",
   218  		"action": {
   219  			"type": "uri",
   220  			"uri": "https://linecorp.com/"
   221  		},
   222  		"animated": true,
   223  		"offsetTop": "xs",
   224  		"offsetBottom": "sm",
   225  		"offsetStart": "md",
   226  		"offsetEnd": "lg"
   227  	},
   228  	"body": {
   229  		"type": "box",
   230  		"layout": "vertical",
   231  		"contents": [
   232  			{
   233  				"type": "text",
   234  				"text": "Brown Cafe",
   235  				"weight": "bold",
   236  				"size": "xl"
   237  			},
   238  			{
   239  				"type": "box",
   240  				"layout": "baseline",
   241  				"margin": "md",
   242  				"contents": [
   243  					{
   244  						"type": "icon",
   245  						"size": "sm",
   246  						"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"
   247  					},
   248  					{
   249  						"type": "icon",
   250  						"size": "sm",
   251  						"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"
   252  					},
   253  					{
   254  						"type": "icon",
   255  						"size": "sm",
   256  						"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"
   257  					},
   258  					{
   259  						"type": "icon",
   260  						"size": "sm",
   261  						"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"
   262  					},
   263  					{
   264  						"type": "icon",
   265  						"size": "sm",
   266  						"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png"
   267  					},
   268  					{
   269  						"type": "text",
   270  						"text": "4.0",
   271  						"size": "sm",
   272  						"color": "#999999",
   273  						"margin": "md",
   274  						"flex": 0
   275  					}
   276  				]
   277  			},
   278  			{
   279  				"type": "box",
   280  				"layout": "vertical",
   281  				"margin": "lg",
   282  				"spacing": "sm",
   283  				"contents": [
   284  					{
   285  						"type": "box",
   286  						"layout": "baseline",
   287  						"spacing": "sm",
   288  						"contents": [
   289  							{
   290  								"type": "text",
   291  								"text": "Place",
   292  								"color": "#aaaaaa",
   293  								"size": "sm",
   294  								"flex": 1,
   295  								"maxLines": 0
   296  							},
   297  							{
   298  								"type": "text",
   299  								"text": "Miraina Tower, 4-1-6 Shinjuku, Tokyo",
   300  								"wrap": true,
   301  								"color": "#666666",
   302  								"size": "sm",
   303  								"flex": 5,
   304  								"maxLines": 1
   305  							}
   306  						]
   307  					},
   308  					{
   309  						"type": "box",
   310  						"layout": "baseline",
   311  						"spacing": "sm",
   312  						"contents": [
   313  							{
   314  								"type": "text",
   315  								"text": "Time",
   316  								"color": "#aaaaaa",
   317  								"size": "sm",
   318  								"flex": 1
   319  							},
   320  							{
   321  								"type": "text",
   322  								"text": "10:00 - 23:00",
   323  								"position": "absolute",
   324  								"wrap": true,
   325  								"color": "#666666",
   326  								"size": "sm",
   327  								"flex": 5,
   328  								"offsetTop": "xs",
   329  								"offsetBottom": "sm",
   330  								"offsetStart": "md",
   331  								"offsetEnd": "lg"
   332  							}
   333  						]
   334  					}
   335  				]
   336  			}
   337  		]
   338  	},
   339  	"footer": {
   340  		"type": "box",
   341  		"layout": "vertical",
   342  		"position": "absolute",
   343  		"spacing": "sm",
   344  		"contents": [
   345  			{
   346  				"type": "button",
   347  				"style": "link",
   348  				"height": "sm",
   349  				"action": {
   350  					"type": "uri",
   351  					"label": "CALL",
   352  					"uri": "https://linecorp.com"
   353  				},
   354  				"position": "absolute",
   355  				"offsetTop": "xs",
   356  				"offsetBottom": "sm",
   357  				"offsetStart": "md",
   358  				"offsetEnd": "lg"
   359  			},
   360  			{
   361  				"type": "button",
   362  				"style": "link",
   363  				"height": "sm",
   364  				"action": {
   365  					"type": "uri",
   366  					"label": "WEBSITE",
   367  					"uri": "https://linecorp.com",
   368  					"altUri": {
   369  						"desktop": "https://line.me/ja/download"
   370  					}
   371  				},
   372  				"adjustMode": "shrink-to-fit"
   373  			},
   374  			{
   375  				"type": "spacer",
   376  				"size": "sm"
   377  			},
   378  			{
   379  				"type": "spacer"
   380  			},
   381  			{
   382  				"type": "spacer",
   383  				"size": "xs"
   384  			}
   385  		],
   386  		"flex": 0,
   387  		"offsetTop": "xs",
   388  		"offsetBottom": "sm",
   389  		"offsetStart": "md",
   390  		"offsetEnd": "lg"
   391  	}
   392  }`),
   393  			Want: &BubbleContainer{
   394  				Type: FlexContainerTypeBubble,
   395  				Hero: &ImageComponent{
   396  					Type:         FlexComponentTypeImage,
   397  					URL:          "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png",
   398  					Position:     FlexComponentPositionTypeAbsolute,
   399  					Size:         FlexImageSizeTypeFull,
   400  					AspectRatio:  FlexImageAspectRatioType20to13,
   401  					AspectMode:   FlexImageAspectModeTypeCover,
   402  					Action:       &URIAction{URI: "https://linecorp.com/"},
   403  					Animated:     true,
   404  					OffsetTop:    FlexComponentOffsetTypeXs,
   405  					OffsetBottom: FlexComponentOffsetTypeSm,
   406  					OffsetStart:  FlexComponentOffsetTypeMd,
   407  					OffsetEnd:    FlexComponentOffsetTypeLg,
   408  				},
   409  				Body: &BoxComponent{
   410  					Type:   FlexComponentTypeBox,
   411  					Layout: FlexBoxLayoutTypeVertical,
   412  					Contents: []FlexComponent{
   413  						&TextComponent{
   414  							Type:   FlexComponentTypeText,
   415  							Text:   "Brown Cafe",
   416  							Size:   FlexTextSizeTypeXl,
   417  							Weight: FlexTextWeightTypeBold,
   418  						},
   419  						&BoxComponent{
   420  							Type:   FlexComponentTypeBox,
   421  							Layout: FlexBoxLayoutTypeBaseline,
   422  							Contents: []FlexComponent{
   423  								&IconComponent{
   424  									Type: FlexComponentTypeIcon,
   425  									URL:  "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
   426  									Size: FlexIconSizeTypeSm,
   427  								},
   428  								&IconComponent{
   429  									Type: FlexComponentTypeIcon,
   430  									URL:  "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
   431  									Size: FlexIconSizeTypeSm,
   432  								},
   433  								&IconComponent{
   434  									Type: FlexComponentTypeIcon,
   435  									URL:  "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
   436  									Size: FlexIconSizeTypeSm,
   437  								},
   438  								&IconComponent{
   439  									Type: FlexComponentTypeIcon,
   440  									URL:  "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
   441  									Size: FlexIconSizeTypeSm,
   442  								},
   443  								&IconComponent{
   444  									Type: FlexComponentTypeIcon,
   445  									URL:  "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png",
   446  									Size: FlexIconSizeTypeSm,
   447  								},
   448  								&TextComponent{
   449  									Type:   FlexComponentTypeText,
   450  									Text:   "4.0",
   451  									Flex:   IntPtr(0),
   452  									Margin: FlexComponentMarginTypeMd,
   453  									Size:   FlexTextSizeTypeSm,
   454  									Color:  "#999999",
   455  								},
   456  							},
   457  							Margin: FlexComponentMarginTypeMd,
   458  						},
   459  						&BoxComponent{
   460  							Type:   FlexComponentTypeBox,
   461  							Layout: FlexBoxLayoutTypeVertical,
   462  							Contents: []FlexComponent{
   463  								&BoxComponent{
   464  									Type:   FlexComponentTypeBox,
   465  									Layout: FlexBoxLayoutTypeBaseline,
   466  									Contents: []FlexComponent{
   467  										&TextComponent{
   468  											Type:     FlexComponentTypeText,
   469  											Text:     "Place",
   470  											Flex:     IntPtr(1),
   471  											Size:     FlexTextSizeTypeSm,
   472  											Color:    "#aaaaaa",
   473  											MaxLines: IntPtr(0),
   474  										},
   475  										&TextComponent{
   476  											Type:     FlexComponentTypeText,
   477  											Text:     "Miraina Tower, 4-1-6 Shinjuku, Tokyo",
   478  											Flex:     IntPtr(5),
   479  											Size:     FlexTextSizeTypeSm,
   480  											Wrap:     true,
   481  											Color:    "#666666",
   482  											MaxLines: IntPtr(1),
   483  										},
   484  									},
   485  									Spacing: FlexComponentSpacingTypeSm,
   486  								},
   487  								&BoxComponent{
   488  									Type:   FlexComponentTypeBox,
   489  									Layout: FlexBoxLayoutTypeBaseline,
   490  									Contents: []FlexComponent{
   491  										&TextComponent{
   492  											Type:  FlexComponentTypeText,
   493  											Text:  "Time",
   494  											Flex:  IntPtr(1),
   495  											Size:  FlexTextSizeTypeSm,
   496  											Color: "#aaaaaa",
   497  										},
   498  										&TextComponent{
   499  											Type:         FlexComponentTypeText,
   500  											Text:         "10:00 - 23:00",
   501  											Position:     FlexComponentPositionTypeAbsolute,
   502  											Flex:         IntPtr(5),
   503  											Size:         FlexTextSizeTypeSm,
   504  											Wrap:         true,
   505  											Color:        "#666666",
   506  											OffsetTop:    FlexComponentOffsetTypeXs,
   507  											OffsetBottom: FlexComponentOffsetTypeSm,
   508  											OffsetStart:  FlexComponentOffsetTypeMd,
   509  											OffsetEnd:    FlexComponentOffsetTypeLg,
   510  										},
   511  									},
   512  									Spacing: FlexComponentSpacingTypeSm,
   513  								},
   514  							},
   515  							Spacing: FlexComponentSpacingTypeSm,
   516  							Margin:  FlexComponentMarginTypeLg,
   517  						},
   518  					},
   519  				},
   520  				Footer: &BoxComponent{
   521  					Type:     FlexComponentTypeBox,
   522  					Layout:   FlexBoxLayoutTypeVertical,
   523  					Position: FlexComponentPositionTypeAbsolute,
   524  					Contents: []FlexComponent{
   525  						&ButtonComponent{
   526  							Type: FlexComponentTypeButton,
   527  							Action: &URIAction{
   528  								Label: "CALL",
   529  								URI:   "https://linecorp.com",
   530  							},
   531  							Position:     FlexComponentPositionTypeAbsolute,
   532  							Height:       FlexButtonHeightTypeSm,
   533  							Style:        FlexButtonStyleTypeLink,
   534  							OffsetTop:    FlexComponentOffsetTypeXs,
   535  							OffsetBottom: FlexComponentOffsetTypeSm,
   536  							OffsetStart:  FlexComponentOffsetTypeMd,
   537  							OffsetEnd:    FlexComponentOffsetTypeLg,
   538  						},
   539  						&ButtonComponent{
   540  							Type: FlexComponentTypeButton,
   541  							Action: &URIAction{
   542  								Label: "WEBSITE",
   543  								URI:   "https://linecorp.com",
   544  								AltURI: &URIActionAltURI{
   545  									Desktop: "https://line.me/ja/download",
   546  								},
   547  							},
   548  							Height:     FlexButtonHeightTypeSm,
   549  							Style:      FlexButtonStyleTypeLink,
   550  							AdjustMode: FlexComponentAdjustModeTypeShrinkToFit,
   551  						},
   552  						&SpacerComponent{
   553  							Type: FlexComponentTypeSpacer,
   554  							Size: FlexSpacerSizeTypeSm,
   555  						},
   556  						&SpacerComponent{
   557  							Type: FlexComponentTypeSpacer,
   558  						},
   559  						&SpacerComponent{
   560  							Type: FlexComponentTypeSpacer,
   561  							Size: FlexSpacerSizeTypeXs,
   562  						},
   563  					},
   564  					Spacing:      FlexComponentSpacingTypeSm,
   565  					Flex:         IntPtr(0),
   566  					OffsetTop:    FlexComponentOffsetTypeXs,
   567  					OffsetBottom: FlexComponentOffsetTypeSm,
   568  					OffsetStart:  FlexComponentOffsetTypeMd,
   569  					OffsetEnd:    FlexComponentOffsetTypeLg,
   570  				},
   571  			},
   572  		},
   573  		{
   574  			JSON: []byte(`{
   575  	"type": "bubble",
   576  	"body": {
   577  		"type": "box",
   578  		"layout": "horizontal",
   579  		"contents": [
   580  			{
   581  				"type": "text",
   582  				"text": "hello",
   583  				"flex": 0
   584  			},
   585  			{
   586  				"type": "filler",
   587  				"flex": 4
   588  			},
   589  			{
   590  				"type": "text",
   591  				"text": "world",
   592  				"flex": 2
   593  			},
   594  			{
   595  				"type": "text",
   596  				"contents": [
   597  					{
   598  						"type": "span",
   599  						"text": "hi"
   600  					},
   601  					{
   602  						"type": "span",
   603  						"text": "span",
   604  						"size": "xl",
   605  						"color": "#29cf5b"
   606  					}
   607  				]
   608  			}
   609  		]
   610  	}
   611  }`),
   612  			Want: &BubbleContainer{
   613  				Type: FlexContainerTypeBubble,
   614  				Body: &BoxComponent{
   615  					Type:   FlexComponentTypeBox,
   616  					Layout: FlexBoxLayoutTypeHorizontal,
   617  					Contents: []FlexComponent{
   618  						&TextComponent{
   619  							Type: FlexComponentTypeText,
   620  							Text: "hello",
   621  							Flex: IntPtr(0),
   622  						},
   623  						&FillerComponent{
   624  							Type: FlexComponentTypeFiller,
   625  							Flex: IntPtr(4),
   626  						},
   627  						&TextComponent{
   628  							Type: FlexComponentTypeText,
   629  							Text: "world",
   630  							Flex: IntPtr(2),
   631  						},
   632  						&TextComponent{
   633  							Type: FlexComponentTypeText,
   634  							Contents: []*SpanComponent{
   635  								{
   636  									Type: FlexComponentTypeSpan,
   637  									Text: "hi",
   638  								},
   639  								{
   640  									Type:  FlexComponentTypeSpan,
   641  									Text:  "span",
   642  									Size:  FlexTextSizeTypeXl,
   643  									Color: "#29cf5b",
   644  								},
   645  							},
   646  						},
   647  					},
   648  				},
   649  			},
   650  		},
   651  		{
   652  			JSON: []byte(`{
   653  	"type": "bubble",
   654  	"size": "nano",
   655  	"hero": {
   656  		"type": "box",
   657  		"layout": "vertical",
   658  		"contents": [
   659  			{
   660  				"type": "text",
   661  				"text": "hello"
   662  			},
   663  			{
   664  				"type": "text",
   665  				"text": "world"
   666  			}
   667  		]
   668  	}
   669  }`),
   670  			Want: &BubbleContainer{
   671  				Type: FlexContainerTypeBubble,
   672  				Size: FlexBubbleSizeTypeNano,
   673  				Hero: &BoxComponent{
   674  					Type:   FlexComponentTypeBox,
   675  					Layout: FlexBoxLayoutTypeVertical,
   676  					Contents: []FlexComponent{
   677  						&TextComponent{
   678  							Type: FlexComponentTypeText,
   679  							Text: "hello",
   680  						},
   681  						&TextComponent{
   682  							Type: FlexComponentTypeText,
   683  							Text: "world",
   684  						},
   685  					},
   686  				},
   687  			},
   688  		},
   689  		{
   690  			JSON: []byte(`{
   691  	"type": "bubble",
   692  	"body": {
   693  		"type": "box",
   694  		"layout": "vertical",
   695  		"contents": [
   696  			{
   697  				"type": "image",
   698  				"url": "https://example.com/flex/images/image.jpg",
   699  				"animated": true
   700  			},
   701  			{
   702  				"type": "separator"
   703  			},
   704  			{
   705  				"type": "text",
   706  				"text": "Text in the box",
   707  				"adjustMode": "shrink-to-fit"
   708  			},
   709  			{
   710  				"type": "box",
   711  				"layout": "vertical",
   712  				"contents": [],
   713  				"width": "30px",
   714  				"maxWidth": "30px",
   715  				"height": "30px",
   716  				"maxHeight": "30px",
   717  				"background": {
   718  					"type": "linearGradient",
   719  					"angle": "0deg",
   720  					"startColor": "#ff0000",
   721  					"centerColor": "#0000ff",
   722  					"endColor": "#00ff00",
   723  					"centerPosition": "10%"
   724  				}
   725  			}
   726  		],
   727  		"height": "400px",
   728  		"justifyContent": "space-evenly",
   729  		"alignItems": "center"
   730  	}
   731  }`),
   732  			Want: &BubbleContainer{
   733  				Type: FlexContainerTypeBubble,
   734  				Body: &BoxComponent{
   735  					Type:   FlexComponentTypeBox,
   736  					Layout: FlexBoxLayoutTypeVertical,
   737  					Contents: []FlexComponent{
   738  						&ImageComponent{
   739  							Type:     FlexComponentTypeImage,
   740  							URL:      "https://example.com/flex/images/image.jpg",
   741  							Animated: true,
   742  						},
   743  						&SeparatorComponent{
   744  							Type: FlexComponentTypeSeparator,
   745  						},
   746  						&TextComponent{
   747  							Type:       FlexComponentTypeText,
   748  							Text:       "Text in the box",
   749  							AdjustMode: FlexComponentAdjustModeTypeShrinkToFit,
   750  						},
   751  						&BoxComponent{
   752  							Type:      FlexComponentTypeBox,
   753  							Layout:    FlexBoxLayoutTypeVertical,
   754  							Contents:  []FlexComponent{},
   755  							Width:     "30px",
   756  							MaxWidth:  "30px",
   757  							Height:    "30px",
   758  							MaxHeight: "30px",
   759  							Background: &BoxBackground{
   760  								Type:           FlexBoxBackgroundTypeLinearGradient,
   761  								Angle:          "0deg",
   762  								StartColor:     "#ff0000",
   763  								EndColor:       "#00ff00",
   764  								CenterColor:    "#0000ff",
   765  								CenterPosition: "10%",
   766  							},
   767  						},
   768  					},
   769  					Height:         "400px",
   770  					JustifyContent: FlexComponentJustifyContentTypeSpaceEvenly,
   771  					AlignItems:     FlexComponentAlignItemsTypeCenter,
   772  				},
   773  			},
   774  		},
   775  		{
   776  			JSON: []byte(`{
   777  	"type": "bubble",
   778  	"hero": {
   779      	"type": "video",
   780      	"url": "https://example.com/video.mp4",
   781      	"previewUrl": "https://example.com/video_preview.png",
   782      	"altContent": {
   783        		"type": "image",
   784        		"size": "full",
   785        		"aspectRatio": "20:13",
   786        		"aspectMode": "cover",
   787        		"url": "https://example.com/image.png"
   788      	},
   789      	"action": {
   790        		"type": "uri",
   791        		"label": "More information",
   792        		"uri": "http://linecorp.com/"
   793      	},
   794      	"aspectRatio": "20:13"
   795    	}
   796  }`),
   797  			Want: &BubbleContainer{
   798  				Type: FlexContainerTypeBubble,
   799  				Hero: &VideoComponent{
   800  					Type:       FlexComponentTypeVideo,
   801  					URL:        "https://example.com/video.mp4",
   802  					PreviewURL: "https://example.com/video_preview.png",
   803  					AltContent: &ImageComponent{
   804  						Type:        FlexComponentTypeImage,
   805  						URL:         "https://example.com/image.png",
   806  						Size:        FlexImageSizeTypeFull,
   807  						AspectRatio: FlexImageAspectRatioType20to13,
   808  						AspectMode:  FlexImageAspectModeTypeCover,
   809  					},
   810  					Action: &URIAction{
   811  						Label: "More information",
   812  						URI:   "http://linecorp.com/",
   813  					},
   814  					AspectRatio: FlexVideoAspectRatioType20to13,
   815  				},
   816  			},
   817  		},
   818  	}
   819  	for i, tc := range testCases {
   820  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   821  			container, err := UnmarshalFlexMessageJSON(tc.JSON)
   822  			if err != nil {
   823  				t.Fatal(err)
   824  			}
   825  			if !reflect.DeepEqual(container, tc.Want) {
   826  				t.Errorf("Container %v, want %v", container, tc.Want)
   827  			}
   828  		})
   829  	}
   830  }
   831  
   832  func TestMarshalJSON(t *testing.T) {
   833  	testCases := []struct {
   834  		component FlexComponent
   835  		want      []byte
   836  	}{
   837  		{
   838  			&FillerComponent{
   839  				Type: FlexComponentTypeFiller,
   840  				Flex: nil,
   841  			},
   842  			[]byte(`{"type":"filler"}`),
   843  		},
   844  		{
   845  			&FillerComponent{
   846  				Type: FlexComponentTypeFiller,
   847  				Flex: IntPtr(4),
   848  			},
   849  			[]byte(`{"type":"filler","flex":4}`),
   850  		},
   851  		{
   852  			&SpanComponent{
   853  				Type:       FlexComponentTypeSpan,
   854  				Text:       "span",
   855  				Size:       FlexTextSizeTypeMd,
   856  				Weight:     FlexTextWeightTypeRegular,
   857  				Color:      "#0000ff",
   858  				Style:      FlexTextStyleTypeNormal,
   859  				Decoration: FlexTextDecorationTypeNone,
   860  			},
   861  			[]byte(`{"type":"span","text":"span","size":"md","weight":"regular","color":"#0000ff","style":"normal","decoration":"none"}`),
   862  		},
   863  	}
   864  
   865  	for i, tc := range testCases {
   866  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   867  			got, err := json.Marshal(tc.component)
   868  			if err != nil {
   869  				t.Fatal(err)
   870  			}
   871  			if !reflect.DeepEqual(got, tc.want) {
   872  				t.Errorf("got %s, want %s", string(got), string(tc.want))
   873  			}
   874  		})
   875  	}
   876  }
   877  
   878  func BenchmarkUnmarshalFlexMessageJSON(b *testing.B) {
   879  	var jsonData = []byte(`{
   880  		"type": "bubble",
   881  		"header": {
   882  			"type": "box",
   883  			"layout": "horizontal",
   884  			"contents": [
   885  				{
   886  					"type": "text",
   887  					"text": "NEWS DIGEST",
   888  					"weight": "bold",
   889  					"color": "#aaaaaa",
   890  					"size": "sm"
   891  				}
   892  			]
   893  		},
   894  		"hero": {
   895  			"type": "image",
   896  			"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_4_news.png",
   897  			"size": "full",
   898  			"aspectRatio": "20:13",
   899  			"aspectMode": "cover",
   900  			"action": {
   901  				"type": "uri",
   902  				"uri": "https://linecorp.com/"
   903  			}
   904  		},
   905  		"body": {
   906  			"type": "box",
   907  			"layout": "horizontal",
   908  			"spacing": "md",
   909  			"contents": [
   910  				{
   911  					"type": "box",
   912  					"layout": "vertical",
   913  					"flex": 1,
   914  					"contents": [
   915  						{
   916  							"type": "image",
   917  							"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/02_1_news_thumbnail_1.png",
   918  							"aspectMode": "cover",
   919  							"aspectRatio": "4:3",
   920  							"size": "sm",
   921  							"gravity": "bottom"
   922  						},
   923  						{
   924  							"type": "image",
   925  							"url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/02_1_news_thumbnail_2.png",
   926  							"aspectMode": "cover",
   927  							"aspectRatio": "4:3",
   928  							"margin": "md",
   929  							"size": "sm"
   930  						}
   931  					]
   932  				},
   933  				{
   934  					"type": "box",
   935  					"layout": "vertical",
   936  					"flex": 2,
   937  					"contents": [
   938  						{
   939  							"type": "text",
   940  							"text": "7 Things to Know for Today",
   941  							"gravity": "top",
   942  							"size": "xs",
   943  							"flex": 1
   944  						},
   945  						{
   946  							"type": "separator"
   947  						},
   948  						{
   949  							"type": "text",
   950  							"text": "Hay fever goes wild",
   951  							"gravity": "center",
   952  							"size": "xs",
   953  							"flex": 2
   954  						},
   955  						{
   956  							"type": "separator"
   957  						},
   958  						{
   959  							"type": "text",
   960  							"text": "LINE Pay Begins Barcode Payment Service",
   961  							"gravity": "center",
   962  							"size": "xs",
   963  							"flex": 2
   964  						},
   965  						{
   966  							"type": "separator"
   967  						},
   968  						{
   969  							"type": "text",
   970  							"text": "LINE Adds LINE Wallet",
   971  							"gravity": "bottom",
   972  							"size": "xs",
   973  							"flex": 1
   974  						},
   975  						{
   976  							"type": "text",
   977  							"contents": [
   978  								{
   979  									"type": "span",
   980  									"text": "LINE",
   981  									"size": "xxl",
   982  									"weight": "bold",
   983  									"style": "italic",
   984  									"color": "#4f8f00"
   985  								},
   986  								{
   987  									"type": "span",
   988  									"text": "MUSIC",
   989  									"size": "xxl",
   990  									"weight": "bold",
   991  									"style": "italic",
   992  									"color": "#4f8f00"
   993  								}
   994  							]
   995  						}
   996  					]
   997  				}
   998  			]
   999  		},
  1000  		"footer": {
  1001  			"type": "box",
  1002  			"layout": "horizontal",
  1003  			"contents": [
  1004  				{
  1005  					"type": "button",
  1006  					"action": {
  1007  						"type": "uri",
  1008  						"label": "More",
  1009  						"uri": "https://linecorp.com"
  1010  					}
  1011  				}
  1012  			]
  1013  		}
  1014  	}`)
  1015  	b.ResetTimer()
  1016  	for i := 0; i < b.N; i++ {
  1017  		_, err := UnmarshalFlexMessageJSON(jsonData)
  1018  		if err != nil {
  1019  			b.Fatal(err)
  1020  		}
  1021  	}
  1022  }