Skip to content

Commit 2237ddd

Browse files
denscollosantgr11iscaltritti-swgabogceciliaavila
authored
[Skills Everywhere][microsoft#259] Add Waterfall FileUpload test (microsoft#307)
* Add FileUpload test * Add attachment support in DirectLineTestClient * Minor fixes * Add missing text * Replace ClientType references * Add UploadAsync method in DirectLineTestClient * Reanme to fix case * Reanmed it back * Update Waterfall FileUpload dialogs and tests Co-authored-by: Santiago Grangetto <[email protected]> Co-authored-by: iscaltritti-sw <[email protected]> Co-authored-by: Gabo Gilabert <[email protected]> Co-authored-by: CeciliaAvila <[email protected]> Co-authored-by: Cecilia Avila <[email protected]> Co-authored-by: Gabo Gilabert <[email protected]>
1 parent 1460d8f commit 2237ddd

File tree

14 files changed

+963
-6
lines changed

14 files changed

+963
-6
lines changed

Bots/DotNet/Skills/CodeFirst/WaterfallSkillBot/Dialogs/FileUpload/FileUploadDialog.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,17 @@ private async Task<DialogTurnResult> HandleAttachmentStepAsync(WaterfallStepCont
4242
{
4343
var remoteFileUrl = file.ContentUrl;
4444
var localFileName = Path.Combine(Path.GetTempPath(), file.Name);
45+
string fileContent;
46+
4547
using (var webClient = new WebClient())
4648
{
4749
webClient.DownloadFile(remoteFileUrl, localFileName);
50+
using var reader = new StreamReader(localFileName);
51+
fileContent = await reader.ReadToEndAsync();
4852
}
4953

50-
fileText += $"Attachment \"{file.Name}\" has been received and saved to \"{localFileName}\"\r\n";
54+
fileText += $"Attachment \"{file.Name}\" has been received.\r\n";
55+
fileText += $"File content: {fileContent}\r\n";
5156
}
5257

5358
await stepContext.Context.SendActivityAsync(MessageFactory.Text(fileText), cancellationToken);

Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/fileUpload/fileUploadDialog.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,17 @@ class FileUploadDialog extends ComponentDialog {
4949
*/
5050
async handleAttachmentStep (stepContext) {
5151
let fileText = '';
52+
let fileContent = '';
5253

5354
for (const file of stepContext.context.activity.attachments) {
5455
const localFileName = path.resolve(os.tmpdir(), file.name);
5556
const tempFile = fs.createWriteStream(localFileName);
56-
fetch(file.contentUrl).then(response => streamPipeline(response.body, tempFile));
57+
const response = await fetch(file.contentUrl);
58+
await streamPipeline(response.body, tempFile);
5759

58-
fileText += `Attachment "${file.name}" has been received and saved to "${localFileName}"\r\n`;
60+
fileContent = fs.readFileSync(localFileName, 'utf8');
61+
fileText += `Attachment "${file.name}" has been received.\r\n`;
62+
fileText += `File content: ${fileContent}\r\n`;
5963
}
6064

6165
await stepContext.context.sendActivity(MessageFactory.text(fileText));

Bots/Python/Skills/CodeFirst/WaterfallSkillBot/dialogs/file_upload/file_upload_dialog.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@ async def prompt_upload_step(self, step_context: WaterfallStepContext):
4343
AttachmentPrompt.__name__,
4444
PromptOptions(
4545
prompt=MessageFactory.text(
46-
"Please upload a file to continue.", InputHints.accepting_input
46+
"Please upload a file to continue.", input_hint=InputHints.accepting_input
4747
),
4848
retry_prompt=MessageFactory.text("You must upload a file."),
4949
),
5050
)
5151

5252
async def handle_attachment_step(self, step_context: WaterfallStepContext):
5353
file_text = ""
54+
file_content = ""
5455

5556
for file in step_context.context.activity.attachments:
5657
remote_file_url = file.content_url
@@ -60,7 +61,9 @@ async def handle_attachment_step(self, step_context: WaterfallStepContext):
6061
) as out_file:
6162
shutil.copyfileobj(response, out_file)
6263

63-
file_text += f'Attachment "${ file.name }" has been received and saved to "${ local_file_name }"\r\n'
64+
file_content = open(local_file_name, "r").read()
65+
file_text += f'Attachment "{ file.name }" has been received.\r\n'
66+
file_text += f'File content: { file_content }\r\n'
6467

6568
await step_context.context.send_activity(MessageFactory.text(file_text))
6669

Libraries/TranscriptTestRunner/TestClientBase.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using System.IO;
45
using System.Threading;
56
using System.Threading.Tasks;
67
using Microsoft.Bot.Schema;
@@ -38,5 +39,14 @@ public abstract class TestClientBase
3839
/// <param name="signInUrl">The sign in Url.</param>
3940
/// <returns>True, if SignIn is successful; otherwise false.</returns>
4041
public abstract Task<bool> SignInAsync(string signInUrl);
42+
43+
/// <summary>
44+
/// Uploads a file.
45+
/// </summary>
46+
/// <param name="file">The file to be uploaded.</param>
47+
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used by other objects
48+
/// or threads to receive notice of cancellation.</param>
49+
/// <returns>A task that represents the work queued to execute.</returns>
50+
public abstract Task UploadAsync(Stream file, CancellationToken cancellationToken);
4151
}
4252
}

