Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
121 changes: 121 additions & 0 deletions __tests__/html2/part-grouping/joinGroup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Part grouping: an activity updated into a group</title>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script type="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for"
}
}
</script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="module">
import { waitFor } from '@testduet/wait-for';

run(async function () {
const { directLine, store } = testHelpers.createDirectLineEmulator();

WebChat.renderWebChat(
{ directLine, store, styleOptions: { botAvatarInitials: 'B', hideSendBox: true } },
document.getElementById('webchat')
);

await pageConditions.uiConnected();

const avatarInitials = () =>
Array.from(document.querySelectorAll('.webchat__initialsAvatar__initials'), ({ textContent }) => textContent);
const getGroup = () => document.querySelector('.part-grouping-activity');
const textsInGroup = () =>
Array.from(
getGroup().querySelectorAll('.webchat__basic-transcript__activity'),
({ textContent }) => textContent
);

const createMessageEntity = (position, isPartOf) => ({
'@context': 'https://schema.org',
'@id': '',
'@type': 'Message',
abstract: `Step ${position}`, // Shown as the title.
// Parts of a group show their status as a checkmark circle, and are sorted by their position.
...(isPartOf && { creativeWorkStatus: 'Published', isPartOf, position }),
type: 'https://schema.org/Message'
});
const howTo = { '@id': '_:how-to-1', '@type': 'HowTo' };

// GIVEN: "Step 1" and "Step 3" in a HowTo group, and "Step 2" on its own.
await directLine.emulateIncomingActivity({
entities: [createMessageEntity(1, howTo)],
from: { role: 'bot' },
id: 'a-1',
text: 'Text of step 1',
timestamp: 1_000,
type: 'message'
});
await directLine.emulateIncomingActivity({
entities: [createMessageEntity(2)],
from: { role: 'bot' },
id: 'a-2',
text: 'Text of step 2',
timestamp: 2_000,
type: 'message'
});
await directLine.emulateIncomingActivity({
entities: [createMessageEntity(3, howTo)],
from: { role: 'bot' },
id: 'a-3',
text: 'Text of step 3',
timestamp: 3_000,
type: 'message'
});

await pageConditions.numActivitiesShown(3);
await pageConditions.scrollStabilized();

// THEN: "Step 2" on its own with an avatar and no checkmark circle, above the group of "Step 1" and "Step 3".
expect(textsInGroup()).toEqual([expect.stringContaining('Step 1'), expect.stringContaining('Step 3')]);
expect(pageElements.activities()[0].closest('.part-grouping-activity')).toBeNull();
expect(pageElements.activities()[0].querySelector('.stacked-layout__message-status')).toBeNull();

await host.snapshot('local');

// WHEN: "Step 2" is re-sent with the same ID, now with `isPartOf` (and the part status).
await directLine.emulateIncomingActivity({
entities: [createMessageEntity(2, howTo)],
from: { role: 'bot' },
id: 'a-2',
text: 'Text of step 2, updated into the group',
timestamp: 2_000,
type: 'message'
});

// THEN: "Step 2" is rendered once, inside the group, between "Step 1" and "Step 3".
await waitFor(() =>
expect(pageElements.activityContents().map(({ textContent }) => textContent)).toEqual([
expect.stringContaining('Text of step 1'),
expect.stringContaining('Text of step 2, updated into the group'),
expect.stringContaining('Text of step 3')
])
);

expect(textsInGroup()).toEqual([
expect.stringContaining('Step 1'),
expect.stringContaining('Step 2'),
expect.stringContaining('Step 3')
]);
expect(avatarInitials()).toEqual(['B']); // The avatar of the group.

await pageConditions.scrollStabilized();

// THEN: 1 group with 1 avatar, each step has a checkmark circle.
await host.snapshot('local');
});
</script>
</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.
127 changes: 127 additions & 0 deletions __tests__/html2/part-grouping/leaveGroup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Part grouping: an activity updated out of its group</title>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script type="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for"
}
}
</script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="module">
import { waitFor } from '@testduet/wait-for';

run(async function () {
const { directLine, store } = testHelpers.createDirectLineEmulator();

WebChat.renderWebChat(
{ directLine, store, styleOptions: { botAvatarInitials: 'B', hideSendBox: true } },
document.getElementById('webchat')
);

await pageConditions.uiConnected();

const avatarInitials = () =>
Array.from(document.querySelectorAll('.webchat__initialsAvatar__initials'), ({ textContent }) => textContent);
const getGroup = () => document.querySelector('.part-grouping-activity');
const textsInGroup = () =>
Array.from(
getGroup().querySelectorAll('.webchat__basic-transcript__activity'),
({ textContent }) => textContent
);

const createMessageEntity = (position, isPartOf) => ({
'@context': 'https://schema.org',
'@id': '',
'@type': 'Message',
abstract: `Step ${position}`, // Shown as the title.
// Parts of a group show their status as a checkmark circle, and are sorted by their position.
...(isPartOf && { creativeWorkStatus: 'Published', isPartOf, position }),
type: 'https://schema.org/Message'
});
const howTo = { '@id': '_:how-to-1', '@type': 'HowTo' };

// GIVEN: 3 bot messages in the same HowTo group.
await directLine.emulateIncomingActivity({
entities: [createMessageEntity(1, howTo)],
from: { role: 'bot' },
id: 'a-1',
text: 'Text of step 1',
timestamp: 1_000,
type: 'message'
});
await directLine.emulateIncomingActivity({
entities: [createMessageEntity(2, howTo)],
from: { role: 'bot' },
id: 'a-2',
text: 'Text of step 2',
timestamp: 2_000,
type: 'message'
});
await directLine.emulateIncomingActivity({
entities: [createMessageEntity(3, howTo)],
from: { role: 'bot' },
id: 'a-3',
text: 'Text of step 3',
timestamp: 3_000,
type: 'message'
});

await pageConditions.numActivitiesShown(3);
await pageConditions.scrollStabilized();

// THEN: 1 group with 1 avatar, each step has a checkmark circle.
expect(textsInGroup()).toEqual([
expect.stringContaining('Step 1'),
expect.stringContaining('Step 2'),
expect.stringContaining('Step 3')
]);
expect(avatarInitials()).toEqual(['B']);

await host.snapshot('local');

// WHEN: "Step 2" is re-sent with the same ID, without `isPartOf` (and without the part status).
await directLine.emulateIncomingActivity({
entities: [createMessageEntity(2)],
from: { role: 'bot' },
id: 'a-2',
text: 'Text of step 2, updated out of the group',
timestamp: 2_000,
type: 'message'
});

// THEN: "Step 2" is rendered once, outside the group, and the group keeps "Step 1" and "Step 3".
await waitFor(() =>
expect(pageElements.activityContents().map(({ textContent }) => textContent)).toEqual([
// Before the group: the group sorts by its latest part ("Step 3").
expect.stringContaining('Text of step 2, updated out of the group'),
expect.stringContaining('Text of step 1'),
expect.stringContaining('Text of step 3')
])
);

expect(textsInGroup()).toEqual([expect.stringContaining('Step 1'), expect.stringContaining('Step 3')]);

// THEN: "Step 2" has its own avatar and no status, which only parts of a group show.
// The group no longer shows its avatar: consecutive messages from the same sender share the avatar of the first one.
expect(avatarInitials()).toEqual(['B']);
expect(pageElements.activities()[0].querySelector('.webchat__initialsAvatar__initials')).toBeTruthy();
expect(pageElements.activities()[0].closest('.part-grouping-activity')).toBeNull();
expect(pageElements.activities()[0].querySelector('.stacked-layout__message-status')).toBeNull();

await pageConditions.scrollStabilized();

// THEN: "Step 2" on its own with an avatar and no checkmark circle, above the group of "Step 1" and "Step 3".
await host.snapshot('local');
});
</script>
</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