Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
88c4d49
Fix streaming behavior by capturing typingActivityId in nextActivityI…
benbrown Oct 3, 2025
8867617
Merge branch 'main' into users/benbro/fix-streaming-behavior
benbrown Oct 3, 2025
00da7ea
Add new test to validate streaming entities in the entities field rat…
benbrown Oct 9, 2025
7c4d142
additional tests for entities
benbrown Oct 9, 2025
8e15b00
Update streaming test data to better match reality
benbrown Oct 10, 2025
5f955d0
remove old snapshot
benbrown Oct 10, 2025
bbe6a55
minor typo
benbrown Oct 10, 2025
9a34ff2
Merge branch 'main' into users/benbro/fix-streaming-behavior
benbrown Oct 13, 2025
02bb569
add one additional message to the test data
benbrown Oct 14, 2025
a669a32
updated snapshots
benbrown Oct 14, 2025
eeec789
:Merge branch 'users/benbro/fix-streaming-behavior' of https://github…
benbrown Oct 14, 2025
442dc40
remove streamId from first entity
benbrown Oct 14, 2025
46c95d5
Merge branch 'main' into users/benbro/fix-streaming-behavior
benbrown Oct 14, 2025
9d0c02b
refactor livestream tests to test both channelData and entity style d…
benbrown Oct 30, 2025
bd985d0
Merge branch 'users/benbro/fix-streaming-behavior' of https://github.…
benbrown Oct 30, 2025
2aecb64
merging main
benbrown Oct 30, 2025
019ae5c
rebuild snaps of entity tests
benbrown Oct 30, 2025
6bd0d75
Clean up
compulim Oct 31, 2025
c53ea48
Merge pull request #1 from microsoft/benbr-fix-streaming-behavior
benbrown Oct 31, 2025
43744a4
Merge branch 'main' into users/benbro/fix-streaming-behavior
OEvgeny Oct 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions __tests__/html2/livestream/activityOrder.entity.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>activityOrder (entity): redirects to ?streamingFormat=entity</title>
<script>
location = './activityOrder?streamingFormat=entity';
</script>
</head>
<body></body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
86 changes: 58 additions & 28 deletions __tests__/html2/livestream/activityOrder.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@
<body>
<main id="webchat"></main>
<script type="module">
const streamingFormat = new URL(window.location.href).searchParams.get('streamingFormat') ?? 'channelData'; // can be entity or channelData

function addLivestreamingMetadata(activity, livestreamingMetadata) {
return streamingFormat === 'entity'
? {
...activity,
entities: [...(activity.entities ?? []), { ...livestreamingMetadata, type: 'streaminfo' }]
}
: {
...activity,
channelData: { ...activity.channelData, ...livestreamingMetadata }
};
}