Libraries/TranscriptTestRunner/TestClients/DirectLineTestClient.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Globalization;
7+
using System.IO;
78
using System.Linq;
89
using System.Net.Http;
910
using System.Net.Http.Headers;
@@ -20,6 +21,7 @@
2021
using TranscriptTestRunner.Authentication;
2122
using Activity = Microsoft.Bot.Connector.DirectLine.Activity;
2223
using ActivityTypes = Microsoft.Bot.Schema.ActivityTypes;
24+
using Attachment = Microsoft.Bot.Connector.DirectLine.Attachment;
2325
using BotActivity = Microsoft.Bot.Schema.Activity;
2426
using BotChannelAccount = Microsoft.Bot.Schema.ChannelAccount;
2527
using ChannelAccount = Microsoft.Bot.Connector.DirectLine.ChannelAccount;
@@ -95,11 +97,28 @@ public override async Task SendActivityAsync(BotActivity activity, CancellationT
9597
}
9698
}
9799

100+
var attachments = new List<Attachment>();
101+
102+
if (activity.Attachments != null && activity.Attachments.Any())
103+
{
104+
foreach (var item in activity.Attachments)
105+
{
106+
attachments.Add(new Attachment
107+
{
108+
ContentType = item.ContentType,
109+
ContentUrl = item.ContentUrl,
110+
Content = item.Content,
111+
Name = item.Name
112+
});
113+
}
114+
}
115+
98116
var activityPost = new Activity
99117
{
100118
From = new ChannelAccount(_user),
101119
Text = activity.Text,
102-
Type = activity.Type
120+
Type = activity.Type,
121+
Attachments = attachments,
103122
};
104123

105124
_logger.LogDebug($"{DateTime.Now} Sending activity to conversation {_conversation.ConversationId}");
@@ -151,6 +170,12 @@ public void Dispose()
151170
GC.SuppressFinalize(this);
152171
}
153172

173+
/// <inheritdoc/>
174+
public override async Task UploadAsync(Stream file, CancellationToken cancellationToken)
175+
{
176+
await _dlClient.Conversations.UploadAsync(_conversation.ConversationId, file, _user, cancellationToken: cancellationToken).ConfigureAwait(false);
177+
}
178+
154179
/// <summary>
155180
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
156181
/// </summary>

Libraries/TranscriptTestRunner/TestRunner.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ public async Task SendActivityAsync(Activity sendActivity, CancellationToken can
9292
await _testClient.SendActivityAsync(sendActivity, cancellationToken).ConfigureAwait(false);
9393
}
9494

95+
/// <summary>
96+
/// Uploads a file through the test client.
97+
/// </summary>
98+
/// <param name="file">The file to upload.</param>
99+
/// <param name="cancellationToken">Optional. A <see cref="CancellationToken"/> that can be used by other objects
100+
/// or threads to receive notice of cancellation.</param>
101+
/// <returns>A task that represents the work queued to execute.</returns>
102+
public async Task UploadAsync(Stream file, CancellationToken cancellationToken = default)
103+
{
104+
_logger.LogInformation("Elapsed Time: {Elapsed}, Uploading file", Stopwatch.Elapsed);
105+
await _testClient.UploadAsync(file, cancellationToken).ConfigureAwait(false);
106+
}
107+
95108
/// <summary>
96109
/// Gets the next reply <see cref="Activity"/> from the bot through the test client.
97110
/// </summary>
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Threading.Tasks;
8+
using Microsoft.Bot.Connector;
9+
using Microsoft.Bot.Schema;
10+
using Microsoft.Extensions.Logging;
11+
using Newtonsoft.Json;
12+
using SkillFunctionalTests.Common;
13+
using TranscriptTestRunner;
14+
using TranscriptTestRunner.XUnit;
15+
using Xunit;
16+
using Xunit.Abstractions;
17+
18+
namespace SkillFunctionalTests.FileUpload
19+
{
20+
[Trait("TestCategory", "FileUpload")]
21+
public class FileUploadTests : ScriptTestBase
22+
{
23+
private readonly string _testScriptsFolder = Directory.GetCurrentDirectory() + @"/FileUpload/TestScripts";
24+
25+
public FileUploadTests(ITestOutputHelper output)
26+
: base(output)
27+
{
28+
}
29+
30+
public static IEnumerable<object[]> TestCases()
31+
{
32+
var channelIds = new List<string> { Channels.Directline };
33+
34+
var deliverModes = new List<string>
35+
{
36+
DeliveryModes.Normal,
37+
DeliveryModes.ExpectReplies
38+
};
39+
40+
var hostBots = new List<HostBot>
41+
{
42+
HostBot.WaterfallHostBotDotNet,
43+
HostBot.WaterfallHostBotJS,
44+
HostBot.WaterfallHostBotPython
45+
46+
// TODO: Enable these when the port to composer is ready
47+
//HostBotNames.ComposerHostBotDotNet
48+
};
49+
50+
var targetSkills = new List<string>
51+
{
52+
SkillBotNames.WaterfallSkillBotDotNet,
53+
SkillBotNames.WaterfallSkillBotJS,
54+
SkillBotNames.WaterfallSkillBotPython
55+
56+
// TODO: Enable these when the port to composer is ready
57+
//SkillBotNames.ComposerSkillBotDotNet
58+
};
59+
60+
var scripts = new List<string>
61+
{
62+
"FileUpload1.json"
63+
};
64+
65+
var testCaseBuilder = new TestCaseBuilder();
66+
var testCases = testCaseBuilder.BuildTestCases(channelIds, deliverModes, hostBots, targetSkills, scripts);
67+
foreach (var testCase in testCases)
68+
{
69+
yield return testCase;
70+
}
71+
}
72+
73+
[Theory]
74+
[MemberData(nameof(TestCases))]
75+
public async Task RunTestCases(TestCaseDataObject testData)
76+
{
77+
const string fileName = "TestFile.txt";
78+
var testGuid = Guid.NewGuid().ToString();
79+
var testCase = testData.GetObject<TestCase>();
80+
Logger.LogInformation(JsonConvert.SerializeObject(testCase, Formatting.Indented));
81+
82+
var options = TestClientOptions[testCase.HostBot];
83+
var runner = new XUnitTestRunner(new TestClientFactory(testCase.ChannelId, options, Logger).GetTestClient(), TestRequestTimeout, Logger);
84+
85+
// Execute the first part of the conversation.
86+
var testParams = new Dictionary<string, string>
87+
{
88+
{ "DeliveryMode", testCase.DeliveryMode },
89+
{ "TargetSkill", testCase.TargetSkill },
90+
{ "FileName", fileName },
91+
{ "TestGuid", testGuid }
92+
};
93+
94+
await runner.RunTestAsync(Path.Combine(_testScriptsFolder, testCase.Script), testParams);
95+
96+
// Create or Update testFile.
97+
await using var stream = File.OpenWrite(Directory.GetCurrentDirectory() + $"/FileUpload/Media/{fileName}");
98+
await using var writer = new StreamWriter(stream);
99+
await writer.WriteLineAsync($"GUID:{testGuid}");
100+
writer.Close();
101+
102+
// Upload file.
103+
await using var file = File.OpenRead(Directory.GetCurrentDirectory() + $"/FileUpload/Media/{fileName}");
104+
await runner.UploadAsync(file);
105+
106+
// Execute the rest of the conversation.
107+
await runner.RunTestAsync(Path.Combine(_testScriptsFolder, "FileUpload2.json"), testParams);
108+
}
109+
}
110+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This is a test file.

0 commit comments

Comments
 (0)