microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.0.4

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

Tests/Microsoft.Teams.Cards.Tests/AdaptiveCardsTest.cs

567lines · modecode

1using System.Text.Json;
2
3using Microsoft.Teams.Common;
4
5namespace Microsoft.Teams.Cards.Tests;
6
7public class AdaptiveCardsTest
8{
9 [Fact]
10 public void Should_Serialize_AdaptiveCard_Simple()
11 {
12 // arrange
13 AdaptiveCard card = new AdaptiveCard()
14 {
15 Body = new List<CardElement>()
16 {
17 new TextBlock("Hello, Adaptive Card!")
18 }
19 };
20
21 // act
22 var json = JsonSerializer.Serialize(card);
23
24 // assert
25 using var doc = JsonDocument.Parse(json);
26 var root = doc.RootElement;
27
28 Assert.True(root.TryGetProperty("body", out var bodyElement));
29 Assert.Equal(JsonValueKind.Array, bodyElement.ValueKind);
30 Assert.Equal(1, bodyElement.GetArrayLength());
31
32 var first = bodyElement[0];
33 Assert.Equal("TextBlock", first.GetProperty("type").GetString());
34 Assert.Equal("Hello, Adaptive Card!", first.GetProperty("text").GetString());
35 }
36
37 [Fact]
38 public void Should_Deserialize_AdaptiveCard_Simple()
39 {
40 string json = @"{
41 ""body"": [
42 {
43 ""type"": ""TextBlock"",
44 ""text"": ""Hello, Adaptive Card!""
45 }
46 ]
47 }";
48
49 var card = JsonSerializer.Deserialize<AdaptiveCard>(json);
50
51 Assert.NotNull(card);
52 Assert.Single(card.Body!);
53 Assert.IsType<TextBlock>(card.Body![0]);
54 Assert.Equal("Hello, Adaptive Card!", ((TextBlock)card.Body[0]).Text);
55 }
56
57 [Fact]
58 public void Should_Serialize_BasicCard_WithToggleInput()
59 {
60 // arrange - recreating CreateBasicAdaptiveCard from samples
61 var card = new AdaptiveCard
62 {
63 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
64 Body = new List<CardElement>
65 {
66 new TextBlock("Hello world")
67 {
68 Wrap = true,
69 Weight = TextWeight.Bolder
70 },
71 new ToggleInput("Notify me")
72 {
73 Id = "notify"
74 }
75 },
76 Actions = new List<Microsoft.Teams.Cards.Action>
77 {
78 new ExecuteAction
79 {
80 Title = "Submit",
81 Data = new Union<string, SubmitActionData>(new SubmitActionData { NonSchemaProperties = new Dictionary<string, object?> { { "action", "submit_basic" } } }),
82 AssociatedInputs = AssociatedInputs.Auto
83 }
84 }
85 };
86
87 // act
88 var json = JsonSerializer.Serialize(card);
89
90 // assert
91 using var doc = JsonDocument.Parse(json);
92 var root = doc.RootElement;
93
94 Assert.True(root.TryGetProperty("$schema", out var schemaElement));
95 Assert.Equal("http://adaptivecards.io/schemas/adaptive-card.json", schemaElement.GetString());
96
97 Assert.True(root.TryGetProperty("body", out var bodyElement));
98 Assert.Equal(2, bodyElement.GetArrayLength());
99
100 var textBlock = bodyElement[0];
101 Assert.Equal("TextBlock", textBlock.GetProperty("type").GetString());
102 Assert.Equal("Hello world", textBlock.GetProperty("text").GetString());
103 Assert.True(textBlock.GetProperty("weight").GetString() == "Bolder");
104
105 var toggleInput = bodyElement[1];
106 Assert.Equal("Input.Toggle", toggleInput.GetProperty("type").GetString());
107 Assert.Equal("notify", toggleInput.GetProperty("id").GetString());
108
109 Assert.True(root.TryGetProperty("actions", out var actionsElement));
110 Assert.Single(actionsElement.EnumerateArray());
111 }
112
113 [Fact]
114 public void Should_Deserialize_ProfileCard_WithInputs()
115 {
116 string json = @"{
117 ""schema"": ""http://adaptivecards.io/schemas/adaptive-card.json"",
118 ""body"": [
119 {
120 ""type"": ""TextBlock"",
121 ""text"": ""User Profile"",
122 ""weight"": ""Bolder"",
123 ""size"": ""Large""
124 },
125 {
126 ""type"": ""Input.Text"",
127 ""id"": ""name"",
128 ""label"": ""Name"",
129 ""value"": ""John Doe""
130 },
131 {
132 ""type"": ""Input.Text"",
133 ""id"": ""email"",
134 ""label"": ""Email"",
135 ""value"": ""john@contoso.com""
136 },
137 {
138 ""type"": ""Input.Toggle"",
139 ""id"": ""subscribe"",
140 ""title"": ""Subscribe to newsletter"",
141 ""value"": ""false""
142 }
143 ],
144 ""actions"": [
145 {
146 ""type"": ""Action.Execute"",
147 ""title"": ""Save"",
148 ""data"": {
149 ""action"": ""save_profile"",
150 ""entity_id"": ""12345""
151 }
152 }
153 ]
154 }";
155
156 var card = JsonSerializer.Deserialize<AdaptiveCard>(cardJson);
157
158 Assert.NotNull(card);
159 // Note: Schema might be serialized as $schema in JSON but not always set on deserialized object
160 Assert.Equal(4, card.Body!.Count);
161
162 var titleBlock = card.Body[0] as TextBlock;
163 Assert.NotNull(titleBlock);
164 Assert.Equal("User Profile", titleBlock.Text);
165 Assert.Equal("Bolder", titleBlock.Weight?.ToString());
166
167 var nameInput = card.Body[1] as TextInput;
168 Assert.NotNull(nameInput);
169 Assert.Equal("name", nameInput.Id);
170 Assert.Equal("Name", nameInput.Label);
171 Assert.Equal("John Doe", nameInput.Value);
172
173 var emailInput = card.Body[2] as TextInput;
174 Assert.NotNull(emailInput);
175 Assert.Equal("email", emailInput.Id);
176 Assert.Equal("Email", emailInput.Label);
177
178 var toggleInput = card.Body[3] as ToggleInput;
179 Assert.NotNull(toggleInput);
180 Assert.Equal("subscribe", toggleInput.Id);
181 Assert.Equal("Subscribe to newsletter", toggleInput.Title);
182
183 Assert.Single(card.Actions!);
184 var executeAction = card!.Actions[0]! as ExecuteAction;
185 Assert.NotNull(executeAction);
186 Assert.Equal("Save", executeAction.Title);
187 }
188
189 [Fact]
190 public void Should_Serialize_TaskFormCard_WithChoiceSet()
191 {
192 // arrange - recreating CreateTaskFormCard from samples
193 var card = new AdaptiveCard
194 {
195 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
196 Body = new List<CardElement>
197 {
198 new TextBlock("Create New Task")
199 {
200 Weight = TextWeight.Bolder,
201 Size = TextSize.Large
202 },
203 new TextInput
204 {
205 Id = "title",
206 Label = "Task Title",
207 Placeholder = "Enter task title"
208 },
209 new ChoiceSetInput
210 {
211 Id = "priority",
212 Label = "Priority",
213 Value = "medium",
214 Choices = new List<Choice>
215 {
216 new() { Title = "High", Value = "high" },
217 new() { Title = "Medium", Value = "medium" },
218 new() { Title = "Low", Value = "low" }
219 }
220 },
221 new DateInput
222 {
223 Id = "due_date",
224 Label = "Due Date",
225 Value = "2024-01-15"
226 }
227 }
228 };
229
230 // act
231 var json = JsonSerializer.Serialize(card);
232
233 // assert
234 using var doc = JsonDocument.Parse(json);
235 var root = doc.RootElement;
236
237 Assert.True(root.TryGetProperty("body", out var bodyElement));
238 Assert.Equal(4, bodyElement.GetArrayLength());
239
240 var choiceSetInput = bodyElement[2];
241 Assert.Equal("Input.ChoiceSet", choiceSetInput.GetProperty("type").GetString());
242 Assert.Equal("priority", choiceSetInput.GetProperty("id").GetString());
243 Assert.Equal("medium", choiceSetInput.GetProperty("value").GetString());
244
245 Assert.True(choiceSetInput.TryGetProperty("choices", out var choicesElement));
246 Assert.Equal(3, choicesElement.GetArrayLength());
247 Assert.Equal("High", choicesElement[0].GetProperty("title").GetString());
248 Assert.Equal("high", choicesElement[0].GetProperty("value").GetString());
249
250 var dateInput = bodyElement[3];
251 Assert.Equal("Input.Date", dateInput.GetProperty("type").GetString());
252 Assert.Equal("due_date", dateInput.GetProperty("id").GetString());
253 }
254
255 [Fact]
256 public void Should_Deserialize_ComplexCard_FromJson()
257 {
258 // Using the JSON structure from CreateCardFromJson in samples
259 string json = @"{
260 ""type"": ""AdaptiveCard"",
261 ""body"": [
262 {
263 ""type"": ""ColumnSet"",
264 ""columns"": [
265 {
266 ""type"": ""Column"",
267 ""verticalContentAlignment"": ""center"",
268 ""items"": [
269 {
270 ""type"": ""Image"",
271 ""style"": ""Person"",
272 ""url"": ""https://aka.ms/AAp9xo4"",
273 ""size"": ""Small"",
274 ""altText"": ""Portrait of David Claux""
275 }
276 ],
277 ""width"": ""auto""
278 },
279 {
280 ""type"": ""Column"",
281 ""spacing"": ""medium"",
282 ""verticalContentAlignment"": ""center"",
283 ""items"": [
284 {
285 ""type"": ""TextBlock"",
286 ""weight"": ""Bolder"",
287 ""text"": ""David Claux"",
288 ""wrap"": true
289 }
290 ],
291 ""width"": ""auto""
292 }
293 ]
294 },
295 {
296 ""type"": ""TextBlock"",
297 ""text"": ""This card was created from JSON deserialization!"",
298 ""wrap"": true,
299 ""color"": ""good"",
300 ""spacing"": ""medium""
301 }
302 ],
303 ""actions"": [
304 {
305 ""type"": ""Action.Execute"",
306 ""title"": ""Test JSON Action"",
307 ""data"": {
308 ""Value"": {
309 ""action"": ""test_json""
310 }
311 }
312 }
313 ],
314 ""version"": ""1.5"",
315 ""schema"": ""http://adaptivecards.io/schemas/adaptive-card.json""
316 }";
317
318 var card = JsonSerializer.Deserialize<AdaptiveCard>(json)!;
319
320 Assert.NotNull(card);
321 Assert.Equal("1.5", card.Version);
322 // Note: Schema property might not be set during deserialization, focus on content verification
323 Assert.Equal(2, card.Body!.Count);
324
325 var columnSet = card.Body[0] as ColumnSet;
326 Assert.NotNull(columnSet);
327 Assert.Equal(2, columnSet.Columns!.Count);
328
329 var firstColumn = columnSet.Columns[0];
330 Assert.Equal("auto", firstColumn!.Width.ToString());
331 Assert.Single(firstColumn.Items!);
332
333 var image = firstColumn.Items[0] as Image;
334 Assert.NotNull(image);
335 Assert.Equal("https://aka.ms/AAp9xo4", image.Url);
336 Assert.Equal("Person", image.Style?.ToString());
337
338 var textBlock = card.Body[1] as TextBlock;
339 Assert.NotNull(textBlock);
340 Assert.Equal("This card was created from JSON deserialization!", textBlock.Text);
341 Assert.Equal("good", textBlock.Color?.ToString());
342
343 Assert.Single(card.Actions!);
344 var executeAction = card.Actions[0] as ExecuteAction;
345 Assert.NotNull(executeAction);
346 Assert.Equal("Test JSON Action", executeAction.Title);
347 }
348
349 [Fact]
350 public void Should_Serialize_FeedbackCard_WithMultilineInput()
351 {
352 // arrange - recreating CreateFeedbackCard from samples
353 var card = new AdaptiveCard
354 {
355 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
356 Body = new List<CardElement>
357 {
358 new TextBlock("Feedback Form")
359 {
360 Weight = TextWeight.Bolder,
361 Size = TextSize.Large
362 },
363 new TextInput
364 {
365 Id = "feedback",
366 Label = "Your Feedback",
367 Placeholder = "Please share your thoughts...",
368 IsMultiline = true,
369 IsRequired = true
370 }
371 },
372 Actions = new List<Microsoft.Teams.Cards.Action>
373 {
374 new ExecuteAction
375 {
376 Title = "Submit Feedback",
377 Data = new Union<string, SubmitActionData>(new SubmitActionData { NonSchemaProperties = new Dictionary<string, object?> { { "action", "submit_feedback" } } }),
378 AssociatedInputs = AssociatedInputs.Auto
379 }
380 }
381 };
382
383 // act
384 var json = JsonSerializer.Serialize(card);
385
386 // assert
387 using var doc = JsonDocument.Parse(json);
388 var root = doc.RootElement;
389
390 Assert.True(root.TryGetProperty("body", out var bodyElement));
391 Assert.Equal(2, bodyElement.GetArrayLength());
392
393 var textInput = bodyElement[1];
394 Assert.Equal("Input.Text", textInput.GetProperty("type").GetString());
395 Assert.Equal("feedback", textInput.GetProperty("id").GetString());
396 Assert.Equal("Your Feedback", textInput.GetProperty("label").GetString());
397 Assert.True(textInput.GetProperty("isMultiline").GetBoolean());
398 Assert.True(textInput.GetProperty("isRequired").GetBoolean());
399
400 Assert.True(root.TryGetProperty("actions", out var actionsElement));
401 var action = actionsElement[0];
402 Assert.Equal("Action.Execute", action.GetProperty("type").GetString());
403 Assert.Equal("Submit Feedback", action.GetProperty("title").GetString());
404 }
405
406 [Fact]
407 public void Should_Deserialize_ValidationCard_WithNumberInput()
408 {
409 string json = @"{
410 ""schema"": ""http://adaptivecards.io/schemas/adaptive-card.json"",
411 ""body"": [
412 {
413 ""type"": ""TextBlock"",
414 ""text"": ""Profile with Validation"",
415 ""weight"": ""Bolder"",
416 ""size"": ""Large""
417 },
418 {
419 ""type"": ""Input.Number"",
420 ""id"": ""age"",
421 ""label"": ""Age"",
422 ""isRequired"": true,
423 ""min"": 0,
424 ""max"": 120
425 },
426 {
427 ""type"": ""Input.Text"",
428 ""id"": ""name"",
429 ""label"": ""Name"",
430 ""isRequired"": true,
431 ""errorMessage"": ""Name is required""
432 }
433 ]
434 }";
435
436 var card = JsonSerializer.Deserialize<AdaptiveCard>(cardJson);
437
438 Assert.NotNull(card);
439 Assert.Equal(3, card.Body!.Count);
440
441 var numberInput = card.Body[1] as NumberInput;
442 Assert.NotNull(numberInput);
443 Assert.Equal("age", numberInput.Id);
444 Assert.Equal("Age", numberInput.Label);
445 Assert.True(numberInput.IsRequired);
446 Assert.Equal(0, numberInput.Min);
447 Assert.Equal(120, numberInput.Max);
448
449 var textInput = card.Body[2] as TextInput;
450 Assert.NotNull(textInput);
451 Assert.Equal("name", textInput.Id);
452 Assert.True(textInput.IsRequired);
453 Assert.Equal("Name is required", textInput.ErrorMessage);
454 }
455
456 [Fact]
457 public void Should_Deserialize()
458 {
459 // Test what minimal JsonSerializerOptions are actually required
460 string json = """
461 {
462 "type": "AdaptiveCard",
463 "body": [
464 {
465 "type": "TextBlock",
466 "text": "Hello World",
467 "weight": "Bolder"
468 }
469 ],
470 "actions": [
471 {
472 "type": "Action.Execute",
473 "title": "Submit",
474 "associatedInputs": "auto"
475 }
476 ]
477 }
478 """;
479
480 // Test 1: No options at all
481 var card1 = JsonSerializer.Deserialize<AdaptiveCard>(json);
482 Assert.NotNull(card1);
483 Assert.Single(card1.Body!);
484
485 // Test 2: Only PropertyNameCaseInsensitive
486 var options2 = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
487 var card2 = JsonSerializer.Deserialize<AdaptiveCard>(json, options2);
488 Assert.NotNull(card2);
489 Assert.Single(card2.Body!);
490
491 // Test 3: With CamelCase policy (what we had in docs)
492 var options3 = new JsonSerializerOptions
493 {
494 PropertyNameCaseInsensitive = true,
495 PropertyNamingPolicy = JsonNamingPolicy.CamelCase
496 };
497 var card3 = JsonSerializer.Deserialize<AdaptiveCard>(json, options3);
498 Assert.NotNull(card3);
499 Assert.Single(card3.Body!);
500
501 // All should work the same
502 var textBlock1 = card1.Body![0] as TextBlock;
503 var textBlock2 = card2.Body![0] as TextBlock;
504 var textBlock3 = card3.Body![0] as TextBlock;
505
506 Assert.Equal("Hello World", textBlock1?.Text);
507 Assert.Equal("Hello World", textBlock2?.Text);
508 Assert.Equal("Hello World", textBlock3?.Text);
509 }
510
511 [Fact]
512 public void Should_Not_Serialize_Null_MsTeams_Property_On_SubmitAction()
513 {
514 // arrange
515 var card = new AdaptiveCard
516 {
517 Body = new List<CardElement>
518 {
519 new TextBlock("Test card with Submit action")
520 },
521 Actions = new List<Microsoft.Teams.Cards.Action>
522 {
523 new SubmitAction
524 {
525 Title = "Submit",
526 Data = new Union<string, SubmitActionData>("test_data")
527 }
528 }
529 };
530
531 // act
532 var json = JsonSerializer.Serialize(card);
533
534 // assert
535 Assert.DoesNotContain("\"msTeams\":null", json);
536 Assert.DoesNotContain("\"msTeams\": null", json);
537
538 // Verify the action is still properly serialized
539 using var doc = JsonDocument.Parse(json);
540 var root = doc.RootElement;
541
542 Assert.True(root.TryGetProperty("actions", out var actionsElement));
543 var action = actionsElement[0];
544 Assert.Equal("Action.Submit", action.GetProperty("type").GetString());
545 Assert.Equal("Submit", action.GetProperty("title").GetString());
546
547 // Verify msTeams property is completely absent, not just null
548 Assert.False(action.TryGetProperty("msTeams", out _));
549 }
550
551 [Fact]
552 public void Should_Serialize_Actions()
553 {
554 var actionJson = """
555 {
556 "type": "Action.OpenUrl",
557 "url": "https://adaptivecards.microsoft.com",
558 "title": "Learn More"
559 }
560 """;
561 var action = OpenUrlAction.Deserialize(actionJson);
562 Assert.NotNull(action);
563 Assert.IsType<OpenUrlAction>(action);
564 Assert.Equal("Learn More", action.Title);
565 Assert.Equal("https://adaptivecards.microsoft.com", action.Url);
566 }
567}