run(async function () {
const {
React: { createElement },
Expand Down Expand Up @@ -61,13 +75,17 @@
// WHEN: Bot is typing a message.
const firstTypingActivityId = 't-00001';

await directLine.emulateIncomingActivity({
channelData: { streamSequence: 1, streamType: 'streaming' },
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: firstTypingActivityId,
text: 'A quick',
type: 'typing'
});
await directLine.emulateIncomingActivity(
addLivestreamingMetadata(
{
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: firstTypingActivityId,
text: 'A quick',
type: 'typing'
},
{ streamSequence: 1, streamType: 'streaming' }
)
);

let secondActivityKey = currentActivityKeysWithId[1][0];

Expand Down Expand Up @@ -123,13 +141,17 @@
// ---

// WHEN: Bot continue typing the message.
await directLine.emulateIncomingActivity({
channelData: { streamId: firstTypingActivityId, streamSequence: 2, streamType: 'streaming' },
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: 't-00002',
text: 'A quick brown fox',
type: 'typing'
});
await directLine.emulateIncomingActivity(
addLivestreamingMetadata(
{
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: 't-00002',
text: 'A quick brown fox',
type: 'typing'
},
{ streamId: firstTypingActivityId, streamSequence: 2, streamType: 'streaming' }
)
);

// THEN: Should display 3 messages, the order should kept the same.
await pageConditions.numActivitiesShown(3);
Expand All @@ -155,13 +177,17 @@
// ---

// WHEN: Bot continue typing the message.
await directLine.emulateIncomingActivity({
channelData: { streamId: firstTypingActivityId, streamSequence: 3, streamType: 'streaming' },
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: 't-00003',
text: 'A quick brown fox jumped over',
type: 'typing'
});
await directLine.emulateIncomingActivity(
addLivestreamingMetadata(
{
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: 't-00003',
text: 'A quick brown fox jumped over',
type: 'typing'
},
{ streamId: firstTypingActivityId, streamSequence: 3, streamType: 'streaming' }
)
);

// THEN: Should display 3 messages, the order should kept the same.
await pageConditions.numActivitiesShown(3);
Expand All @@ -187,13 +213,17 @@
// ---

// WHEN: Bot finished typing the message.
await directLine.emulateIncomingActivity({
channelData: { streamId: firstTypingActivityId, streamType: 'final' },
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: 'a-00002',
text: 'A quick brown fox jumped over the lazy dogs.',
type: 'message'
});
await directLine.emulateIncomingActivity(
addLivestreamingMetadata(
{
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: 'a-00002',
text: 'A quick brown fox jumped over the lazy dogs.',
type: 'message'
},
{ streamId: firstTypingActivityId, streamType: 'final' }
)
);

// THEN: Should display 3 messages.
await pageConditions.numActivitiesShown(3);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!doctype html>
<html>
<head>
<script>
location = './attachmentWithoutText?layout=carousel&streamingFormat=entity';
</script>
</head>
<body></body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions __tests__/html2/livestream/attachmentWithoutText.entity.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>attachmentWithoutText (entity): redirects to ?streamingFormat=entity</title>
<script>
location = './attachmentWithoutText?streamingFormat=entity';
</script>
</head>
<body></body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 71 additions & 47 deletions __tests__/html2/livestream/attachmentWithoutText.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@
const attachmentLayout =
new URLSearchParams(location.search).get('layout') === 'carousel' ? 'carousel' : undefined;

const streamingFormat = new URL(window.location.href).searchParams.get('streamingFormat') ?? 'channelData'; // can be entity or channelData

function addLivestreamingMetadata(activity, livestreamingMetadata) {
return streamingFormat === 'entity'
? {
...activity,
entities: [...(activity.entities ?? []), { ...livestreamingMetadata, type: 'streaminfo' }]
}
: {
...activity,
channelData: { ...activity.channelData, ...livestreamingMetadata }
};
}

run(async function () {
const {
React: { createElement },
Expand Down Expand Up @@ -54,20 +68,24 @@
// WHEN: Bot start a livestream with an attachment.
const firstTypingActivityId = crypto.randomUUID();

await directLine.emulateIncomingActivity({
attachmentLayout,
attachments: [
await directLine.emulateIncomingActivity(
addLivestreamingMetadata(
{
content: 'Card 1',
contentType: 'text/plain'
}
],
channelData: { streamSequence: 1, streamType: 'streaming' },
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: firstTypingActivityId,
timestamp: 1,
type: 'typing'
});
attachmentLayout,
attachments: [
{
content: 'Card 1',
contentType: 'text/plain'
}
],
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: firstTypingActivityId,
timestamp: 1,
type: 'typing'
},
{ streamSequence: 1, streamType: 'streaming' }
)
);

// THEN: Should display the activity.
await pageConditions.numActivitiesShown(1);
Expand All @@ -82,25 +100,28 @@
// ---

// WHEN: Receive another attachment through livestreaming.

await directLine.emulateIncomingActivity({
attachmentLayout,
attachments: [
await directLine.emulateIncomingActivity(
addLivestreamingMetadata(
{
content: 'Card 1',
contentType: 'text/plain'
attachmentLayout,
attachments: [
{
content: 'Card 1',
contentType: 'text/plain'
},
{
content: 'Card 2',
contentType: 'text/plain'
}
],
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: crypto.randomUUID(),
timestamp: 2,
type: 'typing'
},
{
content: 'Card 2',
contentType: 'text/plain'
}
],
channelData: { streamId: firstTypingActivityId, streamSequence: 2, streamType: 'streaming' },
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: crypto.randomUUID(),
timestamp: 2,
type: 'typing'
});
{ streamId: firstTypingActivityId, streamSequence: 2, streamType: 'streaming' }
)
);

// THEN: Should display the activity.
await pageConditions.numActivitiesShown(1);
Expand All @@ -116,25 +137,28 @@
// ---

// WHEN: Finalized message is received.

await directLine.emulateIncomingActivity({
attachmentLayout,
attachments: [
await directLine.emulateIncomingActivity(
addLivestreamingMetadata(
{
content: 'Card A',
contentType: 'text/plain'
attachmentLayout,
attachments: [
{
content: 'Card A',
contentType: 'text/plain'
},
{
content: 'Card B',
contentType: 'text/plain'
}
],
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: crypto.randomUUID(),
timestamp: 3,
type: 'message'
},
{
content: 'Card B',
contentType: 'text/plain'
}
],
channelData: { streamId: firstTypingActivityId, streamType: 'final' },
from: { id: 'u-00001', name: 'Bot', role: 'bot' },
id: crypto.randomUUID(),
timestamp: 3,
type: 'message'
});
{ streamId: firstTypingActivityId, streamType: 'final' }
)
);

// THEN: Should display the activity.
await pageConditions.numActivitiesShown(1);
Expand Down
10 changes: 10 additions & 0 deletions __tests__/html2/livestream/backtrackToEmpty.entity.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>backtrackToEmpty (entity): redirects to ?streamingFormat=entity</title>
<script>
location = './backtrackToEmpty?streamingFormat=entity';
</script>
</head>
<body></body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